package lc

import (
	"fmt"
	"github.com/sirupsen/logrus"
	"golang.org/x/text/encoding/simplifiedchinese"
	"lc-smartX/bx"
	"net"
	"strconv"
	"time"
)

type Screener interface {
	Display()
}

type Screen struct {
	Name      string
	Conn      net.Conn
	IsLive    bool
	StateInfo *bx.StateInfo //状态信息
	Params    *bx.Params    //屏参
}

func NewScreen(name string, conn net.Conn) Screen {
	s := Screen{
		Name:      name,
		Conn:      conn,
		IsLive:    true,
		StateInfo: &bx.StateInfo{},
		Params:    &bx.Params{},
	}
	state := s.State()
	s.StateInfo.Parse(state.Data)
	params := s.Param()
	s.Params.Parse(params.Data)
	return s
}

func (s *Screen) Display() {
	if !s.IsLive {
		return
	}
	s.Lock(1, "P001")
}

func (s *Screen) Back() {
	s.Lock(1, "P000")
}

// Correct 校正时间
func (s *Screen) Correct() {
	now := time.Now()
	if !s.IsLive {
		return
	}
	cmd := bx.NewBxCmdSystemClockCorrect(now)
	data := bx.NewBxDataPackCmd(cmd)
	s.send(data.Pack())
}

func (s *Screen) send(data []byte) {
	if !s.IsLive {
		return
	}
	_, err := s.Conn.Write(data)
	if err != nil {
		logrus.WithFields(map[string]interface{}{"设备名": s.Name}).Error("tcp write error:", err)
		s.IsLive = false
	}
}

type Color byte

const (
	Default Color = iota
	Red
	Green
	Yellow
	Blue
	LightBlue
	LightPurple
	White
	None
)

// TextRam dispMode显示方式
// 0x01——静止显示
// 0x02——快速打出
// 0x03——向左移动
// 0x04——向右移动
// 0x05——向上移动
// 0x06——向下移动
func (s *Screen) TextRam(ff FlashFile) {
	if !s.IsLive {
		return
	}
	var areas []bx.BxArea
	encoder := simplifiedchinese.GB18030.NewEncoder()
	var bytes []byte
	if ff.color == None || ff.color == Default {
		bytes, _ = encoder.Bytes([]byte(ff.msg))
	} else {
		bytes, _ = encoder.Bytes([]byte("\\C" + strconv.Itoa(int(ff.color)) + ff.msg))
	}
	area := bx.NewBxAreaDynamic(s.StateInfo.DynaAreaNum, 0, byte(ff.dispMode), ff.originX, ff.originY, ff.width, ff.height, bytes, false)
	areas = append(areas, area)
	//
	cmd := bx.NewBxCmdSendDynamicArea(areas)
	pack := bx.NewBxDataPackCmd(cmd)
	pack.SetDispType(1) //动态显示模式
	d := pack.Pack()
	s.send(d)
	s.ReadResp()
	s.StateInfo.DynaAreaNum++
}

// DelRamText 删除动态区,不传删除所有
func (s *Screen) DelRamText(numbers ...byte) {
	if !s.IsLive {
		return
	}
	cmd := bx.NewCmdDelDynamicArea(numbers)
	pack := bx.NewBxDataPackCmd(cmd)
	s.send(pack.Pack())
	s.ReadResp()
	if len(numbers) == 0 {
		s.StateInfo.DynaAreaNum = 0
	} else {
		s.StateInfo.DynaAreaNum--
	}
}

type RunMode byte

const (
	Loop              RunMode = iota //循环
	LoopAndStayAtEnd                 //循环直到最后,停留在最后一个动态区
	LoopAndTimeoutOff                //循环直到超时,超时后未更新不在显示
	LoopAndStayAtLogo                //循环完后,停留显示LOGO
	LoopAndOff                       //循环完后不在显示
	LoopAndCountOff                  //循环设定次数后不在显示
	DefaultRunMode    RunMode = Loop //默认
)

type DispMode byte

const (
	_               DispMode = iota
	Static                   //0x01——静止显示
	QuickPunch               //0x02——快速打出
	MoveLeft                 //0x03——向左移动
	MoveRight                //0x04——向右移动
	MoveUp                   //0x05——向上移动
	MoveDown                 //0x06——向下移动
	DefaultDispMode DispMode = Static
)

type FlashFile struct {
	msg      string
	color    Color
	runMode  RunMode
	dispMode DispMode
	originX  uint16
	originY  uint16
	width    uint16
	height   uint16
}

func (ft *FlashFile) SetMsg(msg string, color Color) {
	ft.msg = msg
	ft.color = color
}
func (ft *FlashFile) SetMode(runMode RunMode, dispMode DispMode) {
	ft.runMode = runMode
	ft.dispMode = dispMode
}

// SetOrigin xIsPixel=true表示x坐标为像素单位, =false表示以字节(8像素)为单位;y只有像素单位
func (ft *FlashFile) SetOrigin(x uint16, xIsPixel bool, y uint16) {
	ft.originY = y
	if xIsPixel {
		ft.originX = 0x8000 | x
		return
	}
	ft.originX = x
}

// SetArea yIsPixel=true表示像素单位, =false表示以字节(8像素)为单位
func (ft *FlashFile) SetArea(w uint16, yIsPixel bool, h uint16) {
	ft.height = h
	if yIsPixel {
		ft.width = 0x8000 | w
		return
	}
	ft.width = w
}

func (s *Screen) TextFlash(ft FlashFile, isLogo bool) {
	if !s.IsLive {
		return
	}
	encoder := simplifiedchinese.GB18030.NewEncoder()
	var bytes []byte
	if ft.color == None || ft.color == Default {
		bytes, _ = encoder.Bytes([]byte(ft.msg))
	} else {
		bytes, _ = encoder.Bytes([]byte("\\C" + strconv.Itoa(int(ft.color)) + ft.msg))
	}
	area := bx.NewBxAreaDynamic(0xff, byte(ft.runMode), byte(ft.dispMode), ft.originX, ft.originY, ft.width, ft.height, bytes, false)
	//if s.FileNum
	name := fmt.Sprintf("P%03d", s.StateInfo.ProgramNum)
	if isLogo {
		name = "LOGO"
	}
	file := bx.NewBxFile(name, "", []bx.BxArea{area})
	cmd := file.NewCmdWriteFile()
	pack := bx.NewBxDataPackCmd(cmd)
	data := pack.Pack()
	s.send(data)
	resp := s.ReadResp()
	if !resp.IsAck() {
		logrus.Error("设备拒绝写文件! error:", resp.Error().Description)
		return
	}
	pack1 := bx.NewBxDataPackCmd(cmd)
	data1 := pack1.Pack()
	s.send(data1)
	resp1 := s.ReadResp()
	if resp1.NoError() {
		s.StateInfo.ProgramNum++
	}
}

func (s *Screen) Bitmap(name string, bitmap []byte) {
	file := bx.NewBitmapFile(name, bitmap)
	cmd := file.NewCmd()
	pack := bx.NewBxDataPackCmd(cmd)
	data := pack.Pack()
	fmt.Println("数据长度:", len(data))
	fmt.Printf("数据:% 02x\n", data)
	s.send(data)
	resp := s.ReadResp()
	if !resp.IsAck() {
		logrus.Error("设备拒绝写文件! error:", resp.Error().Description)
		return
	}
	pack1 := bx.NewBxDataPackCmd(cmd)
	data1 := pack1.Pack()
	fmt.Println("数据长度1:", len(data1))
	s.send(data1)
	fmt.Printf("写图文件数据:% 02x\n", data1)
	s.ReadResp()
}

// Lock 锁定状态:0x00——解锁状态,0x01——锁定状态
func (s *Screen) Lock(flag byte, name string) {
	cmd := bx.NewCmdLock(flag, name)
	pack := bx.NewBxDataPackCmd(&cmd)
	s.send(pack.Pack())
}

func (s *Screen) QueryFile() {

}

func (s *Screen) DelFile(delFiles ...string) {
	cmd := bx.NewCmdDeleteFile(delFiles)
	pack := bx.NewBxDataPackCmd(cmd)
	s.send(pack.Pack())
	s.ReadResp()
	if len(delFiles) == 0 {
		s.StateInfo.ProgramNum = 0
	} else {
		s.StateInfo.ProgramNum--
	}
}

func (s *Screen) DelText(delIds []byte) {
	cmd := bx.NewBxCmdSendDynamicArea(nil)
	cmd.SetDelAreaIds(delIds)
	pack := bx.NewBxDataPackCmd(cmd)
	s.send(pack.Pack())
}

func (s *Screen) TurnOnOff(onOff bool) {
	if !s.IsLive {
		return
	}
	cmd := bx.NewBxCmdTurnOnOff(onOff)
	pack := bx.NewBxDataPackCmd(cmd)
	s.send(pack.Pack())
}

// TimingSwitch 定时开关屏
// 13:49 开, 13:55 关,最多设置3组
//
//	onOffSet := [][2]uint64{
//			{1349, 1355},
//		}
func (s *Screen) TimingSwitch(onOffSet [][2]uint64) {
	if !s.IsLive {
		return
	}
	cmd := bx.NewCmdTimingSwitch(onOffSet)
	pack := bx.NewBxDataPackCmd(cmd)
	s.send(pack.Pack())
}

func (s *Screen) CancelTimingSwitch() {
	if !s.IsLive {
		return
	}
	cmd := bx.NewCmdCancelTimingSwitch()
	pack := bx.NewBxDataPackCmd(cmd)
	s.send(pack.Pack())
}

func (s *Screen) State() *bx.BxResp {
	if !s.IsLive {
		return nil
	}
	cmd := bx.NewCmdState()
	pack := bx.NewBxDataPackCmd(cmd)
	s.send(pack.Pack())
	r := s.ReadResp()
	return &r
}

func (s *Screen) Param() *bx.BxResp {
	if !s.IsLive {
		return nil
	}
	cmd := bx.NewCmdReadParams()
	pack := bx.NewBxDataPackCmd(cmd)
	s.send(pack.Pack())
	r := s.ReadResp()
	return &r
}

func (s *Screen) Info() {
	s.StateInfo.Print(s.Name)
}

func (s *Screen) ReadResp() bx.BxResp {
	var resp = make([]byte, 1024)
	read, err := s.Conn.Read(resp)
	if err != nil {
		logrus.Error("读数据错误:", err)
		s.IsLive = false
		return bx.BxResp{}
	}
	var bxResp = bx.BxResp{}
	parse := bxResp.Parse(resp, read)
	if parse.IsAck() {
		fmt.Println("response ACK")
		fmt.Printf("原始响应数据:% 0x\n", resp[:read])
		fmt.Println("解析响应数据:", parse)
	} else if parse.IsInfo() {
		fmt.Println("state ACK")
		fmt.Printf("原始响应数据:% 0x\n", resp[:read])
		fmt.Println("解析响应数据:", parse)
	} else {
		fmt.Println("response")
		fmt.Printf("原始响应数据:% 0x\n", resp[:read])
		fmt.Println("解析响应数据:", parse)
	}
	return *parse
}
