package main

import (
	"bufio"
	"encoding/hex"
	"fmt"
	"log"
	"net"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/songgao/packets/ethernet"
	"github.com/songgao/water"
)

//declare IP and subnet mask here
var (
	self = net.ParseIP(os.Args[2])
	mask = net.IPv4Mask(255, 255, 255, 0)
)

//declare new interface here
var ifce, _ = water.New(water.Config{
	DeviceType: water.TAP,
})

var RCV_B = 0 //recived in byte
var TXT_B = 0 //sent in byte
var RCV_T = 0 //number of packet recived
var TXT_T = 0 //number of packet sended

func main() {
	//for stat only
	go func() {
		for {
			//showing statistics here, every 5 seconds
			log.Println("Total recived: ", ByteCountSI(RCV_B), ", Total transmited: ", ByteCountSI(TXT_B))
			log.Println("Total recived: ", RCV_T, ", Total transmited: ", TXT_T)
			time.Sleep(5 * time.Second)
		}
	}()

	//create socket server
	go func() {
		fmt.Println("Launching server...") //show debug message
		// listen on all interfaces
		ln, _ := net.Listen("tcp", ":8081") //create new interface
		// accept connection on port
		conn, _ := ln.Accept() //accept connections
		// run loop forever (or until ctrl-c)
		for {
			// will listen for message to process ending in newline (\n)
			message, _ := bufio.NewReader(conn).ReadString('\n')
			// output message received
			//fmt.Print("RECV<<",  string(message))
			RCV_B += len(string(message))                                                //for statistics
			RCV_T++                                                                      //for statistics
			data, err := hex.DecodeString(strings.ReplaceAll("\n", "", string(message))) //since data had \n, so replace it and convert to hex array
			if err != nil {
				//log.Println(err)
			}
			//log.Println("RECV<<", data)
			ifce.Write(data) //write to interface
		}
	}()

	//declare server
	// connect to server
	conns, _ := net.Dial("tcp", os.Args[1]+":8081")

	var frame ethernet.Frame                                //declare frame
	setupIfce(net.IPNet{IP: self, Mask: mask}, ifce.Name()) //setup interface info

	for {
		frame.Resize(1500)                 //MTU = 1500
		n, err := ifce.Read([]byte(frame)) //read from ifce
		if err != nil {
			log.Fatal(err)
		}
		frame = frame[:n] //fconvert frame to usabele array
		//log.Println("TRSV>>", hex.EncodeToString(frame))
		TXT_B += len(hex.EncodeToString(frame)) //for statistics
		TXT_T++                                 //for statistics
		/*
			log.Printf("Dst: %s\n", frame.Destination())
			log.Printf("Src: %s\n", frame.Source())
			log.Printf("Ethertype: % x\n", frame.Ethertype())
			log.Printf("Payload: % x\n", frame.Payload())
			log.Printf("HEX: % x\n", frame)
		*/
		fmt.Fprintf(conns, hex.EncodeToString(frame)+"\n") //send to server
		/*
			a := hex.EncodeToString(frame)

				resp, err := http.Get("http://" + os.Args[1] + "/rcv?packet=" + a)
				if err != nil {
					fmt.Println(err)
				} else {
					defer resp.Body.Close()
				}
		*/
	}
}

func setupIfce(ipNet net.IPNet, dev string) {
	sargs := fmt.Sprintf("interface ip set address name=REPLACE_ME source=static addr=REPLACE_ME mask=REPLACE_ME gateway=none")
	args := strings.Split(sargs, " ")
	args[4] = fmt.Sprintf("name=%s", dev)
	args[6] = fmt.Sprintf("addr=%s", ipNet.IP)
	args[7] = fmt.Sprintf("mask=%d.%d.%d.%d", ipNet.Mask[0], ipNet.Mask[1], ipNet.Mask[2], ipNet.Mask[3])
	cmd := exec.Command("netsh", args...)
	if err := cmd.Run(); err != nil {
		log.Println(err)
	}
}

//https://yourbasic.org/golang/formatting-byte-size-to-human-readable-format/
func ByteCountSI(b int) string {
	const unit = 1000
	if b < unit {
		return fmt.Sprintf("%d B", b)
	}
	div, exp := int(unit), 0
	for n := b / unit; n >= unit; n /= unit {
		div *= unit
		exp++
	}
	return fmt.Sprintf("%.1f %cB",
		float64(b)/float64(div), "kMGTPE"[exp])
}
