package test_data

import (
	"bufio"
	"encoding/json"
	"fmt"
	"github.com/shopspring/decimal"
	"github.com/spf13/viper"
	"github.com/xuri/excelize/v2"
	"log"
	"math"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
	"testData/global"
	"testData/model"
	"testData/request"
	"testData/utils"
	"time"
)

func ExportCPMap(req *request.CPMap) string {
	// CP测试没有子批次
	var fileHandles []*model.FileHandled
	global.PostGreSQL.Where("pbi = ? AND product = ? AND lot = ?", req.PBI, req.Product, req.Lot).
		Order("wafer_id").Find(&fileHandles)
	cpFile := excelize.NewFile()
	passStyle := CPMapPassStyle(cpFile)
	failStyle := CPMapFailStyle(cpFile)

	_ = cpFile.SetSheetName("Sheet1", "Summary")
	allBinMap := make(map[string]int)

	for _, fileHandle := range fileHandles {
		_, err := strconv.Atoi(fileHandle.WaferID)
		if err != nil {
			continue
		}
		f, err := os.Open(fileHandle.Path)
		if err != nil {
			log.Println(err)
		}
		scanner := bufio.NewScanner(f)
		datas := make([][]string, 0)
		fieldMap := make(map[string]int)
		for scanner.Scan() {
			line := scanner.Text()
			s := strings.Split(line, ",")
			datas = append(datas, s[:len(s)-1])
			if len(datas) == 1 {
				for index, cell := range s {
					if index == len(s)-1 {
						continue
					}
					fieldMap[cell] = index
				}
			}
		}
		if len(datas) >= 1 {
			datas = datas[1:]
		}
		for _, data := range datas {
			allBinMap[data[fieldMap["SOFT_BIN"]]] = 1
		}
		f.Close()
	}
	var binStrs []string
	for k := range allBinMap {
		binStrs = append(binStrs, "BIN "+k)
	}
	sort.Slice(binStrs, func(i, j int) bool {
		iInt, _ := strconv.Atoi(binStrs[i][4:])
		jInt, _ := strconv.Atoi(binStrs[j][4:])
		return iInt < jInt
	})

	var title []string
	title = append(title, []string{"产品", "工序", "PBI", "批号", "片号", "Total", "Pass", "Fail", "Yield"}...)
	rowIndex := 1
	title = append(title, binStrs...)
	indexMap := make(map[string]int)
	for index, field := range title {
		indexMap[field] = index + 1
	}
	_ = cpFile.SetSheetRow("Summary", "A1", &title)

	maxProbability, minProbability, maxRowIndex, minRowIndex := "-1", "-1", -1, -1
	for _, fileHandle := range fileHandles {
		_, err := strconv.Atoi(fileHandle.WaferID)
		if err != nil {
			continue
		}
		rowIndex++
		f, err := os.Open(fileHandle.Path)
		if err != nil {
			log.Println(err)
		}
		scanner := bufio.NewScanner(f)
		datas := make([][]string, 0)
		fieldMap := make(map[string]int)
		for scanner.Scan() {
			line := scanner.Text()
			s := strings.Split(line, ",")
			datas = append(datas, s[:len(s)-1])
			if len(datas) == 1 {
				for index, cell := range s {
					if index == len(s)-1 {
						continue
					}
					fieldMap[cell] = index
				}
			}
		}
		if len(datas) >= 1 {
			datas = datas[1:]
		}

		sbinHbinMap := make(map[string]map[string]string)
		sbinHbinByte := []byte(fileHandle.SbinHbin)
		_ = json.Unmarshal(sbinHbinByte, &sbinHbinMap)

		var rowData []interface{}
		var latestDatas [][]string
		endRow, endCol := 0, 0
		binMap := make(map[string]int)

		sheetIndex, _ := cpFile.NewSheet(fileHandle.WaferID)
		sheetName := cpFile.GetSheetName(sheetIndex)

		for _, data := range datas {
			binInfoMap := sbinHbinMap[data[fieldMap["SOFT_BIN"]]]
			x := data[fieldMap["X_COORD"]]
			y := data[fieldMap["Y_COORD"]]

			xInt, _ := strconv.Atoi(x)
			yInt, _ := strconv.Atoi(y)
			if endRow < xInt {
				endRow = xInt
			}
			if endCol < yInt {
				endCol = yInt
			}
			//xDecimal, _ := decimal.NewFromString(x)
			//yDecimal, _ := decimal.NewFromString(y)
			//endRowDecimal, _ := decimal.NewFromString(endRow)
			//endColDecimal, _ := decimal.NewFromString(endCol)
			//if endRowDecimal.LessThan(xDecimal) {
			//	endRow = x
			//}
			//if endColDecimal.LessThan(yDecimal) {
			//	endCol = y
			//}

			isAppendData := true
			for _, mainData := range latestDatas {
				if mainData[0] == x && mainData[1] == y {
					mainData = []string{x, y, data[fieldMap["SOFT_BIN"]], binInfoMap["HBin"]}
					isAppendData = false
					break
				}
			}
			if isAppendData {
				latestDatas = append(latestDatas, []string{x, y,
					data[fieldMap["SOFT_BIN"]], binInfoMap["HBin"]}) //field[5]
			}

		}
		for _, mainData := range latestDatas {
			binMap[mainData[2]]++
			x := utils.NumToRow(mainData[0])
			y := mainData[1]
			if mainData[2] == "1" {
				_ = cpFile.SetCellValue(sheetName, x+y, 1)
				_ = cpFile.SetCellStyle(sheetName, x+y, x+y, passStyle)
			} else {
				_ = cpFile.SetCellValue(sheetName, x+y, mainData[2]+":"+mainData[3])
				_ = cpFile.SetCellStyle(sheetName, x+y, x+y, failStyle)
			}
		}

		_ = cpFile.SetColWidth(sheetName, utils.NumToRow("1"), utils.NumToRow(strconv.Itoa(endRow)), 2)
		//endColIndex, _ := strconv.Atoi(endCol)
		//for i := 0; i < endColIndex; i++ {
		//	_ = cpFile.SetRowHeight(sheetName, i, 10)
		//}
		for i := 0; i < endCol; i++ {
			_ = cpFile.SetRowHeight(sheetName, i, 10)
		}

		totalDecimal := decimal.NewFromFloat(float64(len(latestDatas)))
		passDecimal := decimal.NewFromFloat(float64(binMap["1"]))
		maxProbabilityDecimal, _ := decimal.NewFromString(maxProbability)
		minProbabilityDecimal, _ := decimal.NewFromString(minProbability)
		var passProbability string
		if totalDecimal.IsZero() {
			passProbability = "0"
			minProbability = passProbability
			minRowIndex = rowIndex
			if maxProbability == "-1" {
				maxProbability = passProbability
				maxRowIndex = rowIndex
			}
		} else {
			passProbability = passDecimal.Div(totalDecimal).Round(4).Mul(decimal.NewFromInt(100)).String()
			if maxProbability == "-1" {
				maxProbability = passProbability
				maxRowIndex = rowIndex
			} else {
				if maxProbabilityDecimal.LessThan(passDecimal.Div(totalDecimal).Round(4).Mul(decimal.NewFromInt(100))) {
					maxProbability = passProbability
					maxRowIndex = rowIndex
				}
			}
			if minProbability == "-1" {
				minProbability = passProbability
				minRowIndex = rowIndex
			} else {
				if passDecimal.Div(totalDecimal).Round(4).Mul(decimal.NewFromInt(100)).LessThan(minProbabilityDecimal) {
					minProbability = passProbability
					minRowIndex = rowIndex
				}
			}
		}

		total, _ := strconv.Atoi(totalDecimal.String())
		pass, _ := strconv.Atoi(passDecimal.String())
		fail, _ := strconv.Atoi(totalDecimal.Sub(passDecimal).String())
		rowData = []interface{}{fileHandle.Product, fileHandle.Step, fileHandle.PBI, fileHandle.Lot, fileHandle.WaferID,
			total, pass, fail,
			passProbability + "%"}
		for _, bin := range binStrs {
			if quantity, ok := binMap[bin[4:]]; !ok {
				rowData = append(rowData, "")
			} else {
				rowData = append(rowData, quantity)
			}
		}
		_ = cpFile.SetSheetRow("Summary", "A"+strconv.Itoa(rowIndex), &rowData)
		f.Close()
	}

	// Total
	_ = cpFile.SetSheetCol("Summary", "D"+strconv.Itoa(rowIndex+1), &[]interface{}{"Total", "Max Yield", "Min Yield", "Average Yield"})
	_ = cpFile.SetCellValue("Summary", "E"+strconv.Itoa(rowIndex+1), rowIndex-1)
	_ = cpFile.SetCellFormula("Summary", "F"+strconv.Itoa(rowIndex+1), "=SUM(F2:F"+strconv.Itoa(rowIndex)+")")
	_ = cpFile.SetCellFormula("Summary", "G"+strconv.Itoa(rowIndex+1), "=SUM(G2:G"+strconv.Itoa(rowIndex)+")")
	_ = cpFile.SetCellFormula("Summary", "H"+strconv.Itoa(rowIndex+1), "=SUM(H2:H"+strconv.Itoa(rowIndex)+")")

	// Max Yield
	_ = cpFile.SetCellFormula("Summary", "E"+strconv.Itoa(rowIndex+2), "=E"+strconv.Itoa(maxRowIndex))
	_ = cpFile.SetCellFormula("Summary", "F"+strconv.Itoa(rowIndex+2), "=F"+strconv.Itoa(maxRowIndex))
	_ = cpFile.SetCellFormula("Summary", "G"+strconv.Itoa(rowIndex+2), "=G"+strconv.Itoa(maxRowIndex))
	_ = cpFile.SetCellFormula("Summary", "H"+strconv.Itoa(rowIndex+2), "=H"+strconv.Itoa(maxRowIndex))
	_ = cpFile.SetCellFormula("Summary", "I"+strconv.Itoa(rowIndex+2), "=I"+strconv.Itoa(maxRowIndex))

	// Min Yield
	_ = cpFile.SetCellFormula("Summary", "E"+strconv.Itoa(rowIndex+3), "=E"+strconv.Itoa(minRowIndex))
	_ = cpFile.SetCellFormula("Summary", "F"+strconv.Itoa(rowIndex+3), "=F"+strconv.Itoa(minRowIndex))
	_ = cpFile.SetCellFormula("Summary", "G"+strconv.Itoa(rowIndex+3), "=G"+strconv.Itoa(minRowIndex))
	_ = cpFile.SetCellFormula("Summary", "H"+strconv.Itoa(rowIndex+3), "=H"+strconv.Itoa(minRowIndex))
	_ = cpFile.SetCellFormula("Summary", "I"+strconv.Itoa(rowIndex+3), "=I"+strconv.Itoa(minRowIndex))

	// Average Yield
	percentStyle, _ := cpFile.NewStyle(&excelize.Style{
		NumFmt: 10,
	})
	_ = cpFile.SetCellFormula("Summary", "I"+strconv.Itoa(rowIndex+4), "=G"+strconv.Itoa(rowIndex+1)+"/F"+strconv.Itoa(rowIndex+1))
	_ = cpFile.SetCellStyle("Summary", "F"+strconv.Itoa(rowIndex+4), "F"+strconv.Itoa(rowIndex+4), percentStyle)
	for binIndex, bin := range binStrs {
		colName := utils.NumToRow(strconv.Itoa(indexMap[bin]))
		// Total
		_ = cpFile.SetCellFormula("Summary", colName+strconv.Itoa(rowIndex+1), "=SUM("+colName+"2:"+colName+strconv.Itoa(rowIndex)+")")
		// Max Yield
		_ = cpFile.SetCellFormula("Summary", colName+strconv.Itoa(rowIndex+2), "="+colName+strconv.Itoa(maxRowIndex))
		// Min Yield
		_ = cpFile.SetCellFormula("Summary", colName+strconv.Itoa(rowIndex+3), "="+colName+strconv.Itoa(minRowIndex))
		// Average Yield
		_ = cpFile.SetCellFormula("Summary", colName+strconv.Itoa(rowIndex+4), "="+colName+strconv.Itoa(rowIndex+1)+"/F"+strconv.Itoa(rowIndex+1))

		if binIndex == len(binStrs)-1 {
			_ = cpFile.SetCellStyle("Summary", "I"+strconv.Itoa(rowIndex+4), colName+strconv.Itoa(rowIndex+4), percentStyle)
		}
	}
	_ = cpFile.SetColWidth("Summary", "A", "A", 15)
	_ = cpFile.SetColWidth("Summary", "C", "C", 30)
	_ = cpFile.SetColWidth("Summary", "D", "D", 20)
	filePath := time.Now().Format("_CpMap_2006-01-02_150405.xlsx")
	filePath = req.Product + "_" + req.Lot + filePath
	filePath = filepath.Join(utils.MakeSavePath("CP"), filePath)
	_ = cpFile.SaveAs(filePath)
	defer cpFile.Close()
	return strings.ReplaceAll(viper.GetString("domain"), "\\", "/") + filePath
}

type SelectionInfo struct {
	Field             string
	Min               float64
	Max               float64
	Average           float64
	StandardDeviation float64
	Num               float64
}

func ExportHistogram(req *request.Histogram) string {
	var fileHandle *model.FileHandled
	global.PostGreSQL.Where("product = ? AND lot = ? AND pbi = ?",
		req.Product, req.Lot, req.PBI).Find(&fileHandle)
	histogramFile := excelize.NewFile()
	row := 1
	m := make(map[string]map[string]string)
	_ = json.Unmarshal([]byte(fileHandle.TitleInfo), &m)
	sheetName := histogramFile.GetSheetName(0)
	_ = histogramFile.SetSheetName(sheetName, "汇总表")
	sheetName = "汇总表"
	if ((len(req.SubBatch) == 0 && len(req.WaferID) == 0) || len(req.SubBatch) != 0 || len(req.WaferID) != 0) &&
		!req.SpliteSubBatch {
		start := row
		selectionInfo := make(map[string][]float64)
		var datas [][]string
		fieldMap := make(map[string]int)
		if len(req.SubBatch) == 0 && len(req.WaferID) == 0 {
			datas, fieldMap = LotInMultipleFiles(req.Product, req.Lot, req.PBI)
		} else {
			if len(req.SubBatch) != 0 {
				for _, subBatch := range req.SubBatch {
					subBatchDatas, subBatchFieldMap := LotInAFile(req.Product, req.Lot, req.PBI, subBatch, "")
					datas = append(datas, subBatchDatas...)
					fieldMap = subBatchFieldMap
				}
			} else if len(req.WaferID) != 0 {
				for _, waferID := range req.WaferID {
					waferIDDatas, waferIDFieldMap := LotInAFile(req.Product, req.Lot, req.PBI, "", waferID)
					datas = append(datas, waferIDDatas...)
					fieldMap = waferIDFieldMap
				}
			}
		}

		for _, data := range datas {
			if req.OnlyPass && !(data[fieldMap["SOFT_BIN"]] == "1") {
				continue
			}
			for _, selection := range req.Selections {
				selectionDecimal, _ := decimal.NewFromString(data[fieldMap[selection]])
				selectionFloat, _ := selectionDecimal.Float64()
				var filed string
				if req.SpliteSite {
					filed = selection + "_Site" + data[fieldMap["SITE_NUM"]]
				} else {
					filed = selection
				}
				if _, ok := selectionInfo[filed]; !ok {
					selectionInfo[filed] = []float64{math.MaxInt, math.MinInt, 0, 0}
				}
				if selectionInfo[filed][0] > selectionFloat {
					selectionInfo[filed][0] = selectionFloat
				}
				if selectionInfo[filed][1] < selectionFloat {
					selectionInfo[filed][1] = selectionFloat
				}
				selectionInfo[filed][2] += selectionFloat
				selectionInfo[filed][3]++
			}
		}

		selectionHistogramX := make(map[string][]string)
		selectionHistogramY := make(map[string][]int)
		for k, v := range selectionInfo {
			selectionInfo[k] = []float64{v[0], v[1], v[2], v[3], v[2] / v[3], 0, 0}
			selectionHistogramX[k] = []string{}
			minDecimal := decimal.NewFromFloat(v[0])
			if minDecimal.Abs().LessThan(decimal.NewFromInt(1)) {
				if minDecimal.LessThan(decimal.NewFromInt(0)) {
					minDecimal = minDecimal.RoundUp(0)
				} else {
					minDecimal = minDecimal.RoundDown(0)
				}
			} else {
				if minDecimal.LessThan(decimal.NewFromInt(0)) {
					minDecimal = minDecimal.RoundDown(1)
				} else {
					minDecimal = minDecimal.RoundUp(1)
				}
			}
			maxDecimal := decimal.NewFromFloat(v[1])
			if maxDecimal.Abs().LessThan(decimal.NewFromInt(1)) {
				if maxDecimal.LessThan(decimal.NewFromInt(0)) {
					maxDecimal = maxDecimal.RoundDown(0)
				} else {
					maxDecimal = maxDecimal.RoundUp(0)
				}
			} else {
				if maxDecimal.LessThan(decimal.NewFromInt(0)) {
					maxDecimal = maxDecimal.RoundDown(1)
				} else {
					maxDecimal = maxDecimal.RoundUp(1)
				}
			}
			interval := maxDecimal.Sub(minDecimal).Div(decimal.NewFromInt(20))
			for i := 0; i < 21; i++ {
				xPoint := minDecimal.Add(interval.Mul(decimal.NewFromInt(int64(i)))).String()
				selectionHistogramX[k] = append(selectionHistogramX[k], xPoint)
			}
		}
		for _, data := range datas {
			if req.OnlyPass && !(data[fieldMap["SOFT_BIN"]] == "1") {
				continue
			}
			for k, v := range selectionInfo {
				var field, site string
				if req.SpliteSite {
					field = strings.Split(k, "_Site")[0]
					site = strings.Split(k, "_Site")[1]
					if !(site == data[fieldMap["SITE_NUM"]]) {
						continue
					}
				} else {
					field = k
				}
				if data[fieldMap[field]] != "" {
					selectionDecimal, _ := decimal.NewFromString(data[fieldMap[field]])
					averageDecimal := decimal.NewFromFloat(v[4])
					d, _ := averageDecimal.Sub(selectionDecimal).Pow(decimal.NewFromInt(2)).
						Float64()

					for i := 0; i < len(selectionHistogramX[k]); i++ {
						if i == len(selectionHistogramX[k])-1 {
							xPointDecimal, _ := decimal.NewFromString(selectionHistogramX[k][i])
							if xPointDecimal.Equals(selectionDecimal) {
								selectionHistogramY[k][i]++
							}
							break
						}
						xPointMin, _ := decimal.NewFromString(selectionHistogramX[k][i])
						xPointMax, _ := decimal.NewFromString(selectionHistogramX[k][i+1])
						if _, ok := selectionHistogramY[k]; !ok {
							selectionHistogramY[k] = make([]int, len(selectionHistogramX[k]))
						}
						if xPointMin.LessThanOrEqual(selectionDecimal) && selectionDecimal.LessThan(xPointMax) {
							selectionHistogramY[k][i]++
						}
					}
					v[5] += d
					v[6]++
				}
			}
		}

		rowIndex := 2
		histogramFile.NewSheet("All")
		selectionValueMap := make(map[string][]int)
		for k, v := range selectionHistogramX {
			sheetX := []interface{}{k}
			for _, x := range v {
				sheetX = append(sheetX, x)
			}
			sheetY := []interface{}{"数量"}
			for _, y := range selectionHistogramY[k] {
				sheetY = append(sheetY, y)
			}
			for idx, rows := range [][]interface{}{
				sheetX,
				sheetY,
			} {
				cell, err := excelize.CoordinatesToCellName(1, rowIndex+idx)
				if err != nil {
					fmt.Println(err)
				}
				if err = histogramFile.SetSheetRow("All", cell, &rows); err != nil {
					fmt.Println(err)
				}
			}
			selectionValueMap[k] = []int{rowIndex, len(selectionHistogramY[k]) + 1}
			rowIndex += 2
		}
		a := 1
		for _, selection := range req.Selections {
			b := 1
			for k, v := range selectionValueMap {
				if strings.Contains(k, selection) {
					if err := histogramFile.AddChart("All", utils.NumToRow(strconv.Itoa(b))+strconv.Itoa(a), &excelize.Chart{
						Type: excelize.Col,
						Series: []excelize.ChartSeries{
							{
								Name: "All" + "!$A$1",
								Categories: "All" + "!$B$" + strconv.Itoa(v[0]) + ":$" +
									utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]),
								Values: "All" + "!$B$" + strconv.Itoa(v[0]+1) + ":$" +
									utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]+1),
								Marker: excelize.ChartMarker{
									Symbol: "none", Size: 10,
								},
							},
						},
						Format: excelize.GraphicOptions{
							ScaleX:  1,
							ScaleY:  1,
							OffsetX: 15,
							OffsetY: 10,
						},
						Legend: excelize.ChartLegend{
							Position: "left",
						},
						Title: []excelize.RichTextRun{
							{
								Text: k,
							},
						},
						PlotArea: excelize.ChartPlotArea{
							ShowCatName:     false,
							ShowLeaderLines: false,
							ShowVal:         true,
						},
						ShowBlanksAs: "zero",
					}, &excelize.Chart{
						Type: excelize.Line,
						Series: []excelize.ChartSeries{
							{
								Name: "All" + "!$A$1",
								Categories: "All" + "!$B$" + strconv.Itoa(v[0]) + ":$" +
									utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]),
								Values: "All" + "!$B$" + strconv.Itoa(v[0]+1) + ":$" +
									utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]+1),
								Marker: excelize.ChartMarker{
									Symbol: "none", Size: 10,
								},
							},
						},
						Format: excelize.GraphicOptions{
							ScaleX:  1,
							ScaleY:  1,
							OffsetX: 15,
							OffsetY: 10,
						},
						Legend: excelize.ChartLegend{
							Position: "right",
						},
						PlotArea: excelize.ChartPlotArea{
							ShowCatName:     false,
							ShowLeaderLines: false,
						},
					}); err != nil {
						fmt.Println(err)
					}
					b += 9
				}
			}
			a += 20
		}

		//ftFile.NewSheet("ALL")
		//for k, v := range selectionHistogramX {
		//	sheetX := []interface{}{""}
		//	for _, x := range v {
		//		sheetX = append(sheetX, x)
		//	}
		//	sheetY := []interface{}{"数量"}
		//	for _, y := range selectionHistogramY[k] {
		//		sheetY = append(sheetY, y)
		//	}
		//	for idx, rows := range [][]interface{}{
		//		sheetX,
		//		sheetY,
		//	} {
		//		cell, err := excelize.CoordinatesToCellName(1, idx+1)
		//		if err != nil {
		//			fmt.Println(err)
		//		}
		//		if err = ftFile.SetSheetRow(k, cell, &rows); err != nil {
		//			fmt.Println(err)
		//		}
		//	}
		//	if err := ftFile.AddChart(k, "A4", &excelize.Chart{
		//		Type: excelize.Col,
		//		Series: []excelize.ChartSeries{
		//			{
		//				Name:       k + "!$A$1",
		//				Categories: k + "!$B$1:$" + utils.NumToRow(strconv.Itoa(len(selectionHistogramY[k])+1)) + "$1",
		//				Values:     k + "!$B$2:$" + utils.NumToRow(strconv.Itoa(len(selectionHistogramY[k])+1)) + "$2",
		//			},
		//		},
		//		Format: excelize.GraphicOptions{
		//			ScaleX:  1,
		//			ScaleY:  1,
		//			OffsetX: 15,
		//			OffsetY: 10,
		//		},
		//		Legend: excelize.ChartLegend{
		//			Position: "left",
		//		},
		//		Title: []excelize.RichTextRun{
		//			{
		//				Text: k,
		//			},
		//		},
		//		PlotArea: excelize.ChartPlotArea{
		//			ShowCatName:     false,
		//			ShowLeaderLines: false,
		//			ShowVal:         true,
		//		},
		//		ShowBlanksAs: "zero",
		//	}, &excelize.Chart{
		//		Type: excelize.Line,
		//		Series: []excelize.ChartSeries{
		//			{
		//				Name:       k + "!$A$1",
		//				Categories: k + "!$B$1:$" + utils.NumToRow(strconv.Itoa(len(selectionHistogramY[k])+1)) + "$1",
		//				Values:     k + "!$B$2:$" + utils.NumToRow(strconv.Itoa(len(selectionHistogramY[k])+1)) + "$2",
		//				Marker: excelize.ChartMarker{
		//					Symbol: "none", Size: 10,
		//				},
		//			},
		//		},
		//		Format: excelize.GraphicOptions{
		//			ScaleX:  1,
		//			ScaleY:  1,
		//			OffsetX: 15,
		//			OffsetY: 10,
		//		},
		//		Legend: excelize.ChartLegend{
		//			Position: "right",
		//		},
		//		PlotArea: excelize.ChartPlotArea{
		//			ShowCatName:     false,
		//			ShowLeaderLines: false,
		//		},
		//	}); err != nil {
		//		fmt.Println(err)
		//	}
		//}
		col := 1
		_ = histogramFile.SetSheetCol(sheetName, "A"+strconv.Itoa(start), &[]interface{}{
			"", "最小值", "最大值", "平均值", "δ", "数量", "Limit L", "Limit H", "UNIT"})
		col++
		siteDiff := make(map[string][]float64)
		selectionUnit := make(map[string]string)

		var selectionInfoArray []SelectionInfo
		for _, selection := range req.Selections {
			var oneSelectionInfo []SelectionInfo
			for k, v := range selectionInfo {
				if !strings.Contains(k, selection) {
					continue
				}
				oneSelectionInfo = append(oneSelectionInfo, SelectionInfo{
					Field:             k,
					Min:               v[0],
					Max:               v[1],
					Average:           v[4],
					StandardDeviation: math.Sqrt(v[5] / v[6]),
					Num:               v[3],
				})
			}
			sort.Slice(oneSelectionInfo, func(i, j int) bool {
				return oneSelectionInfo[i].Field < oneSelectionInfo[j].Field
			})
			selectionInfoArray = append(selectionInfoArray, oneSelectionInfo...)
		}

		for _, v := range selectionInfoArray {
			field := strings.Split(v.Field, "_Site")[0]
			limitLDecimal, _ := decimal.NewFromString(m[field]["LimitL"])
			limitLFloat, _ := limitLDecimal.Float64()
			limitUDecimal, _ := decimal.NewFromString(m[field]["LimitU"])
			limitUFloat, _ := limitUDecimal.Float64()
			if req.SpliteSite {
				if _, ok := siteDiff[field]; !ok {
					siteDiff[field] = []float64{v.Average, v.Average, v.StandardDeviation, v.StandardDeviation}
					selectionUnit[field] = m[field]["Unit"]
				} else {
					if v.Average > siteDiff[field][0] {
						siteDiff[field][0] = v.Average
					}
					if v.Average < siteDiff[field][1] {
						siteDiff[field][1] = v.Average
					}
					if v.StandardDeviation > siteDiff[field][2] {
						siteDiff[field][2] = v.StandardDeviation
					}
					if v.StandardDeviation < siteDiff[field][3] {
						siteDiff[field][3] = v.StandardDeviation
					}
				}
			}
			_ = histogramFile.SetSheetCol(sheetName, utils.NumToRow(strconv.Itoa(col))+strconv.Itoa(start), &[]interface{}{
				v.Field, v.Min, v.Max, v.Average, v.StandardDeviation, v.Num, limitLFloat, limitUFloat, m[field]["Unit"]})
			col++
		}

		_ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start),
			TitleStyle(histogramFile))
		_ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+1), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+5),
			ActualLimitStyle(histogramFile))
		_ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+6), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+8),
			LimitStyle(histogramFile))
		_ = histogramFile.SetColWidth(sheetName, "A", utils.NumToRow(strconv.Itoa(col-1)),
			20)

		if req.SpliteSite {
			start += 10
			col = 2
			_ = histogramFile.SetSheetCol(sheetName, "A"+strconv.Itoa(start), &[]interface{}{
				"SITE差异"})
			_ = histogramFile.SetSheetCol(sheetName, "A"+strconv.Itoa(start+1), &[]interface{}{
				"", "平均值", "δ", "UNIT"})
			for k, v := range siteDiff {
				_ = histogramFile.SetSheetCol(sheetName, utils.NumToRow(strconv.Itoa(col))+strconv.Itoa(start+1), &[]interface{}{
					k, v[0] - v[1], v[2] - v[3], selectionUnit[k]})
				col++
			}
			_ = histogramFile.MergeCell(sheetName, "A"+strconv.Itoa(start), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start))
			_ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start),
				NormalSheetStyle(histogramFile))
			_ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+1), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+1),
				TitleStyle(histogramFile))
			_ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+2), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+3),
				ActualLimitStyle(histogramFile))
			_ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+4), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+4),
				LimitStyle(histogramFile))
		}
	} else {
		start := row
		selectionUnit := make(map[string]string)
		subBatchDiff := make(map[string][]float64)

		var groups []string
		var isFT bool
		if len(req.SubBatch) != 0 {
			groups = req.SubBatch
			isFT = true
		} else if len(req.WaferID) != 0 {
			groups = req.WaferID
		} else if len(req.SubBatch) == 0 && len(req.WaferID) == 0 {
			global.PostGreSQL.Model(&model.FileHandled{}).Where("product = ? AND lot = ? AND pbi = ?",
				req.Product, req.Lot, req.PBI).Group("wafer_id").Select("wafer_id").Find(&groups)
			if len(groups) == 0 || groups[0] == "" {
				global.PostGreSQL.Model(&model.FileHandled{}).Where("product = ? AND lot = ? AND pbi = ?",
					req.Product, req.Lot, req.PBI).Group("sub_batch").Select("sub_batch").Find(&groups)
				isFT = true
			}
		}
		for _, group := range groups {
			siteSelectionInfo := make(map[string][]float64)
			selectionInfo := make(map[string][]float64)
			var datas [][]string
			fieldMap := make(map[string]int)
			if isFT {
				datas, fieldMap = LotInAFile(req.Product, req.Lot, req.PBI, group, "")
			} else {
				datas, fieldMap = LotInAFile(req.Product, req.Lot, req.PBI, "", group)
			}
			group = strings.ReplaceAll(group, "-", "_")

			for _, data := range datas {
				if req.OnlyPass && !(data[fieldMap["SOFT_BIN"]] == "1") {
					continue
				}
				for _, selection := range req.Selections {
					selectionDecimal, _ := decimal.NewFromString(data[fieldMap[selection]])
					selectionFloat, _ := selectionDecimal.Float64()
					var filed string
					if req.SpliteSite {
						filed = selection + "_Site" + data[fieldMap["SITE_NUM"]]
					} else {
						filed = selection
					}
					if _, ok := siteSelectionInfo[filed]; !ok {
						siteSelectionInfo[filed] = []float64{math.MaxInt, math.MinInt, 0, 0}
					}
					if siteSelectionInfo[filed][0] > selectionFloat {
						siteSelectionInfo[filed][0] = selectionFloat
					}
					if siteSelectionInfo[filed][1] < selectionFloat {
						siteSelectionInfo[filed][1] = selectionFloat
					}
					siteSelectionInfo[filed][2] += selectionFloat
					siteSelectionInfo[filed][3]++

					if _, ok := selectionInfo[selection]; !ok {
						selectionInfo[selection] = []float64{math.MaxInt, math.MinInt, 0, 0}
					}
					if selectionInfo[selection][0] > selectionFloat {
						selectionInfo[selection][0] = selectionFloat
					}
					if selectionInfo[selection][1] < selectionFloat {
						selectionInfo[selection][1] = selectionFloat
					}
					selectionInfo[selection][2] += selectionFloat
					selectionInfo[selection][3]++
				}
			}

			selectionHistogramX := make(map[string][]string)
			selectionHistogramY := make(map[string][]int)
			for k, v := range siteSelectionInfo {
				siteSelectionInfo[k] = []float64{v[0], v[1], v[2], v[3], v[2] / v[3], 0, 0}
				if req.SpliteSite {
					selectionHistogramX[group+"_"+k] = []string{}
					minDecimal := decimal.NewFromFloat(v[0])
					if minDecimal.LessThan(decimal.NewFromInt(0)) {
						minDecimal = minDecimal.RoundUp(0)
					} else {
						minDecimal = minDecimal.RoundDown(0)
					}
					maxDecimal := decimal.NewFromFloat(v[1])
					if maxDecimal.LessThan(decimal.NewFromInt(0)) {
						maxDecimal = maxDecimal.RoundDown(0)
					} else {
						maxDecimal = maxDecimal.RoundUp(0)
					}
					interval := maxDecimal.Sub(minDecimal).Div(decimal.NewFromInt(20))
					for i := 0; i < 21; i++ {
						xPoint := minDecimal.Add(interval.Mul(decimal.NewFromInt(int64(i)))).String()
						selectionHistogramX[group+"_"+k] = append(selectionHistogramX[group+"_"+k], xPoint)
					}
				}
			}
			for k, v := range selectionInfo {
				selectionInfo[k] = []float64{v[0], v[1], v[2], v[3], v[2] / v[3], 0, 0}
				if !req.SpliteSite {
					selectionHistogramX[group+"_"+k] = []string{}
					minDecimal := decimal.NewFromFloat(v[0])
					if minDecimal.LessThan(decimal.NewFromInt(0)) {
						minDecimal = minDecimal.RoundUp(0)
					} else {
						minDecimal = minDecimal.RoundDown(0)
					}
					maxDecimal := decimal.NewFromFloat(v[1])
					if maxDecimal.LessThan(decimal.NewFromInt(0)) {
						maxDecimal = maxDecimal.RoundDown(0)
					} else {
						maxDecimal = maxDecimal.RoundUp(0)
					}

					interval := maxDecimal.Sub(minDecimal).Div(decimal.NewFromInt(20))
					for i := 0; i < 21; i++ {
						xPoint := minDecimal.Add(interval.Mul(decimal.NewFromInt(int64(i)))).String()
						selectionHistogramX[group+"_"+k] = append(selectionHistogramX[group+"_"+k], xPoint)
					}
				}
			}

			for _, data := range datas {
				if req.OnlyPass && !(data[fieldMap["SOFT_BIN"]] == "1") {
					continue
				}
				for k, v := range siteSelectionInfo {
					var field, site string
					if req.SpliteSite {
						field = strings.Split(k, "_Site")[0]
						site = strings.Split(k, "_Site")[1]
						if !(site == data[fieldMap["SITE_NUM"]]) {
							continue
						}
					} else {
						field = k
					}
					if data[fieldMap[field]] != "" {
						selectionDecimal, _ := decimal.NewFromString(data[fieldMap[field]])
						averageDecimal := decimal.NewFromFloat(v[4])
						d, _ := averageDecimal.Sub(selectionDecimal).Pow(decimal.NewFromInt(2)).
							Float64()

						if req.SpliteSite {
							a := group + "_" + k
							for i := 0; i < len(selectionHistogramX[a]); i++ {
								if i == len(selectionHistogramX[a])-1 {
									xPointDecimal, _ := decimal.NewFromString(selectionHistogramX[a][i])
									if xPointDecimal.Equals(selectionDecimal) {
										selectionHistogramY[a][i]++
									}
									break
								}
								xPointMin, _ := decimal.NewFromString(selectionHistogramX[a][i])
								xPointMax, _ := decimal.NewFromString(selectionHistogramX[a][i+1])
								if _, ok := selectionHistogramY[a]; !ok {
									selectionHistogramY[a] = make([]int, len(selectionHistogramX[a]))
								}
								if xPointMin.LessThanOrEqual(selectionDecimal) && selectionDecimal.LessThan(xPointMax) {
									selectionHistogramY[a][i]++
								}
							}
						}

						v[5] += d
						v[6]++
					}
				}

				for k, v := range selectionInfo {
					if data[fieldMap[k]] != "" {
						selectionDecimal, _ := decimal.NewFromString(data[fieldMap[k]])
						averageDecimal := decimal.NewFromFloat(v[4])
						d, _ := averageDecimal.Sub(selectionDecimal).Pow(decimal.NewFromInt(2)).
							Float64()

						if !req.SpliteSite {
							field := group + "_" + k
							for i := 0; i < len(selectionHistogramX[field]); i++ {
								if i == len(selectionHistogramX[field])-1 {
									xPointDecimal, _ := decimal.NewFromString(selectionHistogramX[field][i])
									if xPointDecimal.Equals(selectionDecimal) {
										selectionHistogramY[field][i]++
									}
									break
								}
								xPointMin, _ := decimal.NewFromString(selectionHistogramX[field][i])
								xPointMax, _ := decimal.NewFromString(selectionHistogramX[field][i+1])
								if _, ok := selectionHistogramY[field]; !ok {
									selectionHistogramY[field] = make([]int, len(selectionHistogramX[field]))
								}
								if xPointMin.LessThanOrEqual(selectionDecimal) && selectionDecimal.LessThan(xPointMax) {
									selectionHistogramY[field][i]++
								}
							}
						}

						v[5] += d
						v[6]++
					}
				}

			}

			rowIndex, colIndex := 2, 1
			histogramFile.NewSheet(group)
			selectionValueMap := make(map[string][]int)
			for k, v := range selectionHistogramX {
				sheetX := []interface{}{k}
				for _, x := range v {
					sheetX = append(sheetX, x)
				}
				sheetY := []interface{}{"数量"}
				for _, y := range selectionHistogramY[k] {
					sheetY = append(sheetY, y)
				}
				for idx, rows := range [][]interface{}{
					sheetX,
					sheetY,
				} {
					cell, err := excelize.CoordinatesToCellName(colIndex, rowIndex+idx)
					if err != nil {
						fmt.Println(err)
					}
					if err = histogramFile.SetSheetRow(group, cell, &rows); err != nil {
						fmt.Println(err)
					}
				}
				selectionValueMap[k] = []int{rowIndex, len(selectionHistogramY[k]) + 1}
				rowIndex += 2
			}
			a := 1
			for _, selection := range req.Selections {
				b := 1
				for k, v := range selectionValueMap {
					if strings.Contains(k, selection) {
						if err := histogramFile.AddChart(group, utils.NumToRow(strconv.Itoa(b))+strconv.Itoa(a), &excelize.Chart{
							Type: excelize.Col,
							Series: []excelize.ChartSeries{
								{
									Name: group + "!$A$1",
									Categories: group + "!$B$" + strconv.Itoa(v[0]) + ":$" +
										utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]),
									Values: group + "!$B$" + strconv.Itoa(v[0]+1) + ":$" +
										utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]+1),
									Marker: excelize.ChartMarker{
										Symbol: "none", Size: 10,
									},
								},
							},
							Format: excelize.GraphicOptions{
								ScaleX:  1,
								ScaleY:  1,
								OffsetX: 15,
								OffsetY: 10,
							},
							Legend: excelize.ChartLegend{
								Position: "left",
							},
							Title: []excelize.RichTextRun{
								{
									Text: k,
								},
							},
							PlotArea: excelize.ChartPlotArea{
								ShowCatName:     false,
								ShowLeaderLines: false,
								ShowVal:         true,
							},
							ShowBlanksAs: "zero",
						}, &excelize.Chart{
							Type: excelize.Line,
							Series: []excelize.ChartSeries{
								{
									Name: group + "!$A$1",
									Categories: group + "!$B$" + strconv.Itoa(v[0]) + ":$" +
										utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]),
									Values: group + "!$B$" + strconv.Itoa(v[0]+1) + ":$" +
										utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]+1),
									Marker: excelize.ChartMarker{
										Symbol: "none", Size: 10,
									},
								},
							},
							Format: excelize.GraphicOptions{
								ScaleX:  1,
								ScaleY:  1,
								OffsetX: 15,
								OffsetY: 10,
							},
							Legend: excelize.ChartLegend{
								Position: "right",
							},
							PlotArea: excelize.ChartPlotArea{
								ShowCatName:     false,
								ShowLeaderLines: false,
							},
						}); err != nil {
							fmt.Println(err)
						}
						b += 9
					}
				}
				a += 20
			}

			col := 1
			_ = histogramFile.SetSheetCol(sheetName, "A"+strconv.Itoa(start), &[]interface{}{
				"子批:" + group})

			_ = histogramFile.SetSheetCol(sheetName, "A"+strconv.Itoa(start+1), &[]interface{}{
				"", "最小值", "最大值", "平均值", "δ", "数量", "Limit L", "Limit H", "UNIT"})
			col++
			siteDiff := make(map[string][]float64)

			var selectionInfoArray []SelectionInfo
			for _, selection := range req.Selections {
				var oneSelectionInfo []SelectionInfo
				for k, v := range siteSelectionInfo {
					if !strings.Contains(k, selection) {
						continue
					}
					oneSelectionInfo = append(oneSelectionInfo, SelectionInfo{
						Field:             k,
						Min:               v[0],
						Max:               v[1],
						Average:           v[4],
						StandardDeviation: math.Sqrt(v[5] / v[6]),
						Num:               v[3],
					})
				}
				sort.Slice(oneSelectionInfo, func(i, j int) bool {
					return oneSelectionInfo[i].Field < oneSelectionInfo[j].Field
				})
				selectionInfoArray = append(selectionInfoArray, oneSelectionInfo...)
			}

			for _, v := range selectionInfoArray {
				field := strings.Split(v.Field, "_Site")[0]
				limitLDecimal, _ := decimal.NewFromString(m[field]["LimitL"])
				limitLFloat, _ := limitLDecimal.Float64()
				limitUDecimal, _ := decimal.NewFromString(m[field]["LimitU"])
				limitUFloat, _ := limitUDecimal.Float64()
				selectionUnit[field] = m[field]["Unit"]
				if req.SpliteSite {
					if _, ok := siteDiff[field]; !ok {
						siteDiff[field] = []float64{v.Average, v.Average, v.StandardDeviation, v.StandardDeviation}
					} else {
						if v.Average > siteDiff[field][0] {
							siteDiff[field][0] = v.Average
						}
						if v.Average < siteDiff[field][1] {
							siteDiff[field][1] = v.Average
						}
						if v.StandardDeviation > siteDiff[field][2] {
							siteDiff[field][2] = v.StandardDeviation
						}
						if v.StandardDeviation < siteDiff[field][3] {
							siteDiff[field][3] = v.StandardDeviation
						}
					}
				}
				_ = histogramFile.SetSheetCol(sheetName, utils.NumToRow(strconv.Itoa(col))+strconv.Itoa(start+1), &[]interface{}{
					v.Field, v.Min, v.Max, v.Average, v.StandardDeviation, v.Num, limitLFloat, limitUFloat, m[field]["Unit"]})
				col++
			}

			for k, v := range selectionInfo {
				if _, ok := subBatchDiff[k]; !ok {
					subBatchDiff[k] = []float64{v[4], v[4], v[5] / v[6], v[5] / v[6]}
					selectionUnit[k] = m[k]["Unit"]
				} else {
					if v[4] > subBatchDiff[k][0] {
						subBatchDiff[k][0] = v[4]
					}
					if v[4] < subBatchDiff[k][1] {
						subBatchDiff[k][1] = v[4]
					}
					if v[5]/v[6] > subBatchDiff[k][2] {
						subBatchDiff[k][2] = v[5] / v[6]
					}
					if v[5]/v[6] < subBatchDiff[k][3] {
						subBatchDiff[k][3] = v[5] / v[6]
					}
				}
			}

			_ = histogramFile.MergeCell(sheetName, "A"+strconv.Itoa(start), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start))
			_ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start),
				NormalSheetStyle(histogramFile))
			_ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+1), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+1),
				TitleStyle(histogramFile))
			_ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+2), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+6),
				ActualLimitStyle(histogramFile))
			_ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+7), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+9),
				LimitStyle(histogramFile))
			_ = histogramFile.SetColWidth(sheetName, "A", utils.NumToRow(strconv.Itoa(col-1)),
				20)
			start += 10

			if req.SpliteSite {
				start += 1
				col = 2
				_ = histogramFile.SetSheetCol(sheetName, "A"+strconv.Itoa(start), &[]interface{}{
					"SITE差异-" + group})
				_ = histogramFile.SetSheetCol(sheetName, "A"+strconv.Itoa(start+1), &[]interface{}{
					"", "平均值", "δ", "UNIT"})
				for k, v := range siteDiff {
					_ = histogramFile.SetSheetCol(sheetName, utils.NumToRow(strconv.Itoa(col))+strconv.Itoa(start+1), &[]interface{}{
						k, v[0] - v[1], v[2] - v[3], selectionUnit[k]})
					col++
				}
				_ = histogramFile.MergeCell(sheetName, "A"+strconv.Itoa(start), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start))
				_ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start),
					NormalSheetStyle(histogramFile))
				_ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+1), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+1),
					TitleStyle(histogramFile))
				_ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+2), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+3),
					ActualLimitStyle(histogramFile))
				_ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+4), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+4),
					LimitStyle(histogramFile))
				start += 5
			}
			start += 1
		}

		start += 1
		col := 2
		_ = histogramFile.SetSheetCol(sheetName, "A"+strconv.Itoa(start), &[]interface{}{
			"批次差异汇总"})
		_ = histogramFile.SetSheetCol(sheetName, "A"+strconv.Itoa(start+1), &[]interface{}{
			"", "平均值", "δ", "UNIT"})
		for _, selection := range req.Selections {
			_ = histogramFile.SetSheetCol(sheetName, utils.NumToRow(strconv.Itoa(col))+strconv.Itoa(start+1), &[]interface{}{
				selection, subBatchDiff[selection][0] - subBatchDiff[selection][1],
				subBatchDiff[selection][2] - subBatchDiff[selection][3], selectionUnit[selection]})
			col++
		}
		_ = histogramFile.MergeCell(sheetName, "A"+strconv.Itoa(start), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start))
		_ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start),
			NormalSheetStyle(histogramFile))
		_ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+1), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+1),
			TitleStyle(histogramFile))
		_ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+2), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+3),
			ActualLimitStyle(histogramFile))
		_ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+4), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+4),
			LimitStyle(histogramFile))
	}
	filePath := time.Now().Format("_直方图_2006-01-02_150405.xlsx")
	filePath = req.Product + "_" + req.Lot + filePath
	filePath = filepath.Join(utils.MakeSavePath("直方图"), filePath)
	_ = histogramFile.SaveAs(filePath)
	defer histogramFile.Close()
	return strings.ReplaceAll(viper.GetString("domain"), "\\", "/") + filePath
}

func ExportScatter(req *request.Scatter) string {
	scatterFile := excelize.NewFile()
	//((len(req.SubBatch) == 0 && len(req.WaferID) == 0) || len(req.SubBatch) != 0 || len(req.WaferID) != 0) &&
	if !req.SpliteSubBatch {
		var datas [][]string
		fieldMap := make(map[string]int)
		if len(req.SubBatch) == 0 && len(req.WaferID) == 0 {
			datas, fieldMap = LotInMultipleFiles(req.Product, req.Lot, req.PBI)
		} else {
			if len(req.SubBatch) != 0 {
				for _, subBatch := range req.SubBatch {
					subBatchDatas, subBatchFieldMap := LotInAFile(req.Product, req.Lot, req.PBI, subBatch, "")
					datas = append(datas, subBatchDatas...)
					fieldMap = subBatchFieldMap
				}
			} else if len(req.WaferID) != 0 {
				for _, waferID := range req.WaferID {
					waferIDDatas, waferIDFieldMap := LotInAFile(req.Product, req.Lot, req.PBI, "", waferID)
					datas = append(datas, waferIDDatas...)
					fieldMap = waferIDFieldMap
				}
			}
		}

		selectionPointsMap := make(map[string]map[string]int)
		for _, data := range datas {
			if req.OnlyPass {
				if data[fieldMap["SOFT_BIN"]] != "1" {
					continue
				}
			}
			for _, selection := range req.XYSelection {
				var field string
				if req.SpliteSite {
					field = selection.X + "," + selection.Y + "_SITE" + data[fieldMap["SITE_NUM"]]
				} else {
					field = selection.X + "," + selection.Y
				}
				if _, ok := selectionPointsMap[field]; !ok {
					selectionPointsMap[field] = map[string]int{}
				}
				selectionPointsMap[field][data[fieldMap[selection.X]]+","+data[fieldMap[selection.Y]]]++
			}
		}

		scatterFile.SetSheetName("Sheet1", "All")
		sheetName := scatterFile.GetSheetName(0)
		selectionValueMap := make(map[string][]int)

		rowIndex := 1
		for _, selection := range req.XYSelection {
			for k := range selectionPointsMap {
				if strings.Contains(k, selection.X+","+selection.Y) {
					sheetX := []interface{}{k, selection.X}
					sheetY := []interface{}{"", selection.Y}
					for point := range selectionPointsMap[k] {
						pointXY := strings.Split(point, ",")
						xDecimal, _ := decimal.NewFromString(pointXY[0])
						x, _ := xDecimal.Float64()
						yDecimal, _ := decimal.NewFromString(pointXY[1])
						y, _ := yDecimal.Float64()
						sheetX = append(sheetX, x)
						sheetY = append(sheetY, y)
					}
					for idx, row := range [][]interface{}{
						sheetX,
						sheetY,
					} {
						cell, err := excelize.CoordinatesToCellName(1, rowIndex+idx)
						if err != nil {
							fmt.Println(err)
						}
						if err = scatterFile.SetSheetRow(sheetName, cell, &row); err != nil {
							fmt.Println(err)
						}
					}
					selectionValueMap[k] = []int{rowIndex, len(sheetY) + 1}
					rowIndex += 2
				}
			}
		}
		a := 1
		for _, selection := range req.XYSelection {
			b := 1
			for k, v := range selectionValueMap {
				if strings.Contains(k, selection.X+","+selection.Y) {
					if err := scatterFile.AddChart("All", utils.NumToRow(strconv.Itoa(b))+strconv.Itoa(a), &excelize.Chart{
						Type: excelize.Scatter,
						Series: []excelize.ChartSeries{
							{
								Name: "All" + "!$A$1",
								Categories: "All" + "!$C$" + strconv.Itoa(v[0]) + ":$" +
									utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]),
								Values: "All" + "!$C$" + strconv.Itoa(v[0]+1) + ":$" +
									utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]+1),
								Marker: excelize.ChartMarker{
									Symbol: "circle", Size: 1,
								},
							},
						},
						Format: excelize.GraphicOptions{
							ScaleX:  1,
							ScaleY:  1,
							OffsetX: 15,
							OffsetY: 10,
						},
						Legend: excelize.ChartLegend{
							Position: "bottom",
						},
						Title: []excelize.RichTextRun{
							{
								Text: k,
							},
						},
						//PlotArea: excelize.ChartPlotArea{
						//	ShowCatName:     false,
						//	ShowLeaderLines: false,
						//},
						ShowBlanksAs: "zero",
					}); err != nil {
						fmt.Println(err)
					}
					b += 9
				}
			}
			a += 20
		}
	} else {
		var groups []string
		var isFT bool
		if len(req.SubBatch) != 0 {
			groups = req.SubBatch
			isFT = true
		} else if len(req.WaferID) != 0 {
			groups = req.WaferID
		} else if len(req.SubBatch) == 0 && len(req.WaferID) == 0 {
			global.PostGreSQL.Model(&model.FileHandled{}).Where("product = ? AND lot = ? AND pbi = ?",
				req.Product, req.Lot, req.PBI).Group("wafer_id").Select("wafer_id").Find(&groups)
			if len(groups) == 0 || groups[0] == "" {
				global.PostGreSQL.Model(&model.FileHandled{}).Where("product = ? AND lot = ? AND pbi = ?",
					req.Product, req.Lot, req.PBI).Group("sub_batch").Select("sub_batch").Find(&groups)
				isFT = true
			}
		}
		for _, group := range groups {
			var datas [][]string
			fieldMap := make(map[string]int)
			if isFT {
				datas, fieldMap = LotInAFile(req.Product, req.Lot, req.PBI, group, "")
			} else {
				datas, fieldMap = LotInAFile(req.Product, req.Lot, req.PBI, "", group)
			}
			group = strings.ReplaceAll(group, "-", "_")
			selectionPointsMap := make(map[string]map[string]int)
			for _, data := range datas {
				if req.OnlyPass {
					if data[fieldMap["SOFT_BIN"]] != "1" {
						continue
					}
				}
				for _, selection := range req.XYSelection {
					var field string
					if req.SpliteSite {
						field = selection.X + "," + selection.Y + "_SITE" + data[fieldMap["SITE_NUM"]]
					} else {
						field = selection.X + "," + selection.Y
					}
					if _, ok := selectionPointsMap[field]; !ok {
						selectionPointsMap[field] = map[string]int{}
					}
					selectionPointsMap[field][data[fieldMap[selection.X]]+","+data[fieldMap[selection.Y]]]++
				}
			}

			scatterFile.NewSheet(group)
			selectionValueMap := make(map[string][]int)

			rowIndex := 1
			for _, selection := range req.XYSelection {
				for k := range selectionPointsMap {
					if strings.Contains(k, selection.X+","+selection.Y) {
						sheetX := []interface{}{k, selection.X}
						sheetY := []interface{}{"", selection.Y}
						for point := range selectionPointsMap[k] {
							pointXY := strings.Split(point, ",")
							xDecimal, _ := decimal.NewFromString(pointXY[0])
							x, _ := xDecimal.Float64()
							yDecimal, _ := decimal.NewFromString(pointXY[1])
							y, _ := yDecimal.Float64()
							sheetX = append(sheetX, x)
							sheetY = append(sheetY, y)
						}
						for idx, row := range [][]interface{}{
							sheetX,
							sheetY,
						} {
							cell, err := excelize.CoordinatesToCellName(1, rowIndex+idx)
							if err != nil {
								fmt.Println(err)
							}
							if err = scatterFile.SetSheetRow(group, cell, &row); err != nil {
								fmt.Println(err)
							}
						}
						selectionValueMap[k] = []int{rowIndex, len(sheetY) + 1}
						rowIndex += 2
					}
				}
			}
			a := 1
			for _, selection := range req.XYSelection {
				b := 1
				for k, v := range selectionValueMap {
					if strings.Contains(k, selection.X+","+selection.Y) {
						if err := scatterFile.AddChart(group, utils.NumToRow(strconv.Itoa(b))+strconv.Itoa(a), &excelize.Chart{
							Type: excelize.Scatter,
							Series: []excelize.ChartSeries{
								{
									Name: group + "!$A$1",
									Categories: group + "!$C$" + strconv.Itoa(v[0]) + ":$" +
										utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]),
									Values: group + "!$C$" + strconv.Itoa(v[0]+1) + ":$" +
										utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]+1),
									Marker: excelize.ChartMarker{
										Symbol: "circle", Size: 1,
									},
								},
							},
							Format: excelize.GraphicOptions{
								ScaleX:  1,
								ScaleY:  1,
								OffsetX: 15,
								OffsetY: 10,
							},
							Legend: excelize.ChartLegend{
								Position: "bottom",
							},
							Title: []excelize.RichTextRun{
								{
									Text: k,
								},
							},
							//PlotArea: excelize.ChartPlotArea{
							//	ShowCatName:     false,
							//	ShowLeaderLines: false,
							//},
							ShowBlanksAs: "zero",
						}); err != nil {
							fmt.Println(err)
						}
						b += 9
					}
				}
				a += 20
			}
		}
	}
	scatterFile.DeleteSheet("Sheet1")
	filePath := time.Now().Format("_散点图_2006-01-02_150405.xlsx")
	filePath = req.Product + "_" + req.Lot + filePath
	filePath = filepath.Join(utils.MakeSavePath("散点图"), filePath)
	_ = scatterFile.SaveAs(filePath)
	defer scatterFile.Close()
	return strings.ReplaceAll(viper.GetString("domain"), "\\", "/") + filePath
}
