package main

import (
	"bufio"
	"net"
	"os"
	"regexp"
	"strings"
	"time"

	log "github.com/sirupsen/logrus"
)

var spotRe *regexp.Regexp = regexp.MustCompile(`DX\sde\s([\w\d]+).*:\s+(\d+.\d)\s+([\w\d\/]+)\s+(CW|SSB|FT8|FT4|RTTY|USB|LSB)?\s+(.*)\s\s\s+([\d]+\w{1})`)

type TCPClient struct {
	Login                string
	Password             string
	Address              string
	Port                 string
	LoggedIn             bool
	Timeout              time.Duration
	LogWriter            *bufio.Writer
	Reader               *bufio.Reader
	Writer               *bufio.Writer
	Scanner              *bufio.Scanner
	Conn                 *net.TCPConn
	TCPServer            TCPServer
	MsgChan              chan string
	CmdChan              chan string
	SpotChanToFlex       chan TelnetSpot
	SpotChanToHTTPServer chan TelnetSpot
	Log                  *log.Logger
	Config               *Config
	Countries            Countries
}

func NewTCPClient(TCPServer *TCPServer, Countries Countries) *TCPClient {
	return &TCPClient{
		Address:              Cfg.Cluster.Server,
		Port:                 Cfg.Cluster.Port,
		Login:                Cfg.Cluster.Login,
		Password:             Cfg.Cluster.Password,
		MsgChan:              TCPServer.MsgChan,
		CmdChan:              TCPServer.CmdChan,
		SpotChanToFlex:       make(chan TelnetSpot, 100),
		TCPServer:            *TCPServer,
		SpotChanToHTTPServer: make(chan TelnetSpot, 100),
		Countries:            Countries,
	}
}

func (c *TCPClient) setDefaultParams() {
	if c.Timeout == 0 {
		c.Timeout = 600 * time.Second
	}
	if c.LogWriter == nil {
		c.LogWriter = bufio.NewWriter(os.Stdout)
	}
	c.LoggedIn = false
}

func (c *TCPClient) StartClient() {
	var err error

	addr, err := net.ResolveTCPAddr("tcp", c.Address+":"+c.Port)
	if err != nil {
		Log.Error("Cannot resolve Telnet Client address:", err)
	}

	c.setDefaultParams()
	c.Conn, err = net.DialTCP("tcp", nil, addr)
	if err != nil {
		Log.Error("Cannot connect to Telnet Client:", err)
	}
	Log.Infof("Connected to DX cluster %s:%s", c.Address, c.Port)

	// err = c.Conn.SetKeepAlive(true)
	// if err != nil {
	// 	Log.Error("Error while setting keep alive:", err)
	// }

	c.Reader = bufio.NewReader(c.Conn)
	c.Writer = bufio.NewWriter(c.Conn)

	go func() {
		for message := range c.TCPServer.CmdChan {
			Log.Infof("Received Command: %s", message)
			message := message + "\n"
			c.WriteString(message)
		}
	}()

	go c.ReadLine()
}

func (c *TCPClient) Close() {
	c.Writer.WriteString("bye")
	time.Sleep(time.Second * 2)
}

func (c *TCPClient) SetFilters() {
	if Cfg.Cluster.FT8 {
		c.Write([]byte("set/ft8\r\n"))
		Log.Info("FT8: On")
	}

	if Cfg.Cluster.Skimmer {
		c.Write([]byte("set/skimmer\r\n"))
		Log.Info("Skimmer: On")
	}

	if Cfg.Cluster.FT4 {
		c.Write([]byte("set/ft4\r\n"))
		Log.Info("FT4: On")
	}

	if !Cfg.Cluster.FT8 {
		c.Write([]byte("set/noft8\r\n"))
		Log.Info("FT8: Off")
	}

	if !Cfg.Cluster.FT4 {
		c.Write([]byte("set/noft4\r\n"))
		Log.Info("FT4: Off")
	}

	if !Cfg.Cluster.Skimmer {
		c.Write([]byte("set/noskimmer\r\n"))
		Log.Info("Skimmer: Off")
	}
}

func (c *TCPClient) ReadLine() {

	for {

		// Need to check data with space first to find login and then use \n
		if !c.LoggedIn {
			message, err := c.Reader.ReadString(' ')
			message, _ = strings.CutSuffix(message, "\n")
			message, _ = strings.CutSuffix(message, "\r")

			if err != nil {
				Log.Errorf("Error reading message: %s", err)
				c.Conn.Close()
				c.StartClient()
			}

			if strings.Contains(message, Cfg.Cluster.LoginPrompt) {
				Log.Debug("Found login prompt...sending callsign")
				c.Write([]byte(c.Login + "\r\n"))
				c.LoggedIn = true
				// c.SetFilters()
				// if Cfg.Cluster.Command != "" {
				// 	c.WriteString(Cfg.Cluster.Command + "\n\r")
				// }
				Log.Infof("Connected to cluster %s", Cfg.Cluster.Server)
				Log.Info("Start receiving spots")
				continue
			}
		}

		if c.LoggedIn {
			message, err := c.Reader.ReadString('\n')
			message, _ = strings.CutSuffix(message, "\n")
			message, _ = strings.CutSuffix(message, "\r")

			if strings.Contains(message, "password") {
				Log.Debug("Found password prompt...sending password")
				c.Write([]byte(c.Password + "\r\n"))
			}

			if err != nil {
				Log.Errorf("Error reading message: %s", err)
				c.Conn.Close()
				c.StartClient()
			}

			if strings.Contains(message, "Error reading from server: read tcp") {
				Log.Error("Disconnected from Telnet Server, reconnecting")
				c.Close()
				c.StartClient()
			} else {
				if c.LoggedIn && strings.Contains(message, "DX") {
					ProcessTelnetSpot(spotRe, message, c.SpotChanToFlex, c.SpotChanToHTTPServer, c.Countries)
				}
			}

			// Send the spot message to TCP server
			c.MsgChan <- message

		}

	}
}

// Write sends raw data to remove telnet server
func (tc *TCPClient) Write(data []byte) (n int, err error) {
	n, err = tc.Writer.Write(data)
	if err == nil {
		err = tc.Writer.Flush()
	}

	return
}

func (tc *TCPClient) WriteString(data string) (n int, err error) {
	n, err = tc.Writer.Write([]byte(data))
	if err == nil {
		err = tc.Writer.Flush()
	}

	return
}