package ganserv

import (
	"errors"
	"fmt"
	"net"

	"imuslab.com/zoraxy/mod/database"
)

/*
	Global Area Network
	Server side implementation

	This module do a few things to help manage
	the system GANs

	- Provide DHCP assign to client
	- Provide a list of connected nodes in the same VLAN
	- Provide proxy of packet if the target VLAN is online but not reachable

	Also provide HTTP Handler functions for management
	- Create Network
	- Update Network Properties (Name / Desc)
	- Delete Network

	- Authorize Node
	- Deauthorize Node
	- Set / Get Network Prefered Subnet Mask
	- Handle Node ping
*/

type Node struct {
	Auth          bool   //If the node is authorized in this network
	ClientID      string //The client ID
	MAC           string //The tap MAC this client is using
	Name          string //Name of the client in this network
	Description   string //Description text
	ManagedIP     net.IP //The IP address assigned by this network
	LastSeen      int64  //Last time it is seen from this host
	ClientVersion string //Client application version
	PublicIP      net.IP //Public IP address as seen from this host
}

type Network struct {
	UID         string  //UUID of the network, must be a 16 char random ASCII string
	Name        string  //Name of the network, ASCII only
	Description string  //Description of the network
	CIDR        string  //The subnet masked use by this network
	Nodes       []*Node //The nodes currently attached in this network
}

type NetworkManagerOptions struct {
	Database *database.Database
}

type NetworkManager struct {
	option   *NetworkManagerOptions
	networks []*Network
}

//Create a new GAN manager
func NewNetworkManager(option *NetworkManagerOptions) *NetworkManager {
	option.Database.NewTable("ganserv")

	existingNetworks := []*Network{}
	if option.Database.KeyExists("ganserv", "networks") {
		option.Database.Read("ganserv", "networks", &existingNetworks)
	}
	return &NetworkManager{
		option:   option,
		networks: existingNetworks,
	}
}

//List all the networks within this network manager
func (m *NetworkManager) ListNetworks() []*Network {
	return m.networks
}

func (m *NetworkManager) AddNetwork(n *Network) error {
	if m.NetworkExists(n.UID) {
		return errors.New("network with given id already exists")
	}
	m.networks = append(m.networks, n)

	//Write the latest network list into database
	m.option.Database.Write("ganserv", "networks", m.networks)
	return nil
}

func (m *NetworkManager) RemoveNetwork(id string) error {
	for i, n := range m.networks {
		if n.UID == id {
			m.networks = append(m.networks[:i], m.networks[i+1:]...)

			//Write the latest network list into database
			m.option.Database.Write("ganserv", "networks", m.networks)
			return nil
		}
	}

	return fmt.Errorf("network not found: %s", id)
}

func (m *NetworkManager) GetNetworkByID(id string) (*Network, error) {
	for _, n := range m.networks {
		if n.UID == id {
			return n, nil
		}
	}
	return nil, fmt.Errorf("network not found: %s", id)
}

func (m *NetworkManager) NetworkExists(id string) bool {
	_, err := m.GetNetworkByID(id)
	return err == nil
}