// form/nc/nc-load/nc-load.js
// in layout nc-gantt-chart -component uses:	":data": "rec.gantt_data", ":options": "rec.gantt_options"
/*
Pieni dokumentaatio datamuodosta

On group, joilla voi olla items:ejä tai subgroup:eja.
items:illä on start ja end, sen sisälle (tai kait ulkopuolellekin) voi pistää element:ejä

elementit type: box, rect, text tai tail

Tyylit sekoitus css ja svg:n tyylejä. ks. https://echarts.apache.org/en/option.html#graphic.elements-rect.style
*/
const maxCapacityToDraw = 250

// const rec = reactive({
const rec2 = {
	name: 'form/nc/nc-load/rec.json',
	resource: state.rec.resource,
	gantt_data: [],
	gantt_options: {
		heightRatio: 1,
		labelWidth: 200,
		itemHeight: 22,
		defaultLines: 1,
		timeBuffer: -0.1 // timeBuffer: 24
	},
	no_decimal_capacity: 50,
	opacity_change: -0.2,
	default_opacity: 0.6,
	color_opacity: {
		khaki: 0.8,
		grey: 0.8,
		violet: 0.8,
		blue: 0.45,
		green: 0.8,
		white: 0.9
	},
	box_index: 0,
	tail_index: 1,
	text_index: 2,
	capacity_shift_index: 0,
	capacity_index: 1,
	work_index: 2,
	resource_graph_option: ['always', 'never', 'has work'],
	work_phase_rec: {
		id: 'work',
		start: '',
		end: '',
		title: '',
		tooltip: '',
		elements: [
			{
				type: 'box',
				end: '',
				expand: 1,
				style: {
					stroke: 'black',
					lineWidth: 1.5,
					lineDash: [0]
				}
			},
			{
				type: 'tail',
				start: ''
				// hover: false // true better, but does not draw tail line
			},
			{
				type: 'text',
				text: '',
				inside: false
			}
		],
		connections: [{ to: 0, hover: false }]
	},
	capacity_shift_item_rec: {
		id: 'free-capacity',
		start: '',
		end: '',
		title: '',
		elements: [
			{
				type: 'box',
				style: {
					fill: 'white',
					stroke: 'black',
					lineWidth: 0
				}
			}
		]
	},
	capacity_item_rec: {
		id: 'cap',
		start: '',
		end: '',
		title: '',
		elements: [
			{
				type: 'box',
				style: {
					fill: '',
					stroke: 'black',
					lineWidth: 0
				}
			}
			// { type: 'rect', color: 'lightgreen', height: 0 }
		]
	},
	capacity_rec: {
		title: '',
		defaultElements: [{ type: 'text', inside: true }],
		boxStyle: { lineDash: [2], stroke: 'gray' },
		groupDividerStyle: { lineDash: [0], stroke: '#ccc', lineWidth: 0.5 },
		items: [],
		subgroups: [
			{
				sticky: true,
				title: 'Capacity',
				draggable: true,
				bgStyle: { fill: '#ddd' },
				tooltip: '',
				items: []
			},
			{
				sticky: true,
				title: 'lg',
				// draggable: true,
				bgStyle: { fill: '#f2f2f2' },
				tooltip: '',
				items: []
			},
			{
				defaultLines: 1,
				heightRatio: 0.5,
				timeBuffer: -400,
				title: 'Work phases',
				draggable: true,
				tooltip: '',
				items: []
			}
		]
	}
}
state.rec.phase_open_state = nc.invertTable(state.rec.phase_open_state)
state.rec.phase_stopped_started_state = nc.invertTable(state.rec.phase_stopped_started_state)
const rec = Object.assign({}, state.rec, rec2)
const hdr = state.hdr
let grid = state.grid // may be undefined
const parseBefore = peg.parseBefore
const parseAfter = peg.parseAfter

let defaultCapItem = rec.capacity_item_rec //  defaultCapItem = rec.capacity_shift_item_rec
const noDecimalCapacity = rec.no_decimal_capacity
const phaseStateColor = rec.phase_state_color
const defaultPhaseColor = rec.default_phase_color
const colorOpacity = rec.color_opacity
const defaultOpacity = rec.default_opacity
const opacityChange = rec.opacity_change
const boxIndex = rec.box_index
const textIndex = rec.text_index
const tailIndex = rec.tail_index
const capacityShiftIndex = rec.capacity_shift_index
const capacityIndex = rec.capacity_index
const workIndex = rec.work_index
const weekday = hdr.load.weekday

if (rec.gantt_data.length < 1) {
	update()
}

function update() {
	nc.callServer(state, 'calc/loading', { old_capacity: state.rec.old_capacity }, ret => {
		if (!ret.grid) {
			message.setWarning(state, `Return grid was not found.`)
			return
		}
		grid = ret.grid
		/* setTimeout(() => {
			const graphData = setData(grid)
			console.log(`🚀 ~ file: nc-load.js graph data`, graphData)
		}, 700) */
		// state.rec.gantt_data = clone(capData[0].subgroups[0].items)
		if (grid?.capacityIdx) {
			rec.resource.forEach(item => {
				item.capArr = grid.capacityIdx[item.resource_id]
			})
			state.rec.gantt_data = { grid: grid, rec: state.rec }
		}
	})
}

function clone(input) {
	// copied from: https://medium.com/better-programming/javascript-tips-2-object-array-deep-clone-implementation-2d6a43e43d2a
	// if (input == null || typeof input != 'object') return input
	const output = Array.isArray(input) ? [] : {}
	for (const key of Object.keys(input)) {
		if (typeof input[key] === 'object') {
			output[key] = clone(input[key])
		} else {
			output[key] = input[key]
		}
	}
	return output
}

const mouse = ref({
	mouseover: null,
	mouseout: null,
	mousemove: null,
	click: null,
	drag: null,
	dragend: null
})

function mousemove(id, e) {
	mouse.value.mousemove = `${id}: ${Math.floor(e.offsetX)}x${Math.floor(e.offsetY)}`
}

function formatTime(jsDate) {
	return weekday[jsDate.getDay()] + ' ' + dt.formatDate(jsDate) + ' ' + dt.toHourMin(jsDate)
}

function addWork(capRec, lgRec, workInfo) {
	let startDt = workInfo.startDt
	let endDt = workInfo.endDt
	const resource = lgRec.resource_id
	const defaultWorkPhase = rec.work_phase_rec
	const data = grid['product_work-schedule'].data
	const workRec = capRec.subgroups[workIndex]
	workRec.tooltip = i => `tooltip: ${i}`
	const workPhaseData = workRec.items
	workPhaseData.length = 0
	let count = 0
	for (let idx = 1; idx <= data.length; idx++) {
		const workPhase = data[idx - 1]
		if (workPhase.resource_id === resource) {
			if (workPhase.start_dt < startDt) {
				startDt = workPhase.start_dt
			}
			if (workPhase.end2_dt > endDt) {
				endDt = workPhase.end2_dt
			}
			count++
			const item = clone(defaultWorkPhase)
			item.id = workPhase.record_id
			if (workPhase.end_dt < workPhase.start_dt) {
				message.setWarning(state, `Work end time is smaller than start time: ${workPhase}`)
			}
			if (workPhase.end2_dt < workPhase.end_dt) {
				message.setWarning(state, `Work end 2 time is smaller than end time: ${workPhase}`)
			}
			item.start = new Date(workPhase.start_dt * 1000)
			let end = workPhase.end_dt
			if (workPhase.start_dt <= end) {
				end += 30 * 60 // seconds = 30 minutes
			}
			const workEnd = new Date(end * 1000)
			if (workPhase.end2_dt >= end) {
				end = workPhase.end2_dt
			} else {
				end += 1 * 60 // 1 minute
			}
			const tailEnd = new Date(end * 1000)
			item.end = tailEnd
			item.elements[boxIndex].end = workEnd
			// item.elements[textIndex].end = tailEnd
			item.elements[tailIndex].start = workEnd
			// item.elements[tailIndex].end = tailEnd
			// todo: change end space to 1 if there are no coming connections
			const title = ` ${workPhase.production_order_id}.${workPhase.work_phase_number}  ` // spaces before/after to leave space for connectors
			item.elements[textIndex].text = title
			if (workPhase.color) {
				// const color = parseBefore(workPhase.color, ':')
				// const opacity = Number(parseAfter(workPhase.color, ':')) / 100 + opacityChange
				// item.elements[0].style.opacity = opacity
			} else {
				// item.elements[0].style.fill = phaseStateColor[workPhase.work_phase_state] || defaultPhaseColor
				item.elements[0].style.stroke = phaseStateColor[workPhase.work_phase_state] || defaultPhaseColor
				if (!phaseStateColor[workPhase.work_phase_state]) {
					message.setWarning(state, `Work phase state '${workPhase.work_phase_state}' color is not defined`)
					phaseStateColor[workPhase.work_phase_state] = defaultPhaseColor
				}
			}
			// item.title = title
			item.tooltip =
				// title +
				` ${workPhase.resource_id}, ${workPhase.production_order_id}, phase ${workPhase.work_phase_number}<br>State: ${workPhase.work_phase_state}, produced: ${
					workPhase.produced_amount
				}, remaining: <b>${workPhase.remaining_amount}</b> pcs<br>Work time ${workPhase.work_hours.toFixed(2)} h, extra time: ${workPhase.extra_hours.toFixed(2)} h<br>${formatTime(
					item.start
				)} - ${formatTime(item.end)}<br>${workPhase.description}`
			if (idx == data.length) {
				item.connections == null
			} else {
				item.connections[0].to = data[idx].record_id
			}
			workPhaseData.push(item)
		}
	}
	workRec.title += ` (${count})`
	workInfo.startDt = startDt
	workInfo.endDt = endDt
	workInfo.start = new Date(startDt * 1000)
	workInfo.end = new Date(endDt * 1000)
	workInfo.workCount = count
}

function addCapacitySubgroup(type, capArr, capRec, lgRec, areaStart, areaEnd, info) {
	capRec.tooltip = i => `tooltip: ${i}`
	const isShift = type === 'shift'
	const id = isShift ? '.sh.' : '.cap.'
	let capCount = capRec.items.length
	let capacityIdx = 0
	let load = info.load
	let capacity = 0
	const resource = lgRec.resource_id
	const now = new Date()
	for (let lgIdx = 1; lgIdx <= capArr.length; lgIdx++) {
		const capItem = capArr[lgIdx - 1]
		if (capItem.end_dt < areaStart) {
			// skip
		} else if (capItem.start_dt >= areaEnd) {
			// skip
		} else if (capItem.resource_id === resource) {
			if (capacityIdx > maxCapacityToDraw) {
				break
			}
			capacityIdx++
			let item
			// limit count, screen will flicker badly without this
			if (capacityIdx > capCount) {
				item = clone(defaultCapItem)
			} else {
				item = capRec.items[capacityIdx - 1]
			}
			item.id = resource + id + capacityIdx
			item.start_dt = capItem.start_dt
			item.end_dt = capItem.end_dt
			item.capacity = capItem.capacity
			item.start = new Date(capItem.start_dt * 1000)
			if (capItem.start_dt >= capItem.end_dt) {
				message.setWarning(state, `Capacity end time is smaller than start time: ${capItem}`)
				item.end = new Date((capItem.start_dt + 1) * 1000)
			} else {
				item.end = new Date(capItem.end_dt * 1000)
			}
			if (!isShift) {
				const color = parseBefore(capItem.color, ':')
				// const opacity = Number(parseAfter(capItem.color, ':')) / 100 + opacityChange
				item.elements[0].style.fill = color
				item.elements[0].style.opacity = (colorOpacity[color] || defaultOpacity) + opacityChange
				if (!colorOpacity[color]) {
					message.setWarning(state, `Capacity opacity for color '${color}' is not defined`)
					colorOpacity[color] = defaultOpacity
				}
			}
			if (item.end > now) {
				// todo: check start difference with now
				capacity += capItem.capacity / 3600
			}
			if (isShift) {
				item.title = capacity === 0 ? 0 : capacity < noDecimalCapacity ? capacity.toFixed(1) : capacity.toFixed(0)
			} else {
				let capacity2 = capItem.capacity / 3600
				item.title = capacity2 === 0 ? 0 : capacity2 < noDecimalCapacity ? capacity2.toFixed(1) : capacity2.toFixed(0)
			}
			item.tooltip = `Capacity: ${item.title} h<br>${dt.toHourMin(item.start)} - ${dt.toHourMin(item.end)}<p>${formatTime(item.start)} - ${formatTime(item.end)}</p><pre>${
				capItem.shift
			}. ${capItem.info}</pre>`
			if (capacityIdx > capCount) {
				capRec.items.push(item)
			}
		}
	}
	// console.debug(`🚀 ~ file: nc-load.js ~ capacityIdx ${capacityIdx} / ${capArr.length}`)
	return { capacityIdx, capacity, load }
}

function addCapacity(capArr, capShiftLimitArr, capRec, lgRec, workInfo, workInfoAll) {
	let areaStart = grid.info.start_time
	let areaEnd = grid.info.end_time
	if (workInfo.startDt > areaStart) {
		areaStart = workInfo.startDt
	}
	areaStart = areaStart - 3 * 86400 // 24 * 60 * 60 = 86400 seconds in day, 3 days before because weekend is 2 days
	if (workInfo.endDt < areaEnd) {
		areaEnd = workInfo.endDt
	}
	areaEnd = areaEnd + 86400
	let capSubGroupRec
	let info = { capacity: 0, load: 0, capacityCount: workInfoAll.capacityCount }
	// add shift limit capacity
	capSubGroupRec = capRec.subgroups[capacityShiftIndex]
	capSubGroupRec.tooltip = i => `tooltip: ${i}`
	defaultCapItem = rec.capacity_shift_item_rec
	info = addCapacitySubgroup('shift', capShiftLimitArr, capSubGroupRec, lgRec, areaStart, areaEnd, info)
	workInfo.capacityCount += info.capacityIdx
	// add detailed capacity
	capSubGroupRec = capRec.subgroups[capacityIndex]
	capSubGroupRec.title = lgRec.resource_id
	capSubGroupRec.tooltip = i => `tooltip: ${i}`
	defaultCapItem = rec.capacity_item_rec
	info = addCapacitySubgroup('capacity', capArr, capSubGroupRec, lgRec, areaStart, areaEnd, info)
	workInfo.capacityCount = info.capacityIdx
}

function updateResource(capArr, capShiftLimitArr, capRec, lgRec, workInfoAll) {
	const resource = lgRec.resource_id
	const show = (lgRec.show && `${lgRec.show} ${resource}`) || resource
	capRec.title = show
	let startDt = Number.MAX_SAFE_INTEGER
	let endDt = -Number.MAX_SAFE_INTEGER
	const workInfo = { startDt, endDt, capacityCount: 0 }
	// true ||
	addWork(capRec, lgRec, workInfo)
	addCapacity(capArr, capShiftLimitArr, capRec, lgRec, workInfo, workInfoAll)
	console.debug(`🚀 ~ nc-load.js ~ ${resource} workInfo: `, workInfo)
	return workInfo
}

function setQueryItem(index, event) {
	// nc-dropdown calls this
	console.log(`🚀 ~ file: nc-load.js ~ setQueryItem ~ index, $event`, index, event)
}

function setData(grid) {
	const updateTime = performance.now()
	const capLgArr = grid.capacityIdx
	const capLgShiftLimitArr = capLgArr
	if (!capLgArr) {
		message.setWarning(state, `Capacity array was not found.`)
		return
	}
	const capData = rec.gantt_data
	const defaultCapRec = rec.capacity_rec
	let capRec
	let workInfo
	const workInfoAll = { startDt: Number.MAX_SAFE_INTEGER, endDt: -Number.MAX_SAFE_INTEGER, capacityCount: 0, workCount: 0 }
	debugger
	for (let idx = 1; idx <= rec.resource.length; idx++) {
		const lgRec = rec.resource[idx - 1]
		const capArr = capLgArr[lgRec.resource_id]
		const capShiftLimitArr = capLgShiftLimitArr[lgRec.resource_id] || capLgShiftLimitArr
		if (!capArr) {
			message.setWarning(state, `Capacity for resource '${lgRec.resource_id}' was not found.`)
			debugger
			continue
		}
		// if (lgRec.resource_id === 'HIT' || lgRec.resource_id === 'SOR') {
		if (idx <= capData.length) {
			workInfo = updateResource(capArr, capShiftLimitArr, capData[idx - 1], lgRec, workInfoAll)
		} else {
			capRec = clone(defaultCapRec) // cloneShallow(defaultCapRec)
			capData.push(capRec)
			workInfo = updateResource(capArr, capShiftLimitArr, capRec, lgRec, workInfoAll)
		}
		workInfo.start = new Date(workInfo.startDt * 1000)
		workInfo.end = new Date(workInfo.endDt * 1000)
		workInfoAll.capacityCount += workInfo.capacityCount
		workInfoAll.workCount += workInfo.workCount
		if (workInfo.startDt < workInfoAll.startDt) {
			workInfoAll.startDt = workInfo.startDt
		}
		if (workInfo.endDt > workInfoAll.endDt) {
			workInfoAll.endDt = workInfo.endDt
		}
		// }
	}
	workInfoAll.start = new Date(workInfoAll.startDt * 1000)
	workInfoAll.end = new Date(workInfoAll.endDt * 1000)
	state.rec.show_start = workInfoAll.start
	state.rec.show_end = workInfoAll.end
	console.debug(`🚀 ~ nc-load.js ~ workInfoAll`, workInfoAll, grid['product_work-schedule'].info)
	console.debug(`🚀 ~ nc-load.js ~ update time: ${(performance.now() - updateTime) / 1000}`)
	return capData
}
