package handler

import (
	"fmt"
	"net"

	"github.com/bjdgyc/anylink/base"
	"github.com/bjdgyc/anylink/pkg/arpdis"
	"github.com/bjdgyc/anylink/sessdata"
	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/songgao/packets/ethernet"
	"github.com/songgao/water"
	"github.com/songgao/water/waterutil"
)

const bridgeName = "anylink0"

var (
	bridgeIp net.IP
	bridgeHw net.HardwareAddr
)

func checkTap() {
	brFace, err := net.InterfaceByName(bridgeName)
	if err != nil {
		base.Fatal("testTap err: ", err)
	}
	bridgeHw = brFace.HardwareAddr

	addrs, err := brFace.Addrs()
	if err != nil {
		base.Fatal("testTap err: ", err)
	}
	for _, addr := range addrs {
		ip, _, err := net.ParseCIDR(addr.String())
		if err != nil || ip.To4() == nil {
			continue
		}
		bridgeIp = ip
	}
	if bridgeIp == nil && bridgeHw == nil {
		base.Fatal("bridgeIp is err")
	}

	if !sessdata.IpPool.Ipv4IPNet.Contains(bridgeIp) {
		base.Fatal("bridgeIp or Ip network err")
	}
}

// 创建tap网卡
func LinkTap(cSess *sessdata.ConnSession) error {
	cfg := water.Config{
		DeviceType: water.TAP,
	}

	ifce, err := water.New(cfg)
	if err != nil {
		base.Error(err)
		return err
	}

	cSess.TunName = ifce.Name()

	// arp on
	cmdstr1 := fmt.Sprintf("ip link set dev %s up mtu %d multicast on", ifce.Name(), cSess.Mtu)
	cmdstr2 := fmt.Sprintf("ip link set dev %s master %s", ifce.Name(), bridgeName)
	// cmdstr3 := fmt.Sprintf("sysctl -w net.ipv6.conf.%s.disable_ipv6=1", ifce.Name())
	cmdStrs := []string{cmdstr1, cmdstr2}
	err = execCmd(cmdStrs)
	if err != nil {
		base.Error(err)
		_ = ifce.Close()
		return err
	}

	go tapRead(ifce, cSess)
	go tapWrite(ifce, cSess)
	return nil
}

func tapWrite(ifce *water.Interface, cSess *sessdata.ConnSession) {
	defer func() {
		base.Debug("LinkTap return", cSess.IpAddr)
		cSess.Close()
		_ = ifce.Close()
	}()

	var (
		err     error
		payload *sessdata.Payload
		frame   ethernet.Frame
	)

	for {
		select {
		case payload = <-cSess.PayloadIn:
		case <-cSess.CloseChan:
			return
		}

		// var frame ethernet.Frame
		frame = getByteFull()
		switch payload.LType {
		default:
			// log.Println(payload)
		case sessdata.LTypeEthernet:
			copy(frame, payload.Data)
			frame = frame[:len(payload.Data)]
		case sessdata.LTypeIPData: // 需要转换成 Ethernet 数据
			data := payload.Data

			ip_src := waterutil.IPv4Source(data)
			if waterutil.IsIPv6(data) || !ip_src.Equal(cSess.IpAddr) {
				// 过滤掉IPv6的数据
				// 非分配给客户端ip,直接丢弃
				continue
			}

			// packet := gopacket.NewPacket(data, layers.LayerTypeIPv4, gopacket.Default)
			// fmt.Println("get:", packet)

			ip_dst := waterutil.IPv4Destination(data)
			// fmt.Println("get:", ip_src, ip_dst)

			var dstHw net.HardwareAddr
			if !sessdata.IpPool.Ipv4IPNet.Contains(ip_dst) || ip_dst.Equal(sessdata.IpPool.Ipv4Gateway) {
				// 不是同一网段,使用网关mac地址
				dstAddr := arpdis.Lookup(sessdata.IpPool.Ipv4Gateway, false)
				dstHw = dstAddr.HardwareAddr
			} else {
				dstAddr := arpdis.Lookup(ip_dst, true)
				// fmt.Println("dstAddr", dstAddr)
				if dstAddr != nil {
					dstHw = dstAddr.HardwareAddr
				} else {
					dstHw = bridgeHw
				}

			}
			// fmt.Println("Gateway", ip_dst, dstAddr.HardwareAddr)

			frame.Prepare(dstHw, cSess.MacHw, ethernet.NotTagged, ethernet.IPv4, len(data))
			copy(frame[12+2:], data)
		}

		// packet := gopacket.NewPacket(frame, layers.LayerTypeEthernet, gopacket.Default)
		// fmt.Println("write:", packet)
		_, err = ifce.Write(frame)
		if err != nil {
			base.Error("tap Write err", err)
			return
		}

		putByte(frame)
		putPayload(payload)
	}
}

func tapRead(ifce *water.Interface, cSess *sessdata.ConnSession) {
	defer func() {
		base.Debug("tapRead return", cSess.IpAddr)
		_ = ifce.Close()
	}()

	var (
		err   error
		n     int
		buf   []byte
		frame ethernet.Frame
	)

	for {
		// var frame ethernet.Frame
		// frame.Resize(BufferSize)
		frame = getByteFull()
		n, err = ifce.Read(frame)
		if err != nil {
			base.Error("tap Read err", n, err)
			return
		}
		frame = frame[:n]

		switch frame.Ethertype() {
		default:
			// packet := gopacket.NewPacket(frame, layers.LayerTypeEthernet, gopacket.Default)
			// fmt.Println(packet)
			continue
		case ethernet.IPv6:
			continue
		case ethernet.IPv4:
			// 发送IP数据
			data := frame.Payload()

			ip_dst := waterutil.IPv4Destination(data)
			if !ip_dst.Equal(cSess.IpAddr) {
				// 过滤非本机地址
				// log.Println(ip_dst, sess.Ip)
				continue
			}

			// packet := gopacket.NewPacket(data, layers.LayerTypeIPv4, gopacket.Default)
			// fmt.Println("put:", packet)

			if payloadOut(cSess, sessdata.LTypeIPData, 0x00, data) {
				return
			}

		case ethernet.ARP:
			// 暂时仅实现了ARP协议
			packet := gopacket.NewPacket(frame, layers.LayerTypeEthernet, gopacket.Default)
			layer := packet.Layer(layers.LayerTypeARP)
			arpReq := layer.(*layers.ARP)

			if !cSess.IpAddr.Equal(arpReq.DstProtAddress) {
				// 过滤非本机地址
				continue
			}

			// fmt.Println("arp", net.IP(arpReq.SourceProtAddress), sess.Ip)
			// fmt.Println(packet)

			// 返回ARP数据
			src := &arpdis.Addr{IP: cSess.IpAddr, HardwareAddr: cSess.MacHw}
			dst := &arpdis.Addr{IP: arpReq.SourceProtAddress, HardwareAddr: frame.Source()}
			buf, err = arpdis.NewARPReply(src, dst)
			if err != nil {
				base.Error(err)
				return
			}

			// 从接受的arp信息添加arp地址
			addr := &arpdis.Addr{
				IP:           make([]byte, len(arpReq.SourceProtAddress)),
				HardwareAddr: make([]byte, len(frame.Source())),
			}
			// addr.IP = arpReq.SourceProtAddress
			// addr.HardwareAddr = frame.Source()
			copy(addr.IP, arpReq.SourceProtAddress)
			copy(addr.HardwareAddr, frame.Source())
			arpdis.Add(addr)

			if payloadIn(cSess, sessdata.LTypeEthernet, 0x00, buf) {
				return
			}

		}

		putByte(frame)
	}
}