package kraProtocol

import (
	"reflect"

	"krakatoa.net/backend/modules/structs"
)

const (
	StatusSend                  = 10
	StatusGet                   = 11
	StatusMessageAlreadyInQueue = 12
	StatusErrNoPerms            = 13
	StatusErrTryAgain           = 14
	StatusErrArgLenTooBig       = 15
	StatusReply                 = 16
)

const (
	DestBackend = 1
	DestVoice   = 2
	DestMobile  = 3

	DestProxy                = 10
	DestPlayersCurrentServer = 11
)

func SendMessageToClient(status int, cmdID int, cmdNumber int, args interface{}) []byte {
	argType := reflect.TypeOf(args).String()
	var argLen int
	var kraProtocolMessageToClientData structs.KraProtocolMessageToClient

	if argType == "string" {
		argLen = len(args.(string))
	} else {
		kraProtocolMessageToClientData = args.(structs.KraProtocolMessageToClient)

		argLen = kraProtocolMessageToClientData.ByteLength
	}

	rawLen := 4 + argLen

	raw := make([]byte, rawLen)

	raw[0] = byte(status)

	raw[1] = byte(cmdID)

	raw[2] = byte(cmdNumber & 0xFF)
	raw[3] = byte((cmdNumber >> 8) & 0xFF)

	if argLen > 4 {
		raw[4] = byte(argLen & 0xFF)
		raw[5] = byte((argLen >> 8) & 0xFF)

		if argType == "string" {
			for i, c := range reflect.ValueOf(args).String() {
				raw[4+i] = byte(c)
			}
		} else {
			raw[4] = byte(kraProtocolMessageToClientData.Value & 0xFF)
			for i := 1; i < kraProtocolMessageToClientData.ByteLength; i++ {
				raw[4+i] = byte((kraProtocolMessageToClientData.Value >> (8 * i)) & 0xFF)
			}
		}
	}

	return raw
}

// for testing only
func SimulateMessageFromWebClient(status int, cmdID int, dest int, cmdNumber int, args string) []byte {
	argLen := len(args)

	rawLen := 4 + argLen + 1

	raw := make([]byte, rawLen)

	raw[0] = byte(status)

	raw[1] = byte(cmdID)

	raw[2] = byte(dest)

	raw[3] = byte(cmdNumber & 0xFF)
	raw[4] = byte((cmdNumber >> 8) & 0xFF)

	raw[5] = byte(argLen & 0xFF)
	raw[6] = byte((argLen >> 8) & 0xFF)

	for i, c := range args {
		raw[5+i] = byte(c)
	}

	return raw
}

func SendMessageToJava(status int, cmdID int, dest int, playerUuid string, cmdNumber int, args interface{}) []byte {
	argType := reflect.TypeOf(args).String()
	var argLen int
	var kraProtocolMessageToClientData structs.KraProtocolMessageToClient

	if argType == "string" {
		argLen = len(args.(string))
	} else {
		kraProtocolMessageToClientData = args.(structs.KraProtocolMessageToClient)

		argLen = kraProtocolMessageToClientData.ByteLength
	}

	argLen = 40 + argLen

	raw := make([]byte, argLen)

	raw[0] = byte(status)

	raw[1] = byte(cmdID)
	raw[2] = byte((cmdID >> 8) & 0xFF)
	raw[3] = byte((cmdID >> 16) & 0xFF)
	raw[4] = byte((cmdID >> 24) & 0xFF)

	raw[5] = byte(dest)

	for i, c := range playerUuid {
		raw[6+i] = byte(c)
	}

	raw[38] = byte(cmdNumber)
	raw[39] = byte((cmdNumber >> 8) & 0xFF)

	if argLen > 40 {
		if argType == "string" {
			for i, c := range reflect.ValueOf(args).String() {
				raw[40+i] = byte(c)
			}
		} else {
			raw[40] = byte(kraProtocolMessageToClientData.Value & 0xFF)
			for i := 1; i < kraProtocolMessageToClientData.ByteLength; i++ {
				raw[40+i] = byte((kraProtocolMessageToClientData.Value >> (8 * i) & 0xFF))
			}
		}
	}

	return raw
}

func DecodeMessage(data []byte) (Status int, CmdID int, Dest int, CmdNumber int, Args string) {
	status := data[0]
	cmdID := data[1]
	dest := data[2]

	cmdNumber := int(data[4])*256 + int(data[3])

	argLen := len(data) - 5

	args := make([]byte, argLen)

	for i := 0; i < int(argLen); i++ {
		args[i] = data[5+i]
	}

	return int(status), int(cmdID), int(dest), int(cmdNumber), string(args)
}

func DecodeJavaMessage(data []byte) (Status int, CmdID int, Dest int, PlayerUuid string, CmdNumber int, Args string) {
	status := data[0]
	cmdID := int(data[1]) + int(data[2])*256 + int(data[3])*256*256 + int(data[4])*256*256*256
	dest := data[5]

	playerUuid := make([]byte, 32)

	for i := 0; i < 32; i++ {
		playerUuid[i] = data[6+i]
	}

	cmdNumber := int(data[38]) + int(data[39])*256

	argLen := len(data) - 40

	args := make([]byte, argLen)

	for i := 0; i < int(argLen); i++ {
		args[i] = data[40+i]
	}

	return int(status), cmdID, int(dest), string(playerUuid), cmdNumber, string(args)
}
