package discovery

import (
	"errors"
	"github.com/cihub/seelog"
	"github.com/google/gopacket/layers"
	//"git.loafle.net/overflow/overflow_probe/central/client/events"
	//"git.loafle.net/overflow/overflow_probe/discovery/communicate"
	"git.loafle.net/overflow/overflow_probe/discovery/core/scan/host"
	"git.loafle.net/overflow/overflow_probe/discovery/core/scan/service/matcher/dhcp"
	"git.loafle.net/overflow/overflow_probe/discovery/discovery/types"
	"git.loafle.net/overflow/overflow_probe/discovery/discovery/types/timestamp"
	"git.loafle.net/overflow/overflow_probe/discovery/util/converter"
	"net"
	"strconv"
)

func discoveryHost(zone *types.DiscoveryZone, callback discoverHostCallback) {

	_, err := getHostRangeCount(zone)
	if err != nil {
		return
	}
	//communicate.Send(events.NewEvent(events.CENTRAL_EVENT, events.NewHostStartEvent(rangeCount, zone.CidrInt64())))

	arpCallback := func(packet *layers.ARP) {

		keyIp := net.IP(packet.SourceProtAddress).String()
		if _, ok := zone.GetHost(keyIp); ok == true {
			return
		}

		h := types.NewHost(packet.SourceProtAddress, packet.SourceHwAddress)
		h.FirstScanRange = 1
		h.LastScanRange = 10000
		h.Zone = zone
		zone.SetHost(keyIp, h)

		updateHostHistoryProperty(h, packet, h)

		//e := events.NewEvent(events.CENTRAL_EVENT, events.NewHostFoundEvent(zone.CidrInt64(), h.Ip))
		//communicate.Send(e)
		callback(h)
	}

	// arp send history callback // todo 히스토리 콜백이 필요???
	historyCallback := func(targetHost net.IP) {
		hh := types.NewHostHistory(targetHost, types.TIMEOUT, "")
		zone.AddHistory(hh)
	}

	host.Scan(zone, arpCallback, historyCallback)

	//mdns
	//nbns
	dhcp.DiscoverDHCP(zone)

	//communicate.Send(events.NewEvent(events.CENTRAL_EVENT, events.NewHostEndEvent(zone.CidrInt64(), zone.Histories)))
}
func getHostRangeCount(zone *types.DiscoveryZone) (int, error) {
	minIp := converter.IntToIP4(zone.FirstScanRange).To4()
	maxIp := converter.IntToIP4(zone.LastScanRange).To4()
	if maxIp[3] == 0 || minIp[0] == 0 {
		err := errors.New("target host range error")
		seelog.Critical(err)
		return -1, err
	}
	return int(maxIp[3]) - (int(minIp[3]) - 1), nil
}

func updateHostHistoryProperty(host *types.DiscoveryHost, arp *layers.ARP, h *types.DiscoveryHost) {

	idx := findHistoriyByIP(len(host.Zone.Histories), func(i int) bool {
		if host.Zone.Histories[i].Ip == h.Ip {
			return true
		}
		return false
	})

	if idx >= 0 {
		hh := host.Zone.Histories[idx]
		hh.ResultType = types.SUCCESS
		hh.ResultDate = timestamp.Now()
		hh.Description = strconv.FormatInt(converter.MacToInt(arp.SourceHwAddress), 10)
	}
}

func findHistoriyByIP(limit int, predicate func(i int) bool) int {
	for i := 0; i < limit; i++ {
		if predicate(i) {
			return i
		}
	}
	return -1
}
