package common

import (
	"bufio"
	"crypto/rand"
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"io"
	"net"

	"git.cheetah.cat/cheetah/opentetraflex-go/common/tmkind"
)

type TerminalClient struct {
	address   string
	conn      net.Conn
	netWriter *bufio.Writer
	netReader *bufio.Reader

	HandlerMap map[uint32]chan HandlerResponse

	RX chan *TerminalMessageResponse

	GroupConfig      []GroupConfig
	RegistrationInfo RegistrationInfo
}

func NewTerminalClient(address string) (tc *TerminalClient) {
	return &TerminalClient{
		address:    address,
		RX:         make(chan *TerminalMessageResponse),
		HandlerMap: make(map[uint32]chan HandlerResponse),
	}
}
func (tc *TerminalClient) Connect() (err error) {
	tc.conn, err = net.Dial("tcp", tc.address)
	if err != nil {
		return err
	}
	tc.netWriter = bufio.NewWriter(tc.conn)
	tc.netReader = bufio.NewReader(tc.conn)
	go tc.rxLoop()

	return nil
}
func (tc *TerminalClient) Send(message *TerminalMessage) (err error) {
	_, err = tc.netWriter.Write(message.Encode())
	if err != nil {
		return err
	}
	err = tc.netWriter.Flush()
	if err != nil {
		return err
	}
	return nil
}

func (tc *TerminalClient) Authenticate(addr TetraFlexAddress) (err error) {
	randomBytes := make([]byte, 10)
	_, err = rand.Read(randomBytes)
	if err != nil {
		return err
	}
	//                                                                          20160111
	tfAuthReq, err := NewTMAuthReq(addr, 0, randomBytes, 1, "OpenTetraFlex-Go", 20230831, "1.0.0")
	if err != nil {
		return err
	}
	tc.HandlerMap[tfAuthReq.handler] = make(chan HandlerResponse)

	//fmt.Println(hex.EncodeToString(tfAuthReq.Encode()))
	err = tc.Send(tfAuthReq)
	if err != nil {
		return err
	}
	response := <-tc.HandlerMap[tfAuthReq.handler]
	if !response.Success {
		return response.Error
	}
	tc.RegistrationInfo, err = response.TMR.AsRegistrationInfo()
	if err != nil {
		return err
	}
	return nil
}
func (tc *TerminalClient) SubscribeEvents(messageTypes []tmkind.TetraFlexTerminalMessageKinds) (err error) {
	tfNotReq, err := NewTMNotificationRequest(messageTypes)
	if err != nil {
		return err
	}
	tc.HandlerMap[tfNotReq.handler] = make(chan HandlerResponse)
	err = tc.Send(tfNotReq)
	if err != nil {
		return err
	}
	response := <-tc.HandlerMap[tfNotReq.handler]
	if !response.Success {
		return response.Error
	}
	return nil
}
func (tc *TerminalClient) AttachGroups(groups []GroupConfig) (err error) {
	tfGroupAttachReq, err := NewTMIpApiGroupAttachRequest(groups)
	if err != nil {
		return nil
	}
	fmt.Println(hex.EncodeToString(tfGroupAttachReq.Encode()))
	tc.HandlerMap[tfGroupAttachReq.handler] = make(chan HandlerResponse)

	//fmt.Println(hex.EncodeToString(tfAuthReq.Encode()))
	err = tc.Send(tfGroupAttachReq)
	if err != nil {
		return err
	}
	response := <-tc.HandlerMap[tfGroupAttachReq.handler]
	if !response.Success {
		return response.Error
	}

	return nil
}
func (tc *TerminalClient) CallSetup(address TetraFlexAddress, callType TetraFlexCallTypes, callPriority TetraFlexCallPriorities, hookCall bool, identityInfo TetraFlexIdentityInfo) (callRef byte, err error) {
	tfCallSetupReq, err := NewTMIpApiCallSetupRequest(address, callType, callPriority, hookCall, identityInfo)
	if err != nil {
		return callRef, err
	}
	fmt.Println(hex.EncodeToString(tfCallSetupReq.Encode()))
	tc.HandlerMap[tfCallSetupReq.handler] = make(chan HandlerResponse)

	//fmt.Println(hex.EncodeToString(tfAuthReq.Encode()))
	err = tc.Send(tfCallSetupReq)
	if err != nil {
		return callRef, err
	}
	response := <-tc.HandlerMap[tfCallSetupReq.handler]
	if !response.Success {
		return callRef, response.Error
	}
	//fmt.Println(hex.EncodeToString(response.TMR.payload))
	callRef, err = response.TMR.readByte()
	if err != nil {
		return callRef, err
	}
	return callRef, nil
}
func (tc *TerminalClient) CallDisconnect(callRef byte, disconnectCause TetraFlexCallDisconnectCauses) (err error) {
	tfCallDisconnectReq, err := NewTMIpApiCallDisconnectRequest(callRef, disconnectCause)
	if err != nil {
		return nil
	}
	fmt.Println(hex.EncodeToString(tfCallDisconnectReq.Encode()))
	tc.HandlerMap[tfCallDisconnectReq.handler] = make(chan HandlerResponse)

	//fmt.Println(hex.EncodeToString(tfAuthReq.Encode()))
	err = tc.Send(tfCallDisconnectReq)
	if err != nil {
		return err
	}
	response := <-tc.HandlerMap[tfCallDisconnectReq.handler]
	if !response.Success {
		return response.Error
	}

	return nil
}
func (tc *TerminalClient) CallConnect(callRef byte) (err error) {
	tfCallConnectReq, err := NewTMIpApiCallConnectRequest(callRef)
	if err != nil {
		return nil
	}
	fmt.Println(hex.EncodeToString(tfCallConnectReq.Encode()))
	tc.HandlerMap[tfCallConnectReq.handler] = make(chan HandlerResponse)

	//fmt.Println(hex.EncodeToString(tfAuthReq.Encode()))
	err = tc.Send(tfCallConnectReq)
	if err != nil {
		return err
	}
	response := <-tc.HandlerMap[tfCallConnectReq.handler]
	if !response.Success {
		return response.Error
	}

	return nil
}
func (tc *TerminalClient) PTTRequest(callRef byte, identityInfo TetraFlexIdentityInfo, octaStateByte byte) (err error) {
	// IpApiCallPttRequest
	tfPTTRequest, err := NewTMIpApiCallPTTRequest(callRef, identityInfo, octaStateByte)
	if err != nil {
		return err
	}
	fmt.Println(hex.EncodeToString(tfPTTRequest.Encode()))
	tc.HandlerMap[tfPTTRequest.handler] = make(chan HandlerResponse)

	//fmt.Println(hex.EncodeToString(tfAuthReq.Encode()))
	err = tc.Send(tfPTTRequest)
	if err != nil {
		return err
	}
	response := <-tc.HandlerMap[tfPTTRequest.handler]
	if !response.Success {
		return response.Error
	}
	return nil
}
func (tc *TerminalClient) PTTRelease(callRef byte) (err error) {
	// IpApiCallPttRequest
	tfPTTRelease, err := NewTMIpApiCallPTTRelease(callRef)
	if err != nil {

		return err
	}
	fmt.Println(hex.EncodeToString(tfPTTRelease.Encode()))
	tc.HandlerMap[tfPTTRelease.handler] = make(chan HandlerResponse)

	//fmt.Println(hex.EncodeToString(tfAuthReq.Encode()))
	err = tc.Send(tfPTTRelease)
	if err != nil {
		return err
	}
	response := <-tc.HandlerMap[tfPTTRelease.handler]
	if !response.Success {
		return response.Error
	}
	return nil
}

/*
groupAddr := common.NewAddressFromSSINumber(103)
identityNum := common.NewAddressFromSSINumber(1007)

	response, err = common.NewTMIpApiCallSetupRequest(*groupAddr, common.CallTypeGroup, common.CallPriorityDefault, false, common.TetraFlexIdentityInfo{


		Description:     "blurb",
		Kind:            common.IdentityKindTerminal,
		Address:         *identityNum,
		UnifiedSSIKinds: common.UnifiedKindPersonalNumber,
	})

	if err != nil {
		panic(err)
	}

fmt.Println(hex.EncodeToString(response.Encode()))
terminalClient.Send(response)
*/
func (tc *TerminalClient) rxLoop() {
	var err error
	for {
		dataHeader := make([]byte, 2) //2 bytes for uint16
		_, err = io.ReadFull(tc.netReader, dataHeader)
		if err != nil {
			panic(err)
		}
		payloadLength := binary.LittleEndian.Uint16(dataHeader)
		fmt.Printf("length: %d bytes\n", payloadLength)

		kindData := make([]byte, 2) //2 bytes for uint16
		_, err = io.ReadFull(tc.netReader, kindData)
		if err != nil {
			panic(err)
		}
		messageKind := binary.LittleEndian.Uint16(kindData)
		fmt.Printf("kind: %d\n", messageKind)

		handlerData := make([]byte, 4) //4 bytes for uint32
		_, err = io.ReadFull(tc.netReader, handlerData)
		if err != nil {
			panic(err)
		}
		messageHandler := binary.LittleEndian.Uint32(handlerData)
		fmt.Printf("handler: %d\n", messageHandler)

		dataPayload := make([]byte, payloadLength-(2+2+4)) //8 bytes for header
		_, err = io.ReadFull(tc.netReader, dataPayload)
		if err != nil {
			panic(err)
		}
		parsedResp, err := NewTerminalMessageResponse(payloadLength, tmkind.TetraFlexTerminalMessageKinds(messageKind), messageHandler, dataPayload)
		if err != nil {
			panic(err)
		}

		if _, ok := tc.HandlerMap[messageHandler]; ok {
			tc.HandlerMap[messageHandler] <- HandlerResponse{
				Success: true,
				TMR:     parsedResp,
			}
			delete(tc.HandlerMap, messageHandler)
		} else {
			// Lets Handle KeepAlive for ourself
			if parsedResp.Kind == tmkind.IpApiKeepAliveChallenge {
				challenge, err := parsedResp.AsKeepAliveChallenge()
				if err != nil {
					panic(err)
				}
				response, err := NewTMKeepAliveResponse(challenge)
				if err != nil {
					panic(err)
				}
				err = tc.Send(response)
				if err != nil {
					panic(err)
				}
				continue
			}
			tc.RX <- parsedResp
		}
	}
}