package protobuf

import (
	"encoding/binary"
	"errors"
	"fmt"
	"github.com/golang/protobuf/proto"
	"github.com/name5566/leaf/chanrpc"
	"github.com/name5566/leaf/log"
	"math"
	"reflect"
)

// -------------------------
// | id | protobuf message |
// -------------------------
type Processor struct {
	littleEndian bool
	msgInfoList  map[uint16]*MsgInfo     //消息列表:key=msgId
	msgIdList    map[reflect.Type]uint16 //消息列表列表: key=消息类型; 消息ID规则由原从0自增修改为主命令+子命令（高8位+低8位）
}

type MsgInfo struct {
	msgType       reflect.Type
	msgRouter     *chanrpc.Server
	msgHandler    MsgHandler
	msgRawHandler MsgHandler
}

type MsgHandler func([]interface{})

type MsgRaw struct {
	msgID      uint16
	msgRawData []byte
}

func NewProcessor() *Processor {
	p := new(Processor)
	p.littleEndian = false
	p.msgInfoList = make(map[uint16]*MsgInfo)
	p.msgIdList = make(map[reflect.Type]uint16)
	return p
}

// It's dangerous to call the method on routing or marshaling (unmarshaling)
func (p *Processor) SetByteOrder(littleEndian bool) {
	p.littleEndian = littleEndian
}

// It's dangerous to call the method on routing or marshaling (unmarshaling)
func (p *Processor) Register(mainCmd, subCmd uint8, msg proto.Message) uint16 {
	msgType := reflect.TypeOf(msg)
	if msgType == nil || msgType.Kind() != reflect.Ptr {
		log.Fatal("protobuf message pointer required")
	}
	if _, ok := p.msgIdList[msgType]; ok {
		log.Fatal("message %s is already registered", msgType)
	}
	if len(p.msgInfoList) >= math.MaxUint16 {
		log.Fatal("too many protobuf messages (max = %v)", math.MaxUint16)
	}

	i := new(MsgInfo)
	i.msgType = msgType
	id := encodeMsgID(mainCmd, subCmd)
	p.msgInfoList[id] = i
	p.msgIdList[msgType] = id
	return id
}

// It's dangerous to call the method on routing or marshaling (unmarshaling)
func (p *Processor) SetRouter(msg proto.Message, msgRouter *chanrpc.Server) {
	msgType := reflect.TypeOf(msg)
	id, ok := p.msgIdList[msgType]
	if !ok {
		log.Fatal("message %s not registered", msgType)
	}

	p.msgInfoList[id].msgRouter = msgRouter
}

// It's dangerous to call the method on routing or marshaling (unmarshaling)
func (p *Processor) SetHandler(msg proto.Message, msgHandler MsgHandler) {
	msgType := reflect.TypeOf(msg)
	id, ok := p.msgIdList[msgType]
	if !ok {
		log.Fatal("message %s not registered", msgType)
	}

	p.msgInfoList[id].msgHandler = msgHandler
}

// It's dangerous to call the method on routing or marshaling (unmarshaling)
func (p *Processor) SetRawHandler(id uint16, msgRawHandler MsgHandler) {
	//if id >= uint16(len(p.msgInfo)) {
	//	log.Fatal("message id %v not registered", id)
	//}

	p.msgInfoList[id].msgRawHandler = msgRawHandler
}

// goroutine safe
func (p *Processor) Route(msg interface{}, userData interface{}) error {
	// raw
	if msgRaw, ok := msg.(MsgRaw); ok {
		//if msgRaw.msgID >= uint16(len(p.msgInfo)) {
		//	return fmt.Errorf("message id %v not registered", msgRaw.msgID)
		//}
		i := p.msgInfoList[msgRaw.msgID]
		if i.msgRawHandler != nil {
			i.msgRawHandler([]interface{}{msgRaw.msgID, msgRaw.msgRawData, userData})
		}
		return nil
	}

	// protobuf
	msgType := reflect.TypeOf(msg)
	id, ok := p.msgIdList[msgType]
	if !ok {
		return fmt.Errorf("message %s not registered", msgType)
	}
	i := p.msgInfoList[id]
	if i.msgHandler != nil {
		i.msgHandler([]interface{}{msg, userData})
	}
	if i.msgRouter != nil {
		i.msgRouter.Go(msgType, msg, userData)
	}
	return nil
}

// goroutine safe
func (p *Processor) Unmarshal(data []byte) (interface{}, error) {
	if len(data) < 2 {
		return nil, errors.New("protobuf data too short")
	}

	// id
	var id uint16
	if p.littleEndian {
		id = binary.LittleEndian.Uint16(data)
	} else {
		id = binary.BigEndian.Uint16(data)
	}
	//if id >= uint16(len(p.msgInfo)) {
	//	return nil, fmt.Errorf("message id %v not registered", id)
	//}

	log.Debug("read message id: %d", id)

	// msg
	i := p.msgInfoList[id]
	if i == nil {
		return nil, fmt.Errorf("message id %v not registered", id)
	}

	if i.msgRawHandler != nil {
		return MsgRaw{id, data[2:]}, nil
	} else {
		msg := reflect.New(i.msgType.Elem()).Interface()
		return msg, proto.UnmarshalMerge(data[2:], msg.(proto.Message))
	}
}

// goroutine safe
func (p *Processor) Marshal(msg interface{}) ([][]byte, error) {
	msgType := reflect.TypeOf(msg)

	// id
	_id, ok := p.msgIdList[msgType]
	if !ok {
		err := fmt.Errorf("message %s not registered", msgType)
		return nil, err
	}

	id := make([]byte, 2)
	if p.littleEndian {
		binary.LittleEndian.PutUint16(id, _id)
	} else {
		binary.BigEndian.PutUint16(id, _id)
	}

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

// goroutine safe
func (p *Processor) MarshalByMsgID(mainCmd, subCmd uint8, msg interface{}) ([][]byte, error) {
	// id
	msgID := encodeMsgID(mainCmd, subCmd)
	id := make([]byte, 2)
	if p.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 (p *Processor) Range(f func(id uint16, t reflect.Type)) {
	for id, i := range p.msgInfoList {
		f(uint16(id), i.msgType)
	}
}

// 编码消息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
}
