package logic

import (
	"encoding/json"
	"errors"
	"eta/eta_index_lib/models"
	"eta/eta_index_lib/models/future_good"
	"eta/eta_index_lib/utils"
	"fmt"
	"github.com/dengsgo/math-engine/engine"
	"github.com/shopspring/decimal"
	"sort"
	"strconv"
	"strings"
	"time"
)

// ChartInfoDateReq 图表的日期数据(日期相关)
type ChartInfoDateReq struct {
	Type  int    `description:"配置类型"`
	Date  string `description:"固定日期"`
	Value int    `description:"N天的值"`
	Color string `description:"颜色"`
	Name  string `description:"别名"`
}

// SectionScatterReq 截面散点请求
type SectionScatterReq struct {
	XName       string `description:"x轴名称"`
	XNameEn     string `description:"x轴名称(英文)"`
	XUnitName   string `description:"x轴单位名称"`
	XUnitNameEn string `description:"x轴单位名称(英文)"`
	YName       string `description:"y轴名称"`
	YNameEn     string `description:"y轴名称(英文)"`
	YUnitName   string `description:"y轴单位名称"`
	YUnitNameEn string `description:"y轴单位名称(英文)"`
	XMinValue   string `description:"X轴的最小值"`
	XMaxValue   string `description:"X轴的最大值"`
	YMinValue   string `description:"Y轴的最小值"`
	YMaxValue   string `description:"Y轴的最大值"`
	//EdbList     []SectionScatterEdbItemReq    `description:"指标数据"`
	SeriesList []SectionScatterSeriesItemReq `description:"系列数据"`
}

// SectionScatterSeriesItemReq 系列的请求
type SectionScatterSeriesItemReq struct {
	Name            string `description:"系列名"`
	NameEn          string `description:"系列名(英文名)"`
	Color           string `description:"颜色"`
	EdbInfoList     []SectionScatterEdbItemReq
	ShowTrendLine   bool `description:"是否展示趋势线"`
	ShowFitEquation bool `description:"是否展示方程式"`
	ShowRSquare     bool `description:"是否展示R平方"`
}

// SectionScatterEdbItemReq 截面散点请求的指标
type SectionScatterEdbItemReq struct {
	XEdbInfoId int    `description:"X轴的指标ID"`
	YEdbInfoId int    `description:"Y轴的指标ID"`
	Name       string `description:"别名"`
	NameEn     string `description:"英文别名"`
	XDateType  int    `description:"X轴的日期配置类型"`
	XDate      string `description:"X轴的日期固定日期"`
	XDateValue int    `description:"X轴的日期N天的值"`
	YDateType  int    `description:"Y轴的日期配置类型"`
	YDate      string `description:"Y轴的日期固定日期"`
	YDateValue int    `description:"Y轴的日期N天的值"`
	IsShow     bool   `description:"是否展示"`
}

// XData 商品价格曲线的的x轴数据
type XData struct {
	Name   string `description:"别名"`
	NameEn string `description:"英文别名"`
}

// YData 柱方图的y轴数据
type YData struct {
	Date           string          `description:"数据日期"`
	ConfigDate     time.Time       `description:"配置的日期" json:"-"`
	Color          string          `description:"数据颜色"`
	Name           string          `description:"别名"`
	NameEn         string          `description:"英文别名"`
	Value          []float64       `description:"每个指标的值"`
	NoDataEdbList  []int           `description:"没有数据的指标列表"`
	XEdbInfoIdList []int           `description:"对应X轴的指标id列表"`
	NameList       []string        `description:"每个值对应的名称"`
	EnNameList     []string        `description:"每个值对应的英文名称"`
	EdbValMap      map[int]float64 `description:"指标与值的对应" json:"-"`
	M              []int           `description:"对应开始日期的间隔值" json:"-"`
}

// BarChartInfoEdbItemReq 柱方图预览请求数据(指标相关)
type BarChartInfoEdbItemReq struct {
	EdbInfoId int    `description:"指标ID"`
	Name      string `description:"别名"`
	NameEn    string `description:"英文别名"`
	Source    int    `description:"1:ETA图库;2:商品价格"`
}

// ChartInfoReq 图表预览请求数据
type ChartInfoReq struct {
	FutureGoodEdbInfoIdList []models.EdbInfoFromTag `description:"指标信息"`
	CalculateFormula        string                  `description:"计算公式"`
	BaseEdbInfoId           int                     `description:"基础的指标id"`
	DateList                []ChartInfoDateReq      `description:"日期配置"`
	ProfitNameEn            string                  `description:"利润英文名称"`
	EdbInfoIdList           []int                   `description:"现货指标ID列表"`
	XDataList               []XData                 `description:"横轴配置"`
}

// RefreshByChartId 根据图表id刷新图表
func RefreshByChartId(chartInfoId int) (err error, errMsg string) {
	// 查找图表
	chartInfo, err := models.GetChartInfoById(chartInfoId)
	if err != nil {
		if err.Error() == utils.ErrNoRow() {
			err = nil
		}
		return
	}
	if chartInfo.Source != utils.CHART_SOURCE_FUTURE_GOOD_PROFIT {
		return
	}
	var extraConf ChartInfoReq
	err = json.Unmarshal([]byte(chartInfo.ExtraConfig), &extraConf)
	if err != nil {
		errMsg = "商品利润曲线图配置异常"
		return
	}

	// 查找商品利润图表的扩展信息
	chartInfoFutureGoodProfit := new(future_good.ChartInfoFutureGoodProfit)
	if err = chartInfoFutureGoodProfit.GetItemById(chartInfoId); err != nil {
		errMsg = "获取失败"
		if err.Error() == utils.ErrNoRow() {
			err = nil
		}
		return
	}
	if len(extraConf.EdbInfoIdList) == 0 {
		extraConf.EdbInfoIdList = append(extraConf.EdbInfoIdList, extraConf.BaseEdbInfoId)
	}

	baseEdbInfo := new(models.EdbInfo)
	// ETA指标
	edbInfoListTmp, err := models.GetEdbInfoByIdList(extraConf.EdbInfoIdList)
	if err != nil {
		errMsg = "获取失败"
		return
	}
	//按照请求顺序排序
	edbInfoMap := make(map[int]*models.EdbInfo)
	for _, v := range edbInfoListTmp {
		edbInfoMap[v.EdbInfoId] = v
	}
	edbInfoList := make([]*models.EdbInfo, 0)
	for _, v := range extraConf.EdbInfoIdList {
		edbInfoList = append(edbInfoList, edbInfoMap[v])
	}
	edbInfoListMap := make(map[int]*models.EdbInfo)
	for k, v := range edbInfoList {
		edbInfoList[k].EdbNameSource = v.EdbName
		edbInfoListMap[v.EdbInfoId] = v
		if v.EdbInfoId == extraConf.BaseEdbInfoId {
			baseEdbInfo = v
		}
	}
	// 商品数据库指标
	futureGoodEdbInfoMap := make(map[int]*future_good.FutureGoodEdbInfo)
	zlFutureGoodEdbInfoList := make([]*future_good.FutureGoodEdbInfo, 0)
	for _, v := range extraConf.FutureGoodEdbInfoIdList {
		if _, ok := futureGoodEdbInfoMap[v.EdbInfoId]; ok {
			continue
		}

		zlFutureGoodEdbInfo, tmpErr := future_good.GetFutureGoodEdbInfo(v.EdbInfoId)
		if tmpErr != nil {
			err = tmpErr
			errMsg = "获取失败"
			return
		}

		futureGoodEdbInfoMap[v.EdbInfoId] = zlFutureGoodEdbInfo
		zlFutureGoodEdbInfoList = append(zlFutureGoodEdbInfoList, zlFutureGoodEdbInfo)
	}
	xDataList, yDataList, err := GetProfitChartEdbData(baseEdbInfo, edbInfoList, zlFutureGoodEdbInfoList, extraConf.DateList, extraConf.CalculateFormula, extraConf.FutureGoodEdbInfoIdList, extraConf.XDataList)
	xDataListByte, err := json.Marshal(xDataList)
	if err != nil {
		errMsg = "保存失败"
		err = errors.New("X轴数据转换失败,ERR:" + err.Error())
		return
	}
	yDataListByte, err := json.Marshal(yDataList)
	if err != nil {
		errMsg = "保存失败"
		err = errors.New("Y轴数据转换失败,ERR:" + err.Error())
		return
	}

	extraUpdateCol := make([]string, 0)
	chartInfoFutureGoodProfit.XValue = string(xDataListByte)
	chartInfoFutureGoodProfit.YValue = string(yDataListByte)
	//chartInfoFutureGoodProfit.ProfitName = zlFutureGoodEdbInfoList[0].FutureGoodEdbName + "盘面利润"
	chartInfoFutureGoodProfit.ModifyTime = time.Now()
	extraUpdateCol = []string{"XValue", "YValue", "ProfitName", "ModifyTime"}
	err = chartInfoFutureGoodProfit.Update(extraUpdateCol)

	return
}

// GetProfitChartEdbData 获取利润图表的指标数据
func GetProfitChartEdbData(baseEdbInfo *models.EdbInfo, edbInfoList []*models.EdbInfo, zlFutureGoodEdbInfoList []*future_good.FutureGoodEdbInfo, chartInfoDateList []ChartInfoDateReq, formulaStr string, edbInfoFromTagList []models.EdbInfoFromTag, reqXDataList []XData) (xDataList []XData, yDataList []YData, err error) {

	if baseEdbInfo == nil {
		err = errors.New("ETA指标未选取")
		return
	}
	if len(edbInfoList) == 0 {
		edbInfoList = append(edbInfoList, baseEdbInfo)
	}
	if len(zlFutureGoodEdbInfoList) <= 0 {
		err = errors.New("商品指标未选取")
		return
	}

	// 标签与期货商品指标的关联关系
	tagEdbIdMap := make(map[string]int)
	// 有效的期货商品指标
	futureGoodEdbInfoIdMap := make(map[int]int)
	{
		tmpTagEdbIdMap := make(map[string]int)
		for _, v := range edbInfoFromTagList {
			tmpTagEdbIdMap[v.FromTag] = v.EdbInfoId
		}
		formulaMap := CheckFormula(formulaStr)
		for _, tag := range formulaMap {
			tagEdbIdMap[tag] = tmpTagEdbIdMap[tag]
			futureGoodEdbInfoIdMap[tmpTagEdbIdMap[tag]] = tmpTagEdbIdMap[tag]
		}
	}
	// 指标对应的所有数据
	//edbDataListMap := make(map[int][]*models.EdbDataList)
	// 普通的指标数据
	baseDataListMap := make(map[int][]*models.EdbDataList)
	for _, v := range edbInfoList {
		baseDataList := make([]*models.EdbDataList, 0)
		baseDataList, err = models.GetEdbDataList(v.Source, v.SubSource, v.EdbInfoId, "", "")
		if err != nil {
			return
		}
		baseDataListMap[v.EdbInfoId] = baseDataList
	}

	latestDate := zlFutureGoodEdbInfoList[0].EndDate
	latestDateTime, _ := time.ParseInLocation(utils.FormatDate, latestDate, time.Local)

	earliestDateTime := latestDateTime // 数据的最早日期,目的是为了找出最早的合约
	for _, barChartInfoDate := range chartInfoDateList {
		var findDateTime time.Time
		switch barChartInfoDate.Type {
		case 1: //最新值
			findDateTime = latestDateTime
		case 2: //近期几天
			findDateTime = latestDateTime.AddDate(0, 0, -barChartInfoDate.Value)
		case 3: // 固定日期
			//寻找固定日期的数据
			tmpFindDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, barChartInfoDate.Date, time.Local)
			if tmpErr != nil {
				err = tmpErr
				return
			}
			findDateTime = tmpFindDateTime
		default:
			err = errors.New(fmt.Sprint("日期类型异常,Type:", barChartInfoDate.Type))
			return
		}
		if findDateTime.IsZero() {
			err = errors.New("错误的日期")
			return
		}
		if findDateTime.Before(earliestDateTime) {
			earliestDateTime = findDateTime
		}
	}

	monthNum := (latestDateTime.Year()-earliestDateTime.Year())*12 + int(latestDateTime.Month()-earliestDateTime.Month())

	// 存储主力合约下的所有月份合约
	futureGoodEdbInfoDateMap := make(map[int]map[string]*future_good.FutureGoodEdbInfo)
	futureGoodDataListMap := make(map[int][]*models.EdbDataList, 0)
	// 特殊的商品期货合约(只有M+N的合约,没有固定日期的合约)
	specialFutureGoodEdbInfoMap := make(map[int]map[int]*future_good.FutureGoodEdbInfo, 0)

	isAllChina := true // 是否都是国内的期货合约
	for _, v := range zlFutureGoodEdbInfoList {
		if v.RegionType != "国内" {
			isAllChina = false
			break
		}
	}

	nMap := make(map[string]string)
	var maxN int // 最大N值
	for _, v := range zlFutureGoodEdbInfoList {
		// 如果不是有效的商品期货指标,那么就过滤掉,不做数据查询处理,避免没必要的请求
		if _, ok := futureGoodEdbInfoIdMap[v.FutureGoodEdbInfoId]; !ok {
			continue
		}
		// 获取期货指标以及期货数据
		tmpFutureGoodEdbInfoList, tmpErr := future_good.GetChildFutureGoodEdbInfoListByParentId(v.FutureGoodEdbInfoId)
		if tmpErr != nil {
			err = tmpErr
			return
		}

		childFutureGoodEdbInfoMap, tmpMaxN, tmpErr := getProfitFutureGoodEdbInfoList(earliestDateTime, v, tmpFutureGoodEdbInfoList, isAllChina, monthNum)
		if tmpErr != nil {
			err = tmpErr
			return
		}
		if maxN < tmpMaxN {
			maxN = tmpMaxN
		}
		futureGoodEdbInfoDateMap[v.FutureGoodEdbInfoId] = childFutureGoodEdbInfoMap

		if v.FutureGoodEdbType == 2 {
			specialFutureGoodEdbInfoMap[v.FutureGoodEdbInfoId] = make(map[int]*future_good.FutureGoodEdbInfo)
		}
		// 获取数据
		for date, childFutureGoodEdbInfo := range childFutureGoodEdbInfoMap {
			nMap[date] = date
			dataList := make([]*models.EdbDataList, 0)

			tmpDataList, tmpErr := future_good.GetFutureGoodEdbDataListByDate(childFutureGoodEdbInfo.FutureGoodEdbInfoId, "", "")
			if tmpErr != nil {
				return
			}
			for _, tmpData := range tmpDataList {
				dataList = append(dataList, &models.EdbDataList{
					EdbDataId:     tmpData.FutureGoodEdbDataId,
					EdbInfoId:     tmpData.FutureGoodEdbInfoId,
					DataTime:      tmpData.DataTime.Format(utils.FormatDate),
					DataTimestamp: tmpData.DataTimestamp,
					Value:         tmpData.Close,
				})
			}
			futureGoodDataListMap[childFutureGoodEdbInfo.FutureGoodEdbInfoId] = dataList

			if childFutureGoodEdbInfo.FutureGoodEdbType == 2 {
				specialFutureGoodEdbInfoMap[v.FutureGoodEdbInfoId][childFutureGoodEdbInfo.Month] = childFutureGoodEdbInfo
			}
		}
	}

	// 找出所有的N值,并进行正序排列
	dateList := make([]string, 0)
	for _, n := range nMap {
		dateList = append(dateList, n)
	}
	sort.Slice(dateList, func(i, j int) bool {
		return dateList[i] < dateList[j]
	})
	var reqEdbInfoIds []int
	for _, v := range edbInfoList {
		reqEdbInfoIds = append(reqEdbInfoIds, v.EdbInfoId)
		tmp := XData{
			Name:   v.EdbName,
			NameEn: v.EdbNameEn,
		}
		xDataList = append(xDataList, tmp)
	}
	var edbIdList []int
	futureGoodNameMap := make(map[int]map[int]string)
	edbIdList, yDataList, futureGoodNameMap, err = ProfitChartChartData(baseEdbInfo, baseDataListMap, futureGoodEdbInfoDateMap, futureGoodDataListMap, chartInfoDateList, baseEdbInfo.EndDate, specialFutureGoodEdbInfoMap, formulaStr, tagEdbIdMap, dateList, maxN, reqEdbInfoIds)

	// todo 最后处理数据
	tmpXDataList, newYDataList, err := handleProfitResultData(xDataList, futureGoodNameMap, yDataList, earliestDateTime, edbIdList)
	if err != nil {
		return
	}
	if len(reqXDataList) == 0 {
		xDataList = tmpXDataList
	} else {
		xDataList = reqXDataList
	}

	yDataList = newYDataList

	return
}

// ProfitChartChartData 获取数据
func ProfitChartChartData(baseEdbInfo *models.EdbInfo, baseDataListMap map[int][]*models.EdbDataList, futureGoodEdbInfoMap map[int]map[string]*future_good.FutureGoodEdbInfo, futureGoodEdbDataListMap map[int][]*models.EdbDataList, chartInfoDateList []ChartInfoDateReq, latestDate string, specialFutureGoodEdbInfoMap map[int]map[int]*future_good.FutureGoodEdbInfo, formulaStr string, tagEdbIdMap map[string]int, dateList []string, maxN int, reqEdbInfoIds []int) (edbIdList []int, yDataList []YData, futureGoodNameMap map[int]map[int]string, err error) {
	// 指标数据数组(10086:{"2022-12-02":100.01,"2022-12-01":102.3})
	//earliestDateTime time.Time
	// ETA指标数据
	allBaseEdbDateDataMap := make(map[int]map[string]float64)
	for edbInfoId, baseDataList := range baseDataListMap {
		baseEdbDateData := make(map[string]float64)
		for _, edbData := range baseDataList {
			baseEdbDateData[edbData.DataTime] = edbData.Value
		}
		allBaseEdbDateDataMap[edbInfoId] = baseEdbDateData
	}

	// 商品指标数据
	edbDataMap := make(map[int]map[string]float64)
	for edbInfoId, edbDataList := range futureGoodEdbDataListMap {
		edbDateData := make(map[string]float64)
		for _, edbData := range edbDataList {
			edbDateData[edbData.DataTime] = edbData.Value
		}
		edbDataMap[edbInfoId] = edbDateData
	}

	latestDateTime, _ := time.ParseInLocation(utils.FormatDate, latestDate, time.Local)

	yDataList = make([]YData, 0) //y轴的数据列表

	// 将计算公式中的字母转大写
	formulaStr = strings.ToUpper(formulaStr)
	futureGoodNameMap = make(map[int]map[int]string)
	for tmpk, barChartInfoDate := range chartInfoDateList {
		yDataMap := make(map[int]float64)
		var maxDate time.Time

		var findDateTime time.Time
		switch barChartInfoDate.Type {
		case 1: //最新值
			findDateTime = latestDateTime
		case 2: //近期几天
			findDateTime = latestDateTime.AddDate(0, 0, -barChartInfoDate.Value)
		case 3: // 固定日期
			//寻找固定日期的数据
			tmpFindDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, barChartInfoDate.Date, time.Local)
			if tmpErr != nil {
				err = tmpErr
				return
			}
			findDateTime = tmpFindDateTime
		default:
			err = errors.New(fmt.Sprint("日期类型异常,Type:", barChartInfoDate.Type))
			return
		}
		if findDateTime.IsZero() {
			err = errors.New("错误的日期")
			return
		}

		findDataList := make([]float64, 0)  // 当前日期的数据值
		noDataIdList := make([]int, 0)      // 没有数据的指标id
		noDataIdMap := make(map[int]int, 0) // 没有数据的指标map
		xEdbInfoIdList := make([]int, 0)    // 当前数据的指标id列表

		// 现货指标
		index := 0
		var realDateTime time.Time
		// 现货指标
		baseEdbDateData, ok := allBaseEdbDateDataMap[baseEdbInfo.EdbInfoId]
		if !ok {
			err = fmt.Errorf("指标id: %d 没有数据", baseEdbInfo.EdbInfoId)
			return
		}
		realDateTime, findDataValue, isFind, tmpErr := GetNeedDateData(findDateTime, baseDataListMap[baseEdbInfo.EdbInfoId], baseEdbDateData, edbDataMap)
		if tmpErr != nil {
			err = tmpErr
			return
		}
		if isFind {
			maxDate = realDateTime
		}
		edbIdList = make([]int, 0) //普通指标ID
		for _, edbInfoId := range reqEdbInfoIds {
			if edbInfoId == baseEdbInfo.EdbInfoId {
				findDataList = append(findDataList, findDataValue)
				yDataMap[index] = findDataValue
				xEdbInfoIdList = append(xEdbInfoIdList, edbInfoId)
				edbIdList = append(edbIdList, edbInfoId)
				index += 1
				continue
			}
			baseEdbDateDataTmp, ok := allBaseEdbDateDataMap[edbInfoId]
			if !ok {
				err = fmt.Errorf("指标id: %d 没有数据", edbInfoId)
				return
			}
			findDataValueTmp, isFindTmp := baseEdbDateDataTmp[realDateTime.Format(utils.FormatDate)]
			if !isFindTmp {
				noDataIdList = append(noDataIdList, edbInfoId)
				noDataIdMap[edbInfoId] = edbInfoId
			}
			findDataList = append(findDataList, findDataValueTmp)
			yDataMap[index] = findDataValueTmp

			xEdbInfoIdList = append(xEdbInfoIdList, edbInfoId)
			edbIdList = append(edbIdList, edbInfoId)
			index += 1
		}

		mList := make([]int, 0) // 间隔月份

		tmpNameMap := make(map[int]string)
		// 最小开始的n值
		//minN := (findDateTime.Year()-earliestDateTime.Year())*12 + int(findDateTime.Month()-earliestDateTime.Month())
		for _, date := range dateList {
			currDate, _ := time.ParseInLocation(utils.FormatYearMonthDate, date, time.Local)
			// 如果当前的n值小于最小开始的n值,那么就不处理
			//if n < minN {
			//	continue
			//}
			//findDateTime

			// 获取当前日期相对开始日期的期数
			tmpN := (currDate.Year()-realDateTime.Year())*12 + int(currDate.Month()-realDateTime.Month())
			if tmpN <= 0 {
				continue
			}

			// 如果期数大于最大期数,那么就退出当前匹配
			if tmpN >= maxN {
				break
			}

			zlAndChildEdbId := make(map[int]int)
			childFutureGoodEdbInfoIdList := make([]int, 0)
			for zlFutureGoodEdbInfoId, futureGoodEdbInfoList := range futureGoodEdbInfoMap {
				// 判断是否特殊合约
				if childFutureGoodEdbInfoIdMap, ok := specialFutureGoodEdbInfoMap[zlFutureGoodEdbInfoId]; ok {
					if childFutureGoodEdbInfo, ok2 := childFutureGoodEdbInfoIdMap[tmpN]; ok2 {
						childFutureGoodEdbInfoIdList = append(childFutureGoodEdbInfoIdList, childFutureGoodEdbInfo.FutureGoodEdbInfoId)
						zlAndChildEdbId[zlFutureGoodEdbInfoId] = childFutureGoodEdbInfo.FutureGoodEdbInfoId
					}

				} else {
					if childFutureGoodEdbInfo, ok2 := futureGoodEdbInfoList[date]; ok2 {
						childFutureGoodEdbInfoIdList = append(childFutureGoodEdbInfoIdList, childFutureGoodEdbInfo.FutureGoodEdbInfoId)
						zlAndChildEdbId[zlFutureGoodEdbInfoId] = childFutureGoodEdbInfo.FutureGoodEdbInfoId
					}
				}
			}

			// 合约不全,不参与计算
			//if len(childFutureGoodEdbInfoIdList) != lenZlFutureGoodEdbInfo {
			//	continue
			//}

			calculateMap := make(map[int]float64)
			for _, childFutureGoodEdbInfoId := range childFutureGoodEdbInfoIdList {
				tmpFindDataValue, tmpIsFind := edbDataMap[childFutureGoodEdbInfoId][realDateTime.Format(utils.FormatDate)]

				if tmpIsFind && tmpFindDataValue != 0 {
					calculateMap[childFutureGoodEdbInfoId] = tmpFindDataValue
				}
			}
			// 合约的数据不全,不参与计算
			//if len(calculateMap) != lenZlFutureGoodEdbInfo {
			//	continue
			//}

			newTagEdbIdMap := make(map[string]int)

			for tag, zlEdbId := range tagEdbIdMap {
				if tag == "A" {
					nameTmp := strings.Split(futureGoodEdbInfoMap[zlEdbId][date].FutureGoodEdbName, "(")
					nameTmpEn := strings.Split(futureGoodEdbInfoMap[zlEdbId][date].FutureGoodEdbNameEn, "(")
					if len(nameTmp) > 1 && len(nameTmpEn) > 1 {
						nameTmp[1] = strings.Trim(nameTmp[1], ")")
						nameTmpEn[1] = strings.Trim(nameTmpEn[1], ")")
						tmpNameMap[tmpN+1] = nameTmp[1] + "-" + nameTmpEn[1]
					}
				}
				newTagEdbIdMap[tag] = zlAndChildEdbId[zlEdbId]
			}
			//, formulaStr string, tagEdbIdMap map[string]int
			formulaFormStr := ReplaceFormula(newTagEdbIdMap, calculateMap, formulaStr)
			//计算公式异常,那么就移除该指标
			if formulaFormStr == `` {
				//removeDateList = append(removeDateList, sk)
				fmt.Println("异常了")
				continue
			}

			calVal, e := engine.ParseAndExec(formulaFormStr)
			//calVal, err := calResult.Float64()
			if e != nil {
				err = errors.New("计算失败:获取计算值失败 Err:" + e.Error() + ";formulaStr:" + formulaFormStr)
				fmt.Println(err)
				return
			}
			//yDataMap[n] = calVal
			//xEdbInfoIdList = append(xEdbInfoIdList, n)
			calVal, _ = decimal.NewFromFloat(calVal).Round(4).Float64()
			yDataMap[tmpN] = calVal
			xEdbInfoIdList = append(xEdbInfoIdList, tmpN)
			findDataList = append(findDataList, calVal)
		}
		yName := barChartInfoDate.Name
		yNameEn := barChartInfoDate.Name
		if yName == `` {
			if barChartInfoDate.Type == 2 {
				yName = strconv.Itoa(barChartInfoDate.Value) + "天前"
				if barChartInfoDate.Value == 1 {
					yNameEn = strconv.Itoa(barChartInfoDate.Value) + "day ago"
				} else {
					yNameEn = strconv.Itoa(barChartInfoDate.Value) + " days ago"
				}
			} else {
				yName = maxDate.Format(utils.FormatDate)
				yNameEn = maxDate.Format(utils.FormatDate)
			}
		}
		yDate := "0000-00-00"
		if !maxDate.IsZero() {
			yDate = maxDate.Format(utils.FormatDate)
		}

		{
			hasDataIndexList := make([]int, 0)
			for dataK, edbInfoId := range xEdbInfoIdList {
				if _, ok := noDataIdMap[edbInfoId]; !ok { // 如果是没有数据的指标id
					hasDataIndexList = append(hasDataIndexList, dataK)
				}
			}
			lenHasDataIndex := len(hasDataIndexList)
			if lenHasDataIndex > 0 {
				for lenHasDataI := 1; lenHasDataI < lenHasDataIndex; lenHasDataI++ {
					perK := hasDataIndexList[lenHasDataI-1] //上一个有数据的指标下标
					currK := hasDataIndexList[lenHasDataI]  //当前有数据的指标下标
					preVal := findDataList[perK]            //上一个有数据的坐标的值
					currVal := findDataList[currK]          //当前有数据的指标的值

					// 环差值
					hcValDeci := decimal.NewFromFloat(currVal).Sub(decimal.NewFromFloat(preVal)).Div(decimal.NewFromInt(int64(currK - perK)))
					var tmpI int64
					// 将两个中间的数据做平均值补全
					for hcI := perK + 1; hcI < currK; hcI++ {
						tmpI++
						findDataList[hcI], _ = decimal.NewFromFloat(preVal).Add(hcValDeci.Mul(decimal.NewFromInt(tmpI))).RoundCeil(4).Float64()
					}
				}
			}
		}
		futureGoodNameMap[tmpk] = tmpNameMap
		yDataList = append(yDataList, YData{
			Date:           yDate,
			ConfigDate:     realDateTime,
			Value:          findDataList,
			NoDataEdbList:  noDataIdList,
			XEdbInfoIdList: xEdbInfoIdList,
			Color:          barChartInfoDate.Color,
			Name:           yName,
			NameEn:         yNameEn,
			EdbValMap:      yDataMap,
			M:              mList,
		})
	}

	return
}

// getFutureGoodEdbInfoList 获取适用的指标列表
func getProfitFutureGoodEdbInfoList(earliestDateTime time.Time, zlFutureGoodEdbInfo *future_good.FutureGoodEdbInfo, tmpFutureGoodEdbInfoList []*future_good.FutureGoodEdbInfo, isAllChina bool, monthNum int) (futureGoodEdbInfoDateMap map[string]*future_good.FutureGoodEdbInfo, newMaxN int, err error) {
	maxN := 36 //最大36期合约
	futureGoodEdbInfoList := make([]*future_good.FutureGoodEdbInfo, 0)
	futureGoodEdbInfoDateMap = make(map[string]*future_good.FutureGoodEdbInfo)

	earliestDateTime = time.Date(earliestDateTime.Year(), earliestDateTime.Month(), 1, 0, 0, 0, 0, time.Local)
	if zlFutureGoodEdbInfo.RegionType == "国内" {
		startMonth := int(earliestDateTime.Month())
		if startMonth == 1 {
			futureGoodEdbInfoList = tmpFutureGoodEdbInfoList
		} else {
			// 因为是下标,所以对应想下标是需要-1的
			index := startMonth - 1
			futureGoodEdbInfoList = tmpFutureGoodEdbInfoList[index:]
			futureGoodEdbInfoList = append(futureGoodEdbInfoList, tmpFutureGoodEdbInfoList[:index]...)
		}

		lenFutureGoodEdbInfoList := len(futureGoodEdbInfoList)
		//futureGoodEdbInfoList
		//if isAllChina {
		//}
		// 如果全是国内指标,那么只需要拼上多出的几期合约即可
		maxN = lenFutureGoodEdbInfoList + monthNum

		for i := 1; i < maxN; i++ {
			k := i % lenFutureGoodEdbInfoList
			futureGoodEdbInfoDateMap[earliestDateTime.AddDate(0, i, 0).Format(utils.FormatYearMonthDate)] = futureGoodEdbInfoList[k]
		}

		//需求池604,只要是国内合约,最大必须是12期
		newMaxN = 12

		return
	}

	for _, v := range tmpFutureGoodEdbInfoList {
		//海外的连续日期,目前
		if v.FutureGoodEdbType == 2 {
			if v.Month <= maxN {
				futureGoodEdbInfoDateMap[earliestDateTime.AddDate(0, v.Month, 0).Format(utils.FormatYearMonthDate)] = v
				if v.Month > newMaxN {
					newMaxN = v.Month
				}
			}
			continue
		}

		if v.Year < earliestDateTime.Year() {
			continue
		}
		// 小于等于当前年,那么就肯定是ok的
		if v.Year == earliestDateTime.Year() && v.Month <= int(earliestDateTime.Month()) {
			continue
		}

		subYear := v.Year - earliestDateTime.Year()
		subMonth := v.Month - int(earliestDateTime.Month())
		// 如果(当前年-最新日期的年份) * 12个月 + (当前月-最新日期的月份) 小于总月份
		tmpN := subYear*12 + subMonth
		if tmpN < maxN {
			tmpDateTime := time.Date(v.Year, time.Month(v.Month), 0, 0, 0, 0, 0, time.Local)
			futureGoodEdbInfoDateMap[tmpDateTime.Format(utils.FormatYearMonthDate)] = v
			if tmpN > newMaxN {
				newMaxN = tmpN
			}
			continue
		}
	}

	return
}

// handleProfitResultData 处理成最终的结果数据
func handleProfitResultData(xDataListInit []XData, futureNameMap map[int]map[int]string, yDataList []YData, earliestDateTime time.Time, allEdbInfoIds []int) (xDataList []XData, newYDataList []YData, err error) {
	newYDataList = yDataList
	xDataList = xDataListInit

	nMap := make(map[int]int)
	nList := make([]int, 0)
	nListEdbMap := make(map[int]struct{})
	for _, v := range yDataList {
		for _, n := range v.XEdbInfoIdList {
			if utils.InArrayByInt(allEdbInfoIds, n) {
				if _, ok := nListEdbMap[n]; !ok {
					nList = append(nList, n)
					nListEdbMap[n] = struct{}{}
				}
			} else {
				nMap[n] = n
			}
		}
	}

	// 找出所有的N值,并进行正序排列
	nListTmp := make([]int, 0)
	for _, n := range nMap {
		nListTmp = append(nListTmp, n)
	}
	sort.Slice(nListTmp, func(i, j int) bool {
		return nListTmp[i] < nListTmp[j]
	})
	nList = append(nList, nListTmp...)
	for _, n := range nList {
		if utils.InArrayByInt(allEdbInfoIds, n) {
			continue
		}
		xDataList = append(xDataList, XData{
			Name:   fmt.Sprint("M+", n),
			NameEn: fmt.Sprint("M+", n),
		})
	}

	for yIndex, yData := range yDataList {
		newYDataList[yIndex].XEdbInfoIdList = []int{}
		newYDataList[yIndex].Value = []float64{}
		tmpNList := nList

		xEdbInfoIdList := yData.XEdbInfoIdList
		valIndex := 0
		needNum := 0
		for _, n := range tmpNList {
			if len(xEdbInfoIdList) > 0 {
				currN := xEdbInfoIdList[0]
				// 当前距离最早的日期相差的N数
				if n == currN { // todo 改成所有的基础现货指标
					if needNum > 0 {
						currVal := yData.Value[valIndex]
						preVal := yData.Value[valIndex-1]
						hcValDeci := decimal.NewFromFloat(currVal).Sub(decimal.NewFromFloat(preVal)).Div(decimal.NewFromInt(int64(needNum + 1)))

						for tmpNum := 0; tmpNum < needNum; tmpNum++ {
							newYDataList[yIndex].XEdbInfoIdList = append(newYDataList[yIndex].XEdbInfoIdList, 0)

							// 赋值平均值
							tmpVal, _ := decimal.NewFromFloat(preVal).Add(hcValDeci.Mul(decimal.NewFromInt(int64(tmpNum + 1)))).RoundCeil(4).Float64()
							newYDataList[yIndex].Value = append(newYDataList[yIndex].Value, tmpVal)
						}
					}
					if utils.InArrayByInt(allEdbInfoIds, currN) {
						newYDataList[yIndex].XEdbInfoIdList = append(newYDataList[yIndex].XEdbInfoIdList, currN)
					} else {
						newYDataList[yIndex].XEdbInfoIdList = append(newYDataList[yIndex].XEdbInfoIdList, currN+1)
					}

					newYDataList[yIndex].Value = append(newYDataList[yIndex].Value, yData.Value[valIndex])
					valIndex++
					needNum = 0
					if len(xEdbInfoIdList) > 0 {
						xEdbInfoIdList = xEdbInfoIdList[1:]
					}
				} else {
					needNum++
				}
			}
		}
	}

	maxI := 0
	for _, yData := range newYDataList {
		lenEdb := len(yData.XEdbInfoIdList)
		for i := 0; i < lenEdb; i++ {
			if yData.XEdbInfoIdList[i] != 0 && !utils.InArrayByInt(yData.NoDataEdbList, yData.XEdbInfoIdList[i]) {
				if maxI < i {
					maxI = i
				}
			}
		}
	}

	earliestDateTime = time.Date(earliestDateTime.Year(), earliestDateTime.Month(), 1, 0, 0, 0, 0, time.Local)
	xDataList = xDataList[0 : maxI+1]
	for yIndex, yData := range newYDataList {
		if len(yData.XEdbInfoIdList) > maxI+1 {
			newYDataList[yIndex].XEdbInfoIdList = yData.XEdbInfoIdList[0 : maxI+1]
		}
		if len(yData.Value) > maxI+1 {
			newYDataList[yIndex].Value = yData.Value[0 : maxI+1]
		}

		nameList := make([]string, 0)
		enNameList := make([]string, 0)
		for k1, n := range newYDataList[yIndex].XEdbInfoIdList {
			if utils.InArrayByInt(allEdbInfoIds, n) { // 现货价不处理
				tmpItem := xDataListInit[k1]
				nameList = append(nameList, tmpItem.Name)
				enNameList = append(enNameList, tmpItem.NameEn)
				continue
			}
			if n <= 0 {
				nameList = append(nameList, `无合约`)
				enNameList = append(enNameList, `no contract`)
			} else {
				nameTmp := futureNameMap[yIndex][n]
				nameTmpSlice := strings.Split(nameTmp, "-")
				nameList = append(nameList, nameTmpSlice[0])
				enNameList = append(enNameList, nameTmpSlice[1])
			}
		}
		newYDataList[yIndex].NameList = nameList
		newYDataList[yIndex].EnNameList = enNameList
	}
	return
}

func CheckFormula(formula string) map[string]string {
	mathFormula := []string{"MAX", "MIN", "ABS", "ACOS", "ASIN", "CEIL", "MOD", "POW", "ROUND", "SIGN", "SIN", "TAN", "LOG10", "LOG2", "LOG", "LN"}

	str := strings.ToUpper(formula)
	for _, v := range mathFormula {
		str = strings.Replace(str, v, "", -1)
	}
	str = strings.Replace(str, "(", "", -1)
	str = strings.Replace(str, ")", "", -1)

	byteMap := make(map[string]string)
	for i := 0; i < len(str); i++ {
		byteInt := str[i]
		if byteInt >= 65 && byteInt <= 90 {
			byteStr := string(byteInt)
			if _, ok := byteMap[byteStr]; !ok {
				byteMap[byteStr] = byteStr
			}
		}
	}
	return byteMap
}

func ReplaceFormula(tagEdbIdMap map[string]int, valArr map[int]float64, formulaStr string) string {
	funMap := GetFormulaMap()
	for k, v := range funMap {
		formulaStr = strings.Replace(formulaStr, k, v, -1)
	}

	replaceCount := 0
	for tag, edbInfoId := range tagEdbIdMap {
		if val, valOk := valArr[edbInfoId]; valOk { //值存在
			dvStr := fmt.Sprintf("%v", val)
			formulaStr = strings.Replace(formulaStr, tag, dvStr, -1)
			replaceCount++
		}
	}
	for k, v := range funMap {
		formulaStr = strings.Replace(formulaStr, v, k, -1)
	}
	fmt.Println(formulaStr)
	if replaceCount == len(tagEdbIdMap) {
		return formulaStr
	} else {
		return ""
	}
}

func GetFormulaMap() map[string]string {
	funMap := make(map[string]string)
	funMap["MAX"] = "[@@]"
	funMap["MIN"] = "[@!]"
	funMap["ABS"] = "[@#]"
	funMap["CEIL"] = "[@$]"
	funMap["COS"] = "[@%]"
	funMap["FLOOR"] = "[@^]"
	funMap["MOD"] = "[@&]"
	funMap["POW"] = "[@*]"
	funMap["ROUND"] = "[@(]"
	return funMap
}

// GetNeedDateData 获取合约内需要的日期数据
func GetNeedDateData(needDateTime time.Time, dataList []*models.EdbDataList, edbDataMap map[string]float64, allEdbDataMap map[int]map[string]float64) (findDateTime time.Time, findDataValue float64, isFind bool, err error) {
	//dataList := edbDataListMap[edbInfoId] //指标的所有数据值
	if len(dataList) <= 0 {
		// 没有数据的指标id
		return
	}

	//最早的日期
	minDateTime, err := time.ParseInLocation(utils.FormatDate, dataList[0].DataTime, time.Local)
	if err != nil {
		return
	}

	// 该日期存在数据的期货指标的最小数量,目前是现货和期货各1个,总共2个
	maxCount := 1

	for tmpDateTime := needDateTime; tmpDateTime.After(minDateTime) || tmpDateTime.Equal(minDateTime); tmpDateTime = tmpDateTime.AddDate(0, 0, -1) {
		tmpDate := tmpDateTime.Format(utils.FormatDate)
		tmpValue, ok := edbDataMap[tmpDate]
		if !ok {
			continue
		}

		// 该日期存在数据的指标数量
		count := 0

		for _, currEdbDataMap := range allEdbDataMap {
			_, tmpIsFind := currEdbDataMap[tmpDate]
			if tmpIsFind {
				count++
				if count >= maxCount {
					continue
				}
			}
		}

		// 该日期存在数据的期货指标数量小于2个,那么要继续往前找
		if count < maxCount {
			continue
		}

		//如果能找到数据,那么就返回
		// 数据为0,也直接返回,做无值处理
		if tmpValue == 0 {
			return
		}
		findDateTime, _ = time.ParseInLocation(utils.FormatDate, tmpDate, time.Local)
		findDataValue = tmpValue
		isFind = true
		return
	}

	return
}