package schedule

import (
	dal "LAPP_ACURA_MOM_BACKEND/dao/base"
	"LAPP_ACURA_MOM_BACKEND/db"
	common "LAPP_ACURA_MOM_BACKEND/models/base"
	model "LAPP_ACURA_MOM_BACKEND/models/base"
	"LAPP_ACURA_MOM_BACKEND/utils"
	"container/list"
	"errors"
	"fmt"
	"strconv"
	"time"
)

type WorklineSrv struct {
	PlantNr          int
	WorklineId       string
	WorklineTab      model.WorkLine
	RelCtrlWay       string
	RelCtrlQty       int           // 下达控制数量
	RelCtrlDura      time.Duration // 下达控制时长，基于交货时间和计划结束时间的差值计算
	DayModelArray    []DayModelSrv
	DayModelIdxDict  map[string]int
	SchedTaskList    *list.List
	SchedTaskArray   []TaskSrv                             // 所有调度到当前产线的生产任务
	UnTimeCurve      TimeCurveSrv                          // 加载不可用时间模型后的时间曲线
	TimeCurve        TimeCurveSrv                          // 加载完时间模型后的时间曲线
	SchedTimeCurve   TimeCurveSrv                          // 调度过程中动态计算的时间曲线
	WorkShiftEffDict map[string]model.WorkLineWorkShiftLst // 产线排班表
	WorkShiftArray   []WorkShiftSrv                        // 产线班次列表
	WorkLoadArray    []WorkLoadSrv                         // 产线负载
}

// 初始化产线对象
func (wlsrv *WorklineSrv) Init(wltab model.WorkLine) {
	wlsrv.PlantNr = wltab.PlantNr
	wlsrv.WorklineId = wltab.WorkLineid
	wlsrv.WorklineTab = wltab
	wlsrv.RelCtrlWay = wltab.TaskReleaseWay
	wlsrv.RelCtrlQty = wltab.ReleaseQtyParameter
	wlsrv.RelCtrlDura = time.Duration(wltab.ReleaseDurationParameter) * time.Minute
	wlsrv.DayModelArray = []DayModelSrv{}
	wlsrv.DayModelIdxDict = make(map[string]int)
	wlsrv.WorkShiftEffDict = make(map[string]model.WorkLineWorkShiftLst)
	wlsrv.SchedTaskArray = []TaskSrv{}
	wlsrv.SchedTaskList = list.New()
	wlsrv.TimeCurve = TimeCurveSrv{}
	wlsrv.SchedTimeCurve = TimeCurveSrv{}
	wlsrv.WorkShiftArray = []WorkShiftSrv{}
	wlsrv.WorkLoadArray = []WorkLoadSrv{}
}

// LoadWorkShiftData 加载指定时段内的排班表：班次计划出勤数据
func (wlsrv *WorklineSrv) LoadWorkShiftData(startdate, enddate time.Time) (err error) {
	var (
		i           int
		key, st, et string
		wlwstablst  []model.WorkLineWorkShiftLst
	)

	st = utils.GetYMDString(startdate)
	et = utils.GetYMDString(enddate)
	// 初始化
	wlsrv.WorkShiftEffDict = make(map[string]model.WorkLineWorkShiftLst)
	// 加载指定日期范围内的产线排班数据
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	wlwstab := dal.NewWorkLineWorkShiftLstDAO(session, wlsrv.PlantNr, "scheduler")
	if wlwstablst, err = wlwstab.GetWorkLineWorkShiftData(wlsrv.WorklineId, st, et); err != nil {
		err = errors.New(fmt.Sprintf("无法获取产线%s的排班数据:%v!", wlsrv.WorklineId, err))
		return
	}
	for i = 0; i < len(wlwstablst); i++ {
		key = wlwstablst[i].WorkDay + strconv.Itoa(wlwstablst[i].WorkShiftNr)
		wlsrv.WorkShiftEffDict[key] = wlwstablst[i]
	}

	return
}

// 将任务数组转化成列表
func (wlsrv *WorklineSrv) GetLineTaskList() {
	var (
		task TaskSrv
	)

	// 初始化
	wlsrv.SchedTaskList = list.New()
	for _, task = range wlsrv.SchedTaskArray {
		wlsrv.SchedTaskList.PushBack(task)
	}
}

// 将任务列表转化成数组
func (wlsrv *WorklineSrv) GetLineTaskArray() {
	var (
		e    *list.Element
		task *TaskSrv
	)

	// 初始化数组
	wlsrv.SchedTaskArray = []TaskSrv{}
	for e = wlsrv.SchedTaskList.Front(); e != nil; e = e.Next() {
		task = e.Value.(*TaskSrv)
		wlsrv.SchedTaskArray = append(wlsrv.SchedTaskArray, *task)
	}
}

// 基于产线指定的周模型和工厂日历获取在指定时间范畴内的日模型列表
func (wlsrv *WorklineSrv) GetWorkDayList(tmsrv *TimeModelSrv) (err error) {
	var (
		dmsrv                    DayModelSrv
		wmsrv                    WeekModelSrv
		calsrv                   CalendarSrv
		i, idx, daymodelnr, days int
		ok                       bool
	)

	// 初始化
	wlsrv.DayModelArray = []DayModelSrv{}
	wlsrv.DayModelIdxDict = make(map[string]int)
	// 获取产线的周模型
	if wmsrv, ok = tmsrv.WeekModelDict[wlsrv.WorklineTab.WeekModelNr]; !ok {
		err = errors.New(fmt.Sprintf("产线%s的周模型%d不存在！", wlsrv.WorklineTab.WorkLineid, wlsrv.WorklineTab.WeekModelNr))
		return
	}
	// 获取产线的工作日历
	if calsrv, ok = tmsrv.CalendarDict[wlsrv.WorklineTab.WorkCalendarNr]; !ok {
		err = errors.New(fmt.Sprintf("产线%s的工作日历%d不存在！", wlsrv.WorklineTab.WorkLineid, wlsrv.WorklineTab.WorkCalendarNr))
		return
	}
	// 基于时间模型的起始时间点获取产线的日模型列表
	days = utils.GetCrossDays(tmsrv.StartDate, tmsrv.EndDate)
	for i = 0; i < days; i++ {
		tt := tmsrv.StartDate.Add(time.Duration(i*24) * time.Hour)
		daymodelnr = wmsrv.GetDayModelNrByDate(tt)
		if dmsrv, ok = tmsrv.DayModelDict[daymodelnr]; !ok {
			err = errors.New(fmt.Sprintf("周模型%d中指定的日模型%d不存在！", wmsrv.WeekModelNr, daymodelnr))
			return
		} else {
			dmsrv.StartTime = tt
			dmsrv.EndTime = tt.Add(24 * time.Hour)
			wlsrv.DayModelArray = append(wlsrv.DayModelArray, dmsrv)
			wlsrv.DayModelIdxDict[utils.GetYMDString(tt)] = i
		}
	}
	// 将产线工作日历指定的特殊日模型进行替换
	for i = 0; i < len(calsrv.WorkCalTab.WorkCalendarLstLi); i++ {
		WorkDate := calsrv.WorkCalTab.WorkCalendarLstLi[i].WorkDate.Restore().Format("2006-01-02 15:04:05")
		if idx, ok = wlsrv.DayModelIdxDict[WorkDate]; ok {
			daymodelnr = calsrv.WorkCalTab.WorkCalendarLstLi[i].DayModelNr
			if dmsrv, ok = tmsrv.DayModelDict[daymodelnr]; !ok {
				err = errors.New(fmt.Sprintf("工作日历%d中在工作日%v指定的日模型%d不存在！", calsrv.CalendarNr, calsrv.WorkCalTab.WorkCalendarLstLi[i].WorkDate, daymodelnr))
				return
			} else {
				wlsrv.DayModelArray[idx] = dmsrv
			}
		}
	}

	return
}

// 基于产线指定的周模型和工厂日历生成时间曲线
// 获取产线的班次列表，基于日模型关联的人工班次和班次出勤
func (wlsrv *WorklineSrv) GenerateTimeCurve(tmsrv *TimeModelSrv) {
	var (
		i, j          int
		tlsrv         TimeLineSrv
		dayStartTime  time.Time
		dayEndTime    time.Time
		nextStartTime time.Time
		day           string
	)
	// 初始化
	wlsrv.SchedTimeCurve = TimeCurveSrv{}
	wlsrv.TimeCurve = TimeCurveSrv{}
	wlsrv.UnTimeCurve = TimeCurveSrv{}
	wlsrv.TimeCurve.TimeLineArray = []TimeLineSrv{}
	wlsrv.TimeCurve.StartTime = tmsrv.StartDate
	wlsrv.TimeCurve.EndTime = tmsrv.EndDate
	wlsrv.UnTimeCurve.StartTime = tmsrv.StartDate
	wlsrv.UnTimeCurve.EndTime = tmsrv.EndDate
	// 遍历日模型
	for i = 0; i < len(wlsrv.DayModelArray); i++ {
		dd, _ := time.ParseDuration(strconv.Itoa(i*24) + "h")
		dayStartTime = wlsrv.TimeCurve.StartTime.Add(dd)
		// 遍历日模型的每一条线段
		for j = 0; j < len(wlsrv.DayModelArray[i].LineArray); j++ {
			tlsrv = TimeLineSrv{}
			tlsrv.StartTime = dayStartTime.Add(time.Duration(wlsrv.DayModelArray[i].LineArray[j].StartSecond) * time.Second)
			tlsrv.EndTime = dayStartTime.Add(time.Duration(wlsrv.DayModelArray[i].LineArray[j].EndSecond) * time.Second)
			tlsrv.EffFactor = 1.0
			// 如果是人员班组产线，则基于计划出勤数据决定该时间段的效率因子
			if wlsrv.DayModelArray[i].DayModelTab.WorkShiftToggle {
				day = utils.GetYMDString(dayStartTime)
				tlsrv.EffFactor = float64(wlsrv.GetWorkShiftEff(day, wlsrv.DayModelArray[i].LineArray[j].WorkShiftNr, tmsrv))
			}
			tlsrv.Duration = tlsrv.GetDuration()
			wlsrv.TimeCurve.TimeLineArray = append(wlsrv.TimeCurve.TimeLineArray, tlsrv)
		}
	}

	//根据不可用时间推算出可用时间
	for i = 0; i < len(wlsrv.DayModelArray); i++ {
		dd, _ := time.ParseDuration(strconv.Itoa(i*24) + "h")
		dayStartTime = wlsrv.TimeCurve.StartTime.Add(dd)
		daydd, _ := time.ParseDuration("24h")
		dayEndTime = dayStartTime.Add(daydd)
		lenDay := len(wlsrv.DayModelArray[i].LineArray)
		nextStartTime = dayStartTime
		// 遍历日模型的每一条线段
		for j = 0; j < lenDay; j++ {
			tlsrv = TimeLineSrv{}
			tlsrv.StartTime = nextStartTime
			if j == lenDay {
				tlsrv.EndTime = dayEndTime
			} else {
				tlsrv.EndTime = dayStartTime.Add(time.Duration(wlsrv.DayModelArray[i].LineArray[j].StartSecond) * time.Second)
				nextStartTime = dayStartTime.Add(time.Duration(wlsrv.DayModelArray[i].LineArray[j].EndSecond) * time.Second)
			}
			tlsrv.EffFactor = 1.0
			// 如果是人员班组产线，则基于计划出勤数据决定该时间段的效率因子
			if wlsrv.DayModelArray[i].DayModelTab.WorkShiftToggle {
				day = utils.GetYMDString(dayStartTime)
				tlsrv.EffFactor = float64(wlsrv.GetWorkShiftEff(day, wlsrv.DayModelArray[i].LineArray[j].WorkShiftNr, tmsrv))
			}
			tlsrv.Duration = tlsrv.GetDuration()
			wlsrv.UnTimeCurve.TimeLineArray = append(wlsrv.UnTimeCurve.TimeLineArray, tlsrv)
		}
	}

	//赋值
	wlsrv.SchedTimeCurve = wlsrv.TimeCurve
	// 获取产线的时间链表
	wlsrv.TimeCurve.GetLineList()
	wlsrv.SchedTimeCurve.GetLineList()
	wlsrv.UnTimeCurve.GetLineList()

}

// CalDailyWorkload 计算产线的日产能负荷
func (wlsrv *WorklineSrv) CalDailyWorkload() {
	var (
		i, crossDays      int
		y1, d1            int
		m1                time.Month
		startTime, t1, t2 time.Time
		wloadsrv          WorkLoadSrv
	)

	// 初始化
	wlsrv.WorkLoadArray = []WorkLoadSrv{}
	// 基于指定的时间区间按天遍历
	crossDays = utils.GetCrossDays(wlsrv.TimeCurve.StartTime, wlsrv.TimeCurve.EndTime)
	y1, m1, d1 = wlsrv.TimeCurve.StartTime.Date()
	startTime = time.Date(y1, m1, d1, 0, 0, 0, 0, time.Local)
	for i = 0; i < crossDays; i++ {
		t1 = startTime.Add(time.Duration(i*24) * time.Hour)
		t2 = t1.Add(24 * time.Hour)
		wloadsrv = WorkLoadSrv{}
		wloadsrv.LoadType = common.WorkLoadType_Daily
		wloadsrv.StartTime = t1
		wloadsrv.EndTime = t2
		wloadsrv.AvailCapacity = wlsrv.TimeCurve.SumDuration(t1, t2)
		wloadsrv.RestCapacity = wlsrv.SchedTimeCurve.SumDuration(t1, t2)
		wloadsrv.Init()
		wloadsrv.AvailCapacityStr = time.Duration.String(wloadsrv.AvailCapacity - wloadsrv.RestCapacity)
		wlsrv.WorkLoadArray = append(wlsrv.WorkLoadArray, wloadsrv)
	}
}

// 计算产线的班产能负荷
func (wlsrv *WorklineSrv) CalShifWorkload() {
	var (
		i        int
		t1, t2   time.Time
		wloadsrv WorkLoadSrv
	)

	// 初始化
	wlsrv.WorkLoadArray = []WorkLoadSrv{}

	// 遍历产线班次
	for i = 0; i < len(wlsrv.WorkShiftArray); i++ {
		t1 = wlsrv.WorkShiftArray[i].StartTime
		t2 = wlsrv.WorkShiftArray[i].EndTime
		wloadsrv = WorkLoadSrv{}
		wloadsrv.LoadType = common.WorkLoadType_Daily
		wloadsrv.StartTime = t1
		wloadsrv.EndTime = t2
		wloadsrv.AvailCapacity = wlsrv.TimeCurve.CalDuration(t1, t2)
		wloadsrv.RestCapacity = wlsrv.SchedTimeCurve.CalDuration(t1, t2)
		wloadsrv.Init()
		wlsrv.WorkLoadArray = append(wlsrv.WorkLoadArray, wloadsrv)
	}
}

// 计算产线的小时产能负荷
func (wlsrv *WorklineSrv) CalHourlyWorkload() {
	var (
		i, j, crossDays   int
		y1, d1            int
		m1                time.Month
		startTime, t1, t2 time.Time
		wloadsrv          WorkLoadSrv
	)

	// 初始化
	wlsrv.WorkLoadArray = []WorkLoadSrv{}

	// 基于指定的时间区间按天遍历
	crossDays = utils.GetCrossDays(wlsrv.TimeCurve.StartTime, wlsrv.TimeCurve.EndTime)
	y1, m1, d1 = wlsrv.TimeCurve.StartTime.Date()
	startTime = time.Date(y1, m1, d1, 0, 0, 0, 0, time.UTC)
	for i = 0; i < crossDays; i++ {
		for j = 0; j < 24; j++ {
			t1 = startTime.Add(time.Duration(i*24+j) * time.Hour)
			t2 = t1.Add(1 * time.Hour)
			wloadsrv = WorkLoadSrv{}
			wloadsrv.LoadType = common.WorkLoadType_Hourly
			wloadsrv.StartTime = t1
			wloadsrv.EndTime = t2
			wloadsrv.AvailCapacity = wlsrv.TimeCurve.CalDuration(t1, t2)
			wloadsrv.RestCapacity = wlsrv.SchedTimeCurve.CalDuration(t1, t2)
			wloadsrv.Init()
			wlsrv.WorkLoadArray = append(wlsrv.WorkLoadArray, wloadsrv)
		}
	}
}

// 基于产线排班信息获取指定日期和班次的班次效率，如果不存在则返回班次标准效率1.0
func (wlsrv *WorklineSrv) GetWorkShiftEff(day string, wsnr int, tmsrv *TimeModelSrv) (eff float64) {
	var (
		key        string
		ok         bool
		perqty     int
		wlwsefftab model.WorkLineWorkShiftLst
		wss        WorkShiftSrv
	)

	eff = 1.0
	// 获取人员班组对象
	if wss, ok = tmsrv.WorkShiftDict[wsnr]; !ok {
		fmt.Println("指定人员班组:", wsnr, " 不存在！")
		return
	}
	key = day + strconv.Itoa(wsnr)
	if wlwsefftab, ok = wlsrv.WorkShiftEffDict[key]; ok {
		// 获取指定日期和人员班组号的排班人数
		perqty = wlwsefftab.PersonQty
		// 比较人数，获取效率值
		eff = wss.GetEffByPersonQty(perqty)
		return
	}
	return
}
