package discovery

import (
	"git.loafle.net/overflow/overflow_probe/discovery/discovery/types"

	"git.loafle.net/overflow/overflow_probe/discovery/core/scan/port/tcp"
	"git.loafle.net/overflow/overflow_probe/discovery/core/scan/port/udp"
	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"

	//"git.loafle.net/overflow/overflow_probe/central/client/events"
	//"git.loafle.net/overflow/overflow_probe/discovery/communicate"
	"strconv"

	"git.loafle.net/overflow/overflow_probe/discovery/util/converter"
	"sync"
)

func discoverPort(host *types.DiscoveryHost, tcpCB discoverTCPPortCallback, udpCB discoverUDPPortCallback) {

	var wg sync.WaitGroup
	wg.Add(1)
	go discoverTcp(&wg, host, tcpCB)
	wg.Add(1)
	go discoverUdp(&wg, host, udpCB)

	wg.Wait()

}

func discoverTcp(wg *sync.WaitGroup, host *types.DiscoveryHost, serviceCB discoverTCPPortCallback) {

	recvCallback := func(tcp *layers.TCP) {

		if tcp == nil {
		} else if tcp.DstPort != 60000 {
		} else if tcp.RST {
			types.NewPortScanHistory(host, uint16(tcp.SrcPort), types.TYPE_TCP, types.RECV_RST, "")
		} else if tcp.SYN && tcp.ACK {
			portString := strconv.Itoa(int(tcp.SrcPort))
			port := types.NewPort(portString, host, types.TYPE_TCP)

			types.NewPortScanHistory(host, uint16(tcp.SrcPort), types.TYPE_TCP, types.RECV, "")
			key := portString + ":" + types.TYPE_TCP
			host.SetPort(key, port)

			serviceCB(port)
			//communicate.Send(events.NewEvent(events.CENTRAL_EVENT, events.NewPortFoundEvent(host.Zone.CidrInt64(), host.Ip, uint16(tcp.SrcPort), types.TYPE_TCP)))
		}

	}

	tcp.Scan(host, recvCallback)
	wg.Done()
}

func discoverUdp(wg *sync.WaitGroup, host *types.DiscoveryHost, udpCB discoverUDPPortCallback) {

	rcb := func(packet gopacket.Packet) {

		ipLayer := packet.Layer(layers.LayerTypeIPv4)

		if ipLayer.(*layers.IPv4).SrcIP.String() == converter.IntToStr(host.Zone.Ip) {
			return
		}

		if net := packet.NetworkLayer(); net == nil {
		} else if udpLayer := packet.Layer(layers.LayerTypeUDP); udpLayer == nil {
		} else if udp, ok := udpLayer.(*layers.UDP); ok {

			ipString := ipLayer.(*layers.IPv4).SrcIP.String()
			portString := strconv.Itoa(int(udp.SrcPort))

			if _, exists := host.Zone.GetHost(ipString); !exists {
				return
			}

			ph := types.NewPortScanHistory(host, uint16(udp.SrcPort), types.TYPE_UDP, types.RECV, "")
			host.AddHistory(ph)

			//communicate.Send(events.NewEvent(events.CENTRAL_EVENT, events.NewPortFoundEvent(host.Zone.CidrInt64(), host.Ip, uint16(udp.SrcPort), types.TYPE_UDP)))

			udpCB(host.Zone, ipString, portString, udpLayer)

		}
	}

	udp.Scan(host, rcb)
	wg.Done()
}
