package atservice

import (
	"netease_control_center/interfaces/log"
	"os"
	"path/filepath"
	"strconv"
)

const (
	HEAD       = 1
	DATA       = 0
	PACKET_128 = 128
	PACKET_1k  = 1024
	maxByte    = 255
)

const (
	SOH    = 0x01
	STX    = 0x02
	EOT    = 0x04
	ACK    = 0x06
	NAK    = 0x15
	CAN    = 0x18
	C_CHAR = 0x43
)

type packet struct {
	//fileOTA  *os.File
	fileName   string
	filePath   string
	fileSize   int
	remainSize int

	size    int
	pkgType uint8
	num     uint8
	buf     []byte
}

func otaStart(filePath string) {
	if filePath == "" {
		return
	}

	ymodem_xfr(filePath)
}

func calcCrc(buf []byte) uint16 {
	var crc int = 0
	for i := 0; i < len(buf); i++ {
		crc = crc ^ int(buf[i])<<8
		for j := 0; j < 8; j++ {
			if crc&0x8000 != 0 {
				crc = crc<<1 ^ 0x1021
			} else {
				crc = crc << 1
			}
		}
	}
	return uint16(crc)
}

func packet_send(p *packet) {
	if p.pkgType == HEAD {
		useData := []byte{}
		useData = append(useData, []byte(p.fileName)...)
		useData = append(useData, []byte{0}...)
		str := strconv.Itoa(int(p.fileSize))
		useData = append(useData, []byte(str)...)
		appData := make([]byte, PACKET_128-len(useData))
		useData = append(useData, appData...)

		crcResult := calcCrc(useData)

		pktData := []byte{SOH, p.num, (maxByte - p.num)}
		p.num++
		pktData = append(pktData, useData...)
		pktData = append(pktData, byte(crcResult>>8))
		pktData = append(pktData, byte(crcResult))
		//log.Println(log.DebugLog, "pkt:", pktData)
		//log.Printf(log.DebugLog, "pkt hex:%x\n", pktData)
		uartWrite(pktData)
	} else {

		var pktSize byte
		if p.size == PACKET_128 {
			pktSize = SOH
		} else {
			pktSize = STX
		}
		crcResult := calcCrc(p.buf)
		pktData := []byte{pktSize, p.num, (maxByte - p.num)}
		p.num++
		pktData = append(pktData, p.buf...)
		pktData = append(pktData, byte(crcResult>>8))
		pktData = append(pktData, byte(crcResult))
		//log.Println(log.DebugLog, "pkt:", pktData)
		//log.Printf(log.DebugLog, "pkt hex:%x\n", pktData)
		uartWrite(pktData)
	}
}

func packet_xfr(p *packet) {
	if p.pkgType == HEAD {
		packet_send(p)
	} else {
		if p.remainSize > PACKET_1k {
			p.size = PACKET_1k
		} else {
			p.size = PACKET_128
		}

		f, err := os.Open(p.filePath)
		defer f.Close()
		buf := make([]byte, p.size)
		end := false
		if err != nil {
			log.Println(log.ErrorLog, "open file, err = ", err)
		}

		for {
			if end {
				break
			}
			d, _ := uartRead1byte()
			//log.Printf(log.DebugLog, "get data: 0x%x\n", d)
			switch d {
			case C_CHAR:
				fallthrough
			case ACK:
				n, _ := f.Read(buf)
				//log.Println(log.DebugLog, "Read file, n = ", n)
				if p.remainSize <= 0 {
					for i := 0; i < 3; i++ {
						uartWrite([]byte{EOT})
						if d, _ := uartRead1byte(); d == ACK {
							break
						}
					}

					if d, _ := uartRead1byte(); d == 'C' || d == 'c' {
						p.size = PACKET_128
						p.buf = make([]byte, 128, 128)
						p.num = 0
						packet_send(p)

						if d, _ := uartRead1byte(); d == ACK {
							end = true
							break
						}
					}
				}

				p.remainSize -= n
				p.buf = buf
				if OtaFinishCb != nil {
					var progress uint8 = uint8((p.fileSize - p.remainSize) * 100 / p.fileSize)
					if progress < 100 {
						OtaFinishCb(false, progress, nil)
					}
				}

				packet_send(p)
			case NAK:
				packet_send(p)
			}
		}
	}
}

func ymodem_xfr(filePath string) {
	var pkt packet
	pkt.filePath = filePath
	pkt.fileName = filepath.Base(pkt.filePath)

	if fInfo, err := os.Stat(pkt.filePath); err == nil {
		pkt.fileSize = int(fInfo.Size())
		log.Println(log.DebugLog, "filesize:", pkt.fileSize)
		pkt.remainSize = pkt.fileSize
	}

	pkt.num = 0

	if d, _ := uartRead1byte(); d == 'C' {
		pkt.pkgType = HEAD
		//log.Println(log.DebugLog, "send head-->")
		packet_xfr(&pkt)
		if d, _ := uartRead1byte(); d == ACK {
			pkt.pkgType = DATA
			//log.Println(log.DebugLog, "send body-->")
			packet_xfr(&pkt)
		}
	}

}
