package main

import (
	"encoding/binary"
	"errors"
	"fmt"
	"github.com/golang/protobuf/proto"
	"io"
	"leaf/log"
	"net"
	"reflect"
)

func Read(conn net.Conn) ([]byte, error) {
	var b [4]byte
	bufMsgLen := b[:lenMsgLen]

	// read len
	if _, err := io.ReadFull(conn, bufMsgLen); err != nil {
		return nil, err
	}

	// parse len
	var msgLen uint32
	switch lenMsgLen {
	case 1:
		msgLen = uint32(bufMsgLen[0])
	case 2:
		if littleEndian {
			msgLen = uint32(binary.LittleEndian.Uint16(bufMsgLen))
		} else {
			msgLen = uint32(binary.BigEndian.Uint16(bufMsgLen))
		}
	case 4:
		if littleEndian {
			msgLen = binary.LittleEndian.Uint32(bufMsgLen)
		} else {
			msgLen = binary.BigEndian.Uint32(bufMsgLen)
		}
	}

	// check len
	if msgLen > maxMsgLen {
		return nil, errors.New("message too long")
	} else if msgLen < minMsgLen {
		return nil, errors.New("message too short")
	}

	// data
	msgData := make([]byte, msgLen)
	if _, err := io.ReadFull(conn, msgData); err != nil {
		return nil, err
	}

	return msgData, nil
}

// goroutine safe
func Write(conn net.Conn, args ...[]byte) error {
	// get len
	var msgLen uint32
	for i := 0; i < len(args); i++ {
		msgLen += uint32(len(args[i]))
	}

	// check len
	if msgLen > maxMsgLen {
		return errors.New("message too long")
	} else if msgLen < minMsgLen {
		return errors.New("message too short")
	}

	msg := make([]byte, uint32(lenMsgLen)+msgLen)

	// write len
	switch lenMsgLen {
	case 1:
		msg[0] = byte(msgLen)
	case 2:
		if littleEndian {
			binary.LittleEndian.PutUint16(msg, uint16(msgLen))
		} else {
			binary.BigEndian.PutUint16(msg, uint16(msgLen))
		}
	case 4:
		if littleEndian {
			binary.LittleEndian.PutUint32(msg, msgLen)
		} else {
			binary.BigEndian.PutUint32(msg, msgLen)
		}
	}

	// write data
	l := lenMsgLen
	for i := 0; i < len(args); i++ {
		copy(msg[l:], args[i])
		l += len(args[i])
	}

	// 发送消息到服务器
	_, err := conn.Write([]byte(msg))
	if err != nil {
		log.Fatal("Error writing to server: ", err)
	}

	return nil
}

func WriteMsgByMsgID(conn net.Conn, mainCmd, subCmd uint8, msg interface{}) {
	data, err := MarshalByMsgID(mainCmd, subCmd, msg)
	if err != nil {
		log.Error("marshal message %v error: %v", reflect.TypeOf(msg), err)
		return
	}
	err = Write(conn, data...)
	if err != nil {
		log.Error("write message %v error: %v", reflect.TypeOf(msg), err)
	}
}

func MarshalByMsgID(mainCmd, subCmd uint8, msg interface{}) ([][]byte, error) {
	// id
	msgID := encodeMsgID(mainCmd, subCmd)
	id := make([]byte, 2)
	if littleEndian {
		binary.LittleEndian.PutUint16(id, msgID)
	} else {
		binary.BigEndian.PutUint16(id, msgID)
	}

	// data  序列化
	data, err := proto.Marshal(msg.(proto.Message))
	return [][]byte{id, data}, err
}

// goroutine safe
func Unmarshal(data []byte) (uint16, error) {
	if len(data) < 2 {
		return 0, errors.New("protobuf data too short")
	}

	// id
	var id uint16
	if littleEndian {
		id = binary.LittleEndian.Uint16(data)
	} else {
		id = binary.BigEndian.Uint16(data)
	}
	fmt.Println("id:", id)
	return id, nil
}

// 编码消息ID:主命令范围 0-255，子命令范围 0-255
func encodeMsgID(mainCmd uint8, subCmd uint8) uint16 {
	return uint16(mainCmd)<<8 | uint16(subCmd)
}

// 解码消息ID
func decodeMsgID(msgID uint16) (uint8, uint8) {
	mainCmd := uint8(msgID >> 8)  // 提取高 8 位
	subCmd := uint8(msgID & 0xFF) // 提取低 8 位
	return mainCmd, subCmd
}
