package udp

import (
	"git.loafle.net/overflow/overflow_probe/discovery/core/scan/service/matcher"
	"git.loafle.net/overflow/overflow_probe/discovery/core/scan/service/matcher/packet"
	"git.loafle.net/overflow/overflow_probe/discovery/discovery/types"
	log "github.com/cihub/seelog"
	"net"
	"time"

	"github.com/google/gopacket"

	//"git.loafle.net/overflow/overflow_probe/central/client/events"
	//"git.loafle.net/overflow/overflow_probe/discovery/communicate"
	pw "git.loafle.net/overflow/overflow_probe/discovery/core/pcapwrapper"
)

type udpRecvCallback func(packet gopacket.Packet)

func Scan(host *types.DiscoveryHost, udpCB udpRecvCallback) {

	//Recv(host.Zone, PcapTcpHandler{})

	w, err := pw.GetInstance(host.Zone)
	if err != nil {
		log.Error(err)
	}

	ch := w.OpenUDPChannel(host)

	//read channel
	go func() {
		for data := range ch {
			udpCB(data)
		}
	}()

	Send(host, nil)

	time.Sleep(time.Second * 3)

	//communicate.Send(events.NewEvent(events.CENTRAL_EVENT, events.NewPortEndEvent(host.Zone.CidrInt64(), host.Ip, host.Histories, types.TYPE_UDP)))

	//good game
	w.CloseUDPChannel(host, ch)
	pw.Release(host.Zone)

}

func Check(host *types.DiscoveryHost, ports []*types.DiscoveryPort) {

	//Recv(host.Zone, PcapTcpHandler{})
	Send(host, ports)
}

func Send(host *types.DiscoveryHost, ports []*types.DiscoveryPort) {

	mats := matcher.GetUdpMatchers()

	conn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
	if err != nil {
		log.Error(err)
	}

	//communicate.Send(events.NewEvent(events.CENTRAL_EVENT, events.NewPortStartEvent(host.Zone.CidrInt64(), host.Ip, 10000, types.TYPE_UDP)))

	for indexI := 0; indexI < len(mats); indexI++ {

		mat := mats[indexI]

		if ports == nil {
			for portNumber := host.FirstScanRange; portNumber <= host.LastScanRange; portNumber++ {
				SendUDPPortPacket(host, conn, mat, portNumber)
			}
		} else {
			for _, port := range ports {
				SendUDPPortPacket(host, conn, mat, port.Number)
			}
		}

	}

	conn.Close()

}

func SendUDPPortPacket(host *types.DiscoveryHost, conn *net.UDPConn, mat matcher.UDPMatcher, pn uint16) {

	if mat.IsSend(int(pn)) != true {
		return
	}

	addr := &net.UDPAddr{IP: net.ParseIP(host.Ip_), Port: int(pn)}
	for i := 0; i < mat.PacketCount(); i++ {
		var p *packet.Packet = mat.Packet(i)
		if _, err := conn.WriteToUDP(p.Buffer, addr); err != nil {
			log.Error(err)
		}
	}

	ph := types.NewPortScanHistory(host, pn, types.TYPE_UDP, types.SEND, "")
	host.AddHistory(ph)

	time.Sleep(time.Microsecond * 200)
}
