package packets

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"errors"
	"io"
	"unicode/utf8"

	"github.com/winc-link/hummingbird/internal/pkg/codes"
)

// Error type
var (
	ErrInvalUTF8String = errors.New("invalid utf-8 string")
)

// MQTT Version
type Version = byte

type QoS = byte

var version2protoName = map[Version][]byte{
	Version31:  {'M', 'Q', 'I', 's', 'd', 'p'},
	Version311: {'M', 'Q', 'T', 'T'},
	Version5:   {'M', 'Q', 'T', 'T'},
}

const (
	Version31  Version = 0x03
	Version311 Version = 0x04
	Version5   Version = 0x05
	// The maximum packet size of a MQTT packet
	MaximumSize = 268435456
)

//Packet type
const (
	RESERVED = iota
	CONNECT
	CONNACK
	PUBLISH
	PUBACK
	PUBREC
	PUBREL
	PUBCOMP
	SUBSCRIBE
	SUBACK
	UNSUBSCRIBE
	UNSUBACK
	PINGREQ
	PINGRESP
	DISCONNECT
	AUTH
)

// QoS levels & Subscribe failure
const (
	Qos0             uint8 = 0x00
	Qos1             uint8 = 0x01
	Qos2             uint8 = 0x02
	SubscribeFailure       = 0x80
)

// Flag in the FixHeader
const (
	FlagReserved    = 0
	FlagSubscribe   = 2
	FlagUnsubscribe = 2
	FlagPubrel      = 2
)

//PacketID is the type of packet identifier
type PacketID = uint16

//Max & min packet ID
const (
	MaxPacketID PacketID = 65535
	MinPacketID PacketID = 1
)

type PayloadFormat = byte

const (
	PayloadFormatBytes  PayloadFormat = 0
	PayloadFormatString PayloadFormat = 1
)

func IsVersion3X(v Version) bool {
	return v == Version311 || v == Version31
}

func IsVersion5(v Version) bool {
	return v == Version5
}

// FixHeader represents the FixHeader of the MQTT packet
type FixHeader struct {
	PacketType   byte
	Flags        byte
	RemainLength int
}

// Packet defines the interface for structs intended to hold
// decoded MQTT packets, either from being read or before being
// written
type Packet interface {
	// Pack encodes the packet struct into bytes and writes it into io.Writer.
	Pack(w io.Writer) error
	// Unpack read the packet bytes from io.Reader and decodes it into the packet struct
	Unpack(r io.Reader) error
	// String is mainly used in logging, debugging and testing.
	String() string
}

// Topic represents the MQTT Topic
type Topic struct {
	SubOptions
	Name string
}

// SubOptions is the subscription option of subscriptions.
// For details: https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Subscription_Options
type SubOptions struct {
	// Qos is the QoS level of the subscription.
	// 0 = At most once delivery
	// 1 = At least once delivery
	// 2 = Exactly once delivery
	Qos uint8
	// RetainHandling specifies whether retained messages are sent when the subscription is established.
	// 0 = Send retained messages at the time of the subscribe
	// 1 = Send retained messages at subscribe only if the subscription does not currently exist
	// 2 = Do not send retained messages at the time of the subscribe
	RetainHandling byte
	// NoLocal is the No Local option.
	//  If the value is 1, Application Messages MUST NOT be forwarded to a connection with a ClientID equal to the ClientID of the publishing connection
	NoLocal bool
	// RetainAsPublished is the Retain As Published option.
	// If 1, Application Messages forwarded using this subscription keep the RETAIN flag they were published with.
	// If 0, Application Messages forwarded using this subscription have the RETAIN flag set to 0. Retained messages sent when the subscription is established have the RETAIN flag set to 1.
	RetainAsPublished bool
}

// Reader is used to read data from bufio.Reader and create MQTT packet instance.
type Reader struct {
	bufr    *bufio.Reader
	version Version
}

// Writer is used to encode MQTT packet into bytes and write it to bufio.Writer.
type Writer struct {
	bufw *bufio.Writer
}

// Flush writes any buffered data to the underlying io.Writer.
func (w *Writer) Flush() error {
	return w.bufw.Flush()
}

// ReadWriter warps Reader and Writer.
type ReadWriter struct {
	*Reader
	*Writer
}

// NewReader returns a new Reader.
func NewReader(r io.Reader) *Reader {
	if bufr, ok := r.(*bufio.Reader); ok {
		return &Reader{bufr: bufr, version: Version311}
	}
	return &Reader{bufr: bufio.NewReaderSize(r, 2048), version: Version311}
}

func (r *Reader) SetVersion(version Version) {
	r.version = version
}

// NewWriter returns a new Writer.
func NewWriter(w io.Writer) *Writer {
	if bufw, ok := w.(*bufio.Writer); ok {
		return &Writer{bufw: bufw}
	}
	return &Writer{bufw: bufio.NewWriterSize(w, 2048)}
}

// ReadPacket reads data from Reader and returns a  Packet instance.
// If any errors occurs, returns nil, error
func (r *Reader) ReadPacket() (Packet, error) {
	first, err := r.bufr.ReadByte()
	if err != nil {
		return nil, err
	}
	fh := &FixHeader{PacketType: first >> 4, Flags: first & 15} //设置FixHeader
	length, err := EncodeRemainLength(r.bufr)
	if err != nil {
		return nil, err
	}
	fh.RemainLength = length
	packet, err := NewPacket(fh, r.version, r.bufr)
	if err != nil {
		return nil, err
	}
	if p, ok := packet.(*Connect); ok {
		r.version = p.Version
	}
	return packet, err
}

// WritePacket writes the packet bytes to the Writer.
// Call Flush after WritePacket to flush buffered data to the underlying io.Writer.
func (w *Writer) WritePacket(packet Packet) error {
	err := packet.Pack(w.bufw)
	if err != nil {
		return err
	}
	return nil
}

// WriteRaw write raw bytes to the Writer.
// Call Flush after WriteRaw to flush buffered data to the underlying io.Writer.
func (w *Writer) WriteRaw(b []byte) error {
	_, err := w.bufw.Write(b)
	if err != nil {
		return err
	}
	return nil
}

// WriteAndFlush writes and flush the packet bytes to the underlying io.Writer.
func (w *Writer) WriteAndFlush(packet Packet) error {
	err := packet.Pack(w.bufw)
	if err != nil {
		return err
	}
	return w.Flush()
}

// Pack encodes the FixHeader struct into bytes and writes it into io.Writer.
func (fh *FixHeader) Pack(w io.Writer) error {
	var err error
	b := make([]byte, 1)
	packetType := fh.PacketType << 4
	b[0] = packetType | fh.Flags
	length, err := DecodeRemainLength(fh.RemainLength)
	if err != nil {
		return err
	}
	b = append(b, length...)
	_, err = w.Write(b)
	return err
}

//DecodeRemainLength 将remain length 转成byte表示
//
//DecodeRemainLength puts the length int into bytes
func DecodeRemainLength(length int) ([]byte, error) {
	var result []byte
	if length < 128 {
		result = make([]byte, 1)
	} else if length < 16384 {
		result = make([]byte, 2)
	} else if length < 2097152 {
		result = make([]byte, 3)
	} else if length < 268435456 {
		result = make([]byte, 4)
	} else {
		return nil, codes.ErrMalformed
	}
	var i int
	for {
		encodedByte := length % 128
		length = length / 128
		// if there are more data to encode, set the top bit of this byte
		if length > 0 {
			encodedByte = encodedByte | 128
		}
		result[i] = byte(encodedByte)
		i++
		if length <= 0 {
			break
		}
	}
	return result, nil
}

// EncodeRemainLength 读remainLength,如果格式错误返回 error
//
// EncodeRemainLength reads the remain length bytes from bufio.Reader and returns length int.
func EncodeRemainLength(r io.ByteReader) (int, error) {
	var vbi uint32
	var multiplier uint32
	for {
		digit, err := r.ReadByte()
		if err != nil && err != io.EOF {
			return 0, err
		}
		vbi |= uint32(digit&127) << multiplier
		if vbi > 268435455 {
			return 0, codes.ErrMalformed
		}
		if (digit & 128) == 0 {
			break
		}
		multiplier += 7
	}
	return int(vbi), nil
}

// EncodeUTF8String encodes the bytes into UTF-8 encoded strings, returns the encoded bytes, bytes size and error.
func EncodeUTF8String(buf []byte) (b []byte, size int, err error) {
	buflen := len(buf)
	if buflen > 65535 {
		return nil, 0, codes.ErrMalformed
	}
	//length := int(binary.BigEndian.Uint16(buf[0:2]))
	bufw := make([]byte, 2, 2+buflen)
	binary.BigEndian.PutUint16(bufw, uint16(buflen))
	bufw = append(bufw, buf...)
	return bufw, 2 + buflen, nil
}

func readUint16(r *bytes.Buffer) (uint16, error) {
	if r.Len() < 2 {
		return 0, codes.ErrMalformed
	}
	return binary.BigEndian.Uint16(r.Next(2)), nil
}

func writeUint16(w *bytes.Buffer, i uint16) {
	w.WriteByte(byte(i >> 8))
	w.WriteByte(byte(i))
}
func writeUint32(w *bytes.Buffer, i uint32) {
	w.WriteByte(byte(i >> 24))
	w.WriteByte(byte(i >> 16))
	w.WriteByte(byte(i >> 8))
	w.WriteByte(byte(i))
}

func readUint32(r *bytes.Buffer) (uint32, error) {
	if r.Len() < 4 {
		return 0, codes.ErrMalformed
	}
	return binary.BigEndian.Uint32(r.Next(4)), nil
}

func readBinary(r *bytes.Buffer) (b []byte, err error) {
	return readUTF8String(false, r)
}

func readUTF8String(mustUTF8 bool, r *bytes.Buffer) (b []byte, err error) {
	if r.Len() < 2 {
		return nil, codes.ErrMalformed
	}
	length := int(binary.BigEndian.Uint16(r.Next(2)))
	if r.Len() < length {
		return nil, codes.ErrMalformed
	}
	payload := r.Next(length)
	if mustUTF8 {
		if !ValidUTF8(payload) {
			return nil, codes.ErrMalformed
		}
	}
	return payload, nil
}

// the length of s cannot be greater than 65535
func writeUTF8String(w *bytes.Buffer, s []byte) {
	writeUint16(w, uint16(len(s)))
	w.Write(s)
}
func writeBinary(w *bytes.Buffer, b []byte) {
	writeUint16(w, uint16(len(b)))
	w.Write(b)
}

// DecodeUTF8String decodes the  UTF-8 encoded strings into bytes, returns the decoded bytes, bytes size and error.
func DecodeUTF8String(buf []byte) (b []byte, size int, err error) {
	buflen := len(buf)
	if buflen < 2 {
		return nil, 0, ErrInvalUTF8String
	}
	length := int(binary.BigEndian.Uint16(buf[0:2]))
	if buflen < length+2 {
		return nil, 0, ErrInvalUTF8String
	}
	payload := buf[2 : length+2]
	if !ValidUTF8(payload) {
		return nil, 0, ErrInvalUTF8String
	}

	return payload, length + 2, nil
}

// NewPacket returns a packet representing the decoded MQTT packet and an error.
func NewPacket(fh *FixHeader, version Version, r io.Reader) (Packet, error) {
	switch fh.PacketType {
	case CONNECT:
		return NewConnectPacket(fh, version, r)
	case CONNACK:
		return NewConnackPacket(fh, version, r)
	case PUBLISH:
		return NewPublishPacket(fh, version, r)
	case PUBACK:
		return NewPubackPacket(fh, version, r)
	case PUBREC:
		return NewPubrecPacket(fh, version, r)
	case PUBREL:
		return NewPubrelPacket(fh, r)
	case PUBCOMP:
		return NewPubcompPacket(fh, version, r)
	case SUBSCRIBE:
		return NewSubscribePacket(fh, version, r)
	case SUBACK:
		return NewSubackPacket(fh, version, r)
	case UNSUBSCRIBE:
		return NewUnsubscribePacket(fh, version, r)
	case PINGREQ:
		return NewPingreqPacket(fh, r)
	case DISCONNECT:
		return NewDisConnectPackets(fh, version, r)
	case UNSUBACK:
		return NewUnsubackPacket(fh, version, r)
	case PINGRESP:
		return NewPingrespPacket(fh, r)
	case AUTH:
		return NewAuthPacket(fh, r)
	default:
		return nil, codes.ErrProtocol

	}
}

// ValidUTF8 验证是否utf8
//
// ValidUTF8 returns whether the given bytes is in UTF-8 form.
func ValidUTF8(p []byte) bool {
	for {
		if len(p) == 0 {
			return true
		}
		ru, size := utf8.DecodeRune(p)
		if ru >= '\u0000' && ru <= '\u001f' { //[MQTT-1.5.3-2]
			return false
		}
		if ru >= '\u007f' && ru <= '\u009f' {
			return false
		}
		if ru == utf8.RuneError {
			return false
		}
		if !utf8.ValidRune(ru) {
			return false
		}
		if size == 0 {
			return true
		}
		p = p[size:]
	}
}

// ValidTopicName returns whether the bytes is a valid non-shared topic filter.[MQTT-4.7.1-1].
func ValidTopicName(mustUTF8 bool, p []byte) bool {
	for len(p) > 0 {
		ru, size := utf8.DecodeRune(p)
		if mustUTF8 && ru == utf8.RuneError {
			return false
		}
		if size == 1 {
			//主题名不允许使用通配符
			if p[0] == byte('+') || p[0] == byte('#') {
				return false
			}
		}
		p = p[size:]
	}
	return true
}

// ValidV5Topic returns whether the given bytes is a valid MQTT V5 topic
func ValidV5Topic(p []byte) bool {
	if len(p) == 0 {
		return false
	}
	if bytes.HasPrefix(p, []byte("$share/")) {
		if len(p) < 9 {
			return false
		}
		if p[7] != '/' {
			subp := p[7:]
			for len(subp) > 0 {
				ru, size := utf8.DecodeRune(subp)
				if ru == utf8.RuneError {
					return false
				}
				if size == 1 {
					if subp[0] == '/' {
						return ValidTopicFilter(true, subp[1:])
					}
					if subp[0] == byte('+') || subp[0] == byte('#') {
						return false
					}
				}
				subp = subp[size:]
			}

		}
		return false
	}
	return ValidTopicFilter(true, p)
}

// ValidTopicFilter 验证主题过滤器是否合法
//
// ValidTopicFilter  returns whether the bytes is a valid topic filter. [MQTT-4.7.1-2]  [MQTT-4.7.1-3]
// ValidTopicFilter 验证主题过滤器是否合法
//
// ValidTopicFilter  returns whether the bytes is a valid topic filter. [MQTT-4.7.1-2]  [MQTT-4.7.1-3]
func ValidTopicFilter(mustUTF8 bool, p []byte) bool {
	if len(p) == 0 {
		return false
	}
	var prevByte byte //前一个字节
	var isSetPrevByte bool

	for len(p) > 0 {
		ru, size := utf8.DecodeRune(p)
		if mustUTF8 && ru == utf8.RuneError {
			return false
		}
		plen := len(p)
		if p[0] == byte('#') && plen != 1 { // #一定是最后一个字符
			return false
		}
		if size == 1 && isSetPrevByte {
			// + 前（如果有前后字节）,一定是'/' [MQTT-4.7.1-2]  [MQTT-4.7.1-3]
			if (p[0] == byte('+') || p[0] == byte('#')) && prevByte != byte('/') {
				return false
			}

			if plen > 1 { // p[0] 不是最后一个字节
				if p[0] == byte('+') && p[1] != byte('/') { // + 后（如果有字节）,一定是 '/'
					return false
				}
			}
		}
		prevByte = p[0]
		isSetPrevByte = true
		p = p[size:]
	}
	return true
}

// TopicMatch 返回topic和topic filter是否
//
// TopicMatch returns whether the topic and topic filter is matched.
func TopicMatch(topic []byte, topicFilter []byte) bool {
	var spos int
	var tpos int
	var sublen int
	var topiclen int
	var multilevelWildcard bool //是否是多层的通配符
	sublen = len(topicFilter)
	topiclen = len(topic)
	if sublen == 0 || topiclen == 0 {
		return false
	}
	if (topicFilter[0] == '$' && topic[0] != '$') || (topic[0] == '$' && topicFilter[0] != '$') {
		return false
	}
	for {
		//e.g. ([]byte("foo/bar"),[]byte("foo/+/#")
		if spos < sublen && tpos <= topiclen {
			if tpos != topiclen && topicFilter[spos] == topic[tpos] { // sublen是订阅 topiclen是发布,首字母匹配
				if tpos == topiclen-1 { //遍历到topic的最后一个字节
					/* Check for e.g. foo matching foo/# */
					if spos == sublen-3 && topicFilter[spos+1] == '/' && topicFilter[spos+2] == '#' {
						return true
					}
				}
				spos++
				tpos++
				if spos == sublen && tpos == topiclen { //长度相等，内容相同，匹配
					return true
				} else if tpos == topiclen && spos == sublen-1 && topicFilter[spos] == '+' {
					//订阅topic比发布topic多一个字节，并且多出来的内容是+ ,比如: sub: foo/+ ,topic: foo/
					if spos > 0 && topicFilter[spos-1] != '/' {
						return false
					}
					spos++
					return true
				}
			} else {
				if topicFilter[spos] == '+' { //sub 和 topic 内容不匹配了
					spos++
					for { //找到topic的下一个主题分割符
						if tpos < topiclen && topic[tpos] != '/' {
							tpos++
						} else {
							break
						}
					}
					if tpos == topiclen && spos == sublen { //都遍历完了,返回true
						return true
					}
				} else if topicFilter[spos] == '#' {
					multilevelWildcard = true
					return true
				} else {
					/* Check for e.g. foo/bar matching foo/+/# */
					if spos > 0 && spos+2 == sublen && tpos == topiclen && topicFilter[spos-1] == '+' && topicFilter[spos] == '/' && topicFilter[spos+1] == '#' {
						multilevelWildcard = true
						return true
					}
					return false
				}
			}
		} else {
			break
		}
	}
	if !multilevelWildcard && (tpos < topiclen || spos < sublen) {
		return false
	}
	return false
}

// TotalBytes returns how many bytes of the packet
func TotalBytes(p Packet) uint32 {
	var header *FixHeader
	switch pt := p.(type) {
	case *Auth:
		header = pt.FixHeader
	case *Connect:
		header = pt.FixHeader
	case *Connack:
		header = pt.FixHeader
	case *Disconnect:
		header = pt.FixHeader
	case *Pingreq:
		header = pt.FixHeader
	case *Pingresp:
		header = pt.FixHeader
	case *Puback:
		header = pt.FixHeader
	case *Pubcomp:
		header = pt.FixHeader
	case *Publish:
		header = pt.FixHeader
	case *Pubrec:
		header = pt.FixHeader
	case *Pubrel:
		header = pt.FixHeader
	case *Suback:
		header = pt.FixHeader
	case *Subscribe:
		header = pt.FixHeader
	case *Unsuback:
		header = pt.FixHeader
	case *Unsubscribe:
		header = pt.FixHeader
	}
	if header == nil {
		return 0
	}
	var headerLength uint32
	if header.RemainLength <= 127 {
		headerLength = 2
	} else if header.RemainLength <= 16383 {
		headerLength = 3
	} else if header.RemainLength <= 2097151 {
		headerLength = 4
	} else if header.RemainLength <= 268435455 {
		headerLength = 5
	}
	return headerLength + uint32(header.RemainLength)

}
