package excel

import (
	"errors"
	"fmt"
	"strconv"

	"github.com/xuri/excelize/v2"
)

const (
	StartOfColumn = 3 // start address of every colume

	IndexOfSheetBin   = 0 // sheet index of bin file data in excel
	IndexOfSheetCHL   = 1 // sheet index of CHL bmp file data in excel
	IndexOfSheetRCL   = 2 // sheet index of RCL bmp file data in excel
	IndexOfSheetTheme = 3 // sheet index of theme data in excel, use for combine all bin files

	WidthOfCHLBmpData  = 50 // width of CHL bmp
	HeightOfCHLBmpData = 18 // height of CHL bmp

	WidthOfRCLBmpData  = 37 // width of RCL bmp
	HeightOfRCLBmpData = 7  // width of RCL bmp
)

func OpenFile(str string) (*MExcel, error) {
	var mexcel MExcel
	f, err := excelize.OpenFile(str)
	if err != nil {
		fmt.Println(err)
		return &mexcel, errors.New("no valid info")
	}
	defer func() {
		if err := f.Close(); err != nil {
			fmt.Println(err)
		}
	}()

	// get Bin data
	mexcel.BinData = getBinData(f)

	// get CHL and RCL data
	mexcel.CHLBmpData = getCHLBmpData(f)
	mexcel.RCLBmpData = getRCLBmpData(f)

	// get bin theme data
	mexcel.BinThemeData = getThemeData(f)

	// init channel
	mexcel.ChDone = make(chan bool)
	mexcel.ChOneTaskFinished = make(chan bool)
	mexcel.ChTaskAdd = make(chan uint16)

	return &mexcel, nil
}

func getBinData(f *excelize.File) *CommonALBin {
	// 1.read section data
	sheetBin := f.GetSheetName(IndexOfSheetBin)
	var cnt uint16 = StartOfColumn
	var section *SectionBin = new(SectionBin)
	var BinData CommonALBin
	section.IsType = 0x00
	for {
		cell, _ := f.GetCellValue(sheetBin, getCellIndex("B", cnt))

		// detect change of section, save current data and clear position wait for new data writing
		tmp, _ := strconv.Atoi(cell)
		if section.IsType != uint16(tmp) {
			section.Length = uint16(len(section.PointInfo))
			BinData.Section = append(BinData.Section, section)

			section = new(SectionBin)
			section.IsType = uint16(tmp)
		}

		// finished, break
		if cell == "" {
			break
		}

		// if there is no pointinfo, read maincolor, brightnessDay, brightnessNight data once
		if len(section.PointInfo) == 0 {
			// mainColor
			cell, _ := f.GetCellValue(sheetBin, getCellIndex("K", cnt))
			section.MainColor = cell

			// BrightnessDay
			cell, _ = f.GetCellValue(sheetBin, getCellIndex("L", cnt))
			brightnessDay, _ := strconv.ParseFloat(cell, 32)
			section.BrightnessDay = float32(brightnessDay)

			// BrightnessNight
			cell, _ = f.GetCellValue(sheetBin, getCellIndex("M", cnt))
			BrightnessNight, _ := strconv.ParseFloat(cell, 32)
			section.BrightnessNight = float32(BrightnessNight)
		}

		// read src position and dst position
		var mimgPointInfo MImgPointInfo
		mimgPointInfo.SrcX = getCellUint16(f, sheetBin, getCellIndex("G", cnt))
		mimgPointInfo.SrcY = getCellUint16(f, sheetBin, getCellIndex("H", cnt))
		mimgPointInfo.DstX = getCellUint16(f, sheetBin, getCellIndex("I", cnt))
		mimgPointInfo.DstY = getCellUint16(f, sheetBin, getCellIndex("J", cnt))

		// get BrightnessDay for every point
		cell, _ = f.GetCellValue(sheetBin, getCellIndex("L", cnt))
		brightnessDay, _ := strconv.ParseFloat(cell, 32)
		mimgPointInfo.BrightnessDay = float32(brightnessDay)

		// get BrightnessNight for every point
		cell, _ = f.GetCellValue(sheetBin, getCellIndex("M", cnt))
		BrightnessNight, _ := strconv.ParseFloat(cell, 32)
		mimgPointInfo.BrightnessNight = float32(BrightnessNight)

		section.PointInfo = append(section.PointInfo, &mimgPointInfo)
		cnt = cnt + 1
	}

	// remember the max count of valid data
	cntMax := cnt

	// read subSection，link to father Section
	cnt = StartOfColumn
	LengthStartCnt := cnt // calc length of subsection，it will be the value of new subsection`s count`
	lastSubSectionName := ""
	var offset uint16 = 0           // every subsection offset, will set to 0 after father section change
	var lastFatherIsType uint16 = 0 // the last father IsType
	for {
		if cnt >= cntMax {
			break
		}

		// read all valid data
		cell, _ := f.GetCellValue(sheetBin, getCellIndex("E", cnt))
		if cell != lastSubSectionName {
			if lastSubSectionName != "" {
				// get father section Type, use for link to father section
				fatherIsType := getCellUint16(f, sheetBin, getCellIndex("B", cnt-1))

				// subsection offset will set to 0 after father section changed
				if lastFatherIsType != fatherIsType {
					lastFatherIsType = fatherIsType
					offset = 0
				}

				// if type of father section <= 0, break
				if fatherIsType <= 0 {
					break
				}

				// link data to father section
				var subSectionAttribute SubSectionAttribute
				subSectionAttribute.Length = cnt - LengthStartCnt
				subSectionAttribute.PointInfo = BinData.Section[fatherIsType].PointInfo[offset:(offset + subSectionAttribute.Length)]
				offset += subSectionAttribute.Length
				subSectionAttribute.MainColor, _ = f.GetCellValue(sheetBin, getCellIndex("K", cnt-1))
				BinData.Section[fatherIsType].subSection = append(BinData.Section[fatherIsType].subSection, &subSectionAttribute)
			}

			LengthStartCnt = cnt
			lastSubSectionName = cell
		}

		cnt = cnt + 1
	}
	return &BinData
}

func getCHLBmpData(f *excelize.File) *CommonALBmp {
	var cnt uint16 = StartOfColumn
	var CHLBmpData CommonALBmp
	sheetCHL := f.GetSheetName(IndexOfSheetCHL)
	for {
		cell, _ := f.GetCellValue(sheetCHL, getCellIndex("E", cnt))
		if cell == "" {
			break
		}

		tmp, _ := strconv.Atoi(cell)
		var mimgPointInfo MImgPointInfo
		mimgPointInfo.SrcX = uint16(tmp)
		mimgPointInfo.SrcY = getCellUint16(f, sheetCHL, getCellIndex("F", cnt))
		mimgPointInfo.DstX = getCellUint16(f, sheetCHL, getCellIndex("G", cnt))
		mimgPointInfo.DstY = getCellUint16(f, sheetCHL, getCellIndex("H", cnt))
		CHLBmpData.ImgInfo = append(CHLBmpData.ImgInfo, &mimgPointInfo)
		cnt = cnt + 1
	}
	CHLBmpData.width = WidthOfCHLBmpData
	CHLBmpData.height = HeightOfCHLBmpData

	return &CHLBmpData
}

func getRCLBmpData(f *excelize.File) *CommonALBmp {
	var cnt uint16 = StartOfColumn
	var RCLBmpData CommonALBmp
	sheetRCL := f.GetSheetName(IndexOfSheetRCL)
	for {
		cell, _ := f.GetCellValue(sheetRCL, getCellIndex("E", cnt))
		if cell == "" {
			break
		}

		tmp, _ := strconv.Atoi(cell)
		var mimgPointInfo MImgPointInfo
		mimgPointInfo.SrcX = uint16(tmp)
		mimgPointInfo.SrcY = getCellUint16(f, sheetRCL, getCellIndex("F", cnt))
		mimgPointInfo.DstX = getCellUint16(f, sheetRCL, getCellIndex("G", cnt))
		mimgPointInfo.DstY = getCellUint16(f, sheetRCL, getCellIndex("H", cnt))
		RCLBmpData.ImgInfo = append(RCLBmpData.ImgInfo, &mimgPointInfo)
		cnt = cnt + 1
	}
	RCLBmpData.width = WidthOfRCLBmpData
	RCLBmpData.height = HeightOfRCLBmpData

	return &RCLBmpData
}

func getThemeData(f *excelize.File) []*ThemeInfo {
	sheetTheme := f.GetSheetName(IndexOfSheetTheme)
	var cnt uint16 = StartOfColumn
	var themeinfo *ThemeInfo = new(ThemeInfo)
	themeinfo.ID = 0
	var themeInfoArray []*ThemeInfo
	for {
		cell, _ := f.GetCellValue(sheetTheme, getCellIndex("B", cnt))

		// detect change of section, save current data and create new pointer
		tmp, _ := strconv.Atoi(cell)
		if themeinfo.ID != uint8(tmp) {
			themeinfo.ThemeName, _ = f.GetCellValue(sheetTheme, getCellIndex("C", cnt-1))
			themeInfoArray = append(themeInfoArray, themeinfo)
			themeinfo = new(ThemeInfo)
			themeinfo.ID = uint8(tmp)
		}

		// read all data, break
		if cell == "" {
			break
		}

		// add animation data by mode
		animationName, _ := f.GetCellValue(sheetTheme, getCellIndex("D", cnt))
		indexString, _ := f.GetCellValue(sheetTheme, getCellIndex("F", cnt))
		mode, _ := f.GetCellValue(sheetTheme, getCellIndex("E", cnt))
		if mode == ModeDay || mode == ModeBoth {
			var animation AnimationInfo
			animation.AnimationName = animationName
			animation.IndexString = indexString
			animation.Mode = ModeDay
			themeinfo.Animation = append(themeinfo.Animation, &animation)
		}

		if mode == ModeNight || mode == ModeBoth {
			var animation AnimationInfo
			animation.AnimationName = animationName
			animation.IndexString = indexString
			animation.Mode = ModeNight
			themeinfo.Animation = append(themeinfo.Animation, &animation)
		}

		cnt++
	}
	return themeInfoArray
}

func getCellUint16(f *excelize.File, sheet string, cell string) uint16 {
	str, _ := f.GetCellValue(sheet, cell)
	tmp, _ := strconv.Atoi(str)
	return uint16(tmp)
}

func getCellIndex(str string, num uint16) string {
	return str + strconv.FormatUint(uint64(num), 10)
}

func (d *MExcel) GetCHLWidth() uint16 {
	return d.CHLBmpData.width
}

func (d *MExcel) GetCHLHeight() uint16 {
	return d.CHLBmpData.height
}

func (d *MExcel) GetRCLWidth() uint16 {
	return d.RCLBmpData.width
}

func (d *MExcel) GetRCLHeight() uint16 {
	return d.RCLBmpData.height
}
