import { CustomHTMLElement } from './lib/custom_html_element.mjs'

export class SmonDashboard extends CustomHTMLElement {// {{{
	static {// {{{
		this.tmpl = document.createElement('template')
		this.tmpl.innerHTML = `
			<style>
				:host {
					--edit-border: 1px solid #888;

					display: grid;
				}

				:host(.edit) {
					.el-grid {
						grid-gap: unset;
						border-top: var(--edit-border);
						border-left: var(--edit-border);

						& > * {
							border-right: var(--edit-border);
							border-bottom: var(--edit-border);
							padding: 8px 8px;
						}

						& > *:hover {
							background-color: rgba(255, 255, 255, 0.1);
							cursor: pointer;
						}

						& > .empty {
							display: block;
						}
					}

					.only-on-edit {
						display: unset;
					}
				}

				.el-error {
					display: none;
					margin-bottom: 16px;
					width: min-content;
					white-space: nowrap;
					background-color: #ff2a2a;
					color: #fff;
					font-weight: bold;
					font-size: 1.25em;
					padding: 16px 32px;
					border-radius: 8px;

					&.show {
						display: block;
					}
				}

				.el-grid {
					display: grid;
					grid-auto-flow: column;
					grid-auto-columns: min-content;
					grid-gap: 8px 16px;
					width: min-content;
					
					.empty {
						display: none;
					}
				}

				.only-on-edit {
					display: none;
				}

				button {
					width: min-content;
					white-space: nowrap;
					padding: 4px 8px;
					margin-bottom: 16px;
				}

				dialog {
					input[type="text"] {
						padding: 4px;
						margin-top: 4px;
					}

					select {
						margin-top: 4px;
					}

					button {
						margin-bottom: unset;
					}
				}

			</style>

			<div style="display: flex; gap: 8px;">
				<button data-el="edit-dashboard">Edit dashboard</button>
				<button class="only-on-edit" data-el="add-columns">Add columns</button>
				<button class="only-on-edit" data-el="add-rows">Add rows</button>
				<button class="only-on-edit" data-el="update-dashboard">Update dashboard</button>
			</div>

			<div data-el="error"></div>
			<div data-el="grid">
			</div>

			<dialog data-el="edit">
				<div data-el="edit-components"></div>
				<div style="display: flex; gap: 8px; margin-top: 16px;">
					<button data-el="edit-update">Update</button>
					<button data-el="edit-remove">Remove</button>
				</div>
			</dialog>
		`
	}// }}}
	constructor() {// {{{
		super(true)
		const script = this.querySelector('script')
		this.data = JSON.parse(script.textContent)
		globalThis.foo = this.data
		this.widgets = new Map() // keyed by datapoint ID, value is an array of widgets.
		this.addColumns = 0
		this.addRows = 0
		this.editedWidget = null

		this.elEditDashboard.addEventListener('click', () => this.classList.toggle('edit'))
		this.elAddColumns.addEventListener('click', () => { this.addColumns += 5; this.render() })
		this.elAddRows.addEventListener('click', () => { this.addRows += 5; this.render() })
		this.elUpdateDashboard.addEventListener('click', () => this.saveDashboard())
		this.elEditUpdate.addEventListener('click', () => this.editUpdate())

		this.render()
		this.fetchData() // Retrieve one as fast as possible since setInterval doesn't run before an interval.
		setInterval(() => this.fetchData(), 1000)
	}// }}}

	render() {// {{{
		const widgets = []
		this.widgets = new Map()

		let maxX = 0
		let maxY = 0
		let occupiedCells = new Map()

		for (const wd of this.data.Widgets) {
			if (!this.widgets.has(wd.DatapointID))
				this.widgets.set(wd.DatapointID, [])
			// widgetRefs modifies the array in the map directly.
			const widgetRefs = this.widgets.get(wd.DatapointID)
			const widget = SmonWidget.create(wd.Type, wd)
			widget.addEventListener('click', () => this.selectCell(widget))
			widgetRefs.push(widget)
			widgets.push(widget)

			maxX = Math.max(maxX, wd.X)
			maxY = Math.max(maxY, wd.Y)

			occupiedCells.set(`${wd.X}x${wd.Y}`, true)
			for (let x = 0; x <= wd.SpanX; x++) {
				occupiedCells.set(`${wd.X + x}x${wd.Y}`, true)
			}
		}

		maxX += this.addColumns
		maxY += this.addRows

		// Empty elements are created to fill up the empty cells in the grid.
		// This is needed as placeholders for grid and dragdrop targets when editing.
		for (let x = 1; x <= maxX; x++)
			for (let y = 1; y <= maxY; y++) {
				if (occupiedCells.has(`${x}x${y}`))
					continue

				const empty = new SmonWidgetEmpty(x, y)
				empty.addEventListener('click', () => this.selectCell(empty))
				widgets.push(empty)
			}

		this.elGrid.replaceChildren(...widgets)
	}// }}}
	error(msg) {// {{{
		this.elError.innerText = msg
		if (msg === '')
			this.elError.classList.remove('show')
		else
			this.elError.classList.add('show')
	}// }}}
	saveDashboard() {// {{{
	}// }}}
	async editUpdate() {// {{{
		await this.editedWidget.editUpdate()
		this.elEdit.close()
		this.editedWidget = null
		this.render()
	}// }}}
	selectCell(el) {// {{{
		if (!this.classList.contains('edit'))
			return

		this.editedWidget = el
		const components = el.edit()
		this.elEditComponents.replaceChildren(...components)
		this.elEdit.showModal()
	}// }}}
	async fetchData() {// {{{
		try {
			const res = await fetch(`/dashboard/values`)
			const json = await res.json()

			if (!json.OK)
				throw new Error(json.Error)

			for (const dp of json.Values) {
				const widgets = this.widgets.get(dp.ID) || []
				for (const w of widgets) {
					w.setValue(dp.Value)
					w.render()
				}
			}

			this.error('')
		} catch (e) {
			console.error(e)
			this.error(e.message)

			// All widgets are set in a NULL state to visualize the error.
			const allDpIDs = this.widgets.keys()
			for (const id of allDpIDs) {
				const widgets = this.widgets.get(id) || []
				for (const w of widgets) {
					w.setValue(null)
					w.render()
				}
			}
		}
	}// }}}
}// }}}

class SmonWidget extends CustomHTMLElement {// {{{
	static create(t, data) {// {{{
		switch (t) {
			case 0: return new SmonWidgetLabel(data)
			case 1: return new SmonWidgetOnOff(data)
		}
	}// }}}
	constructor(data) {// {{{
		super(true)
		this.data = data
		this.value = null
		this.editDialog = null
	}// }}}

	render() {// {{{
		this.style.gridColumn = this.data.X
		this.style.gridRow = this.data.Y

		if (this.data.SpanX > 0)
			this.style.gridColumn = `${this.data.X} / ${this.data.X + this.data.SpanX + 1}`

		if (this.data.Attributes.BorderBottom) {
			if (this.data.Attributes.Color)
				this.style.borderBottom = `1px solid ${this.data.Attributes.Color}`
			else
				this.style.borderBottom = `1px solid currentColor`
		}
	}// }}}
	setValue(v) {// {{{
		this.value = v
	}// }}}
	edit() {// {{{
		if (this.editDialog !== null)
			return

		const components = this.editWidget()
		return components
	}// }}}

	// Many widgets can have labels.
	// This function applies common attributes like size and color.
	applyLabelAttributes(el) {// {{{
		if (this.data.Attributes?.Size)
			el.style.fontSize = this.data.Attributes.Size

		if (this.data.Attributes?.Color)
			el.style.color = this.data.Attributes.Color

		if (this.data.Attributes?.Underline)
			if (this.data.Attributes?.Color)
				el.style.borderBottom = `1px solid ${this.data.Attributes.Color}`
			else
				el.style.borderBottom = `1px solid currentColor`
	}// }}}
}// }}}
class SmonWidgetEmpty extends SmonWidget {// {{{
	static {// {{{
		this.tmpl = document.createElement('template')
		this.tmpl.innerHTML = `
			<style>
				:host {
					min-width: 16px;
					min-height: 16px;
				}
			</style>
		`
	}// }}}
	constructor(x, y) {// {{{
		super({
			Attributes: {},
			X: x,
			Y: y,
		})

		this.classList.add('empty')
		this.render()
	}// }}}
	render() {// {{{
		super.render()
	}// }}}
	editWidget() {// {{{
		const div = document.createElement('div')
		div.innerHTML = `
			<div>Add widget:</div>
			<select style="width: 100%; padding: 4px 4px">
				<option value="0">Label</option>
				<option value="1">On / off</option>
			</select>
		`
		return [div]
	}// }}}
}// }}}
class SmonWidgetLabel extends SmonWidget {// {{{
	static {// {{{
		this.tmpl = document.createElement('template')
		this.tmpl.innerHTML = `
			<style>
				.el-label {
					font-weight: bold;
					margin-bottom: 8px;
				}
			</style>
			<div data-el="label"></div>
		`
	}// }}}
	constructor(data) {// {{{
		super(data)
		this.render()
	}// }}}
	render() {// {{{
		super.render()
		this.elLabel.innerText = this.data.Attributes?.Label || '[set Label]'
		this.applyLabelAttributes(this.elLabel)
	}// }}}
	editWidget() {// {{{
		this.editDiv = document.createElement('div')
		this.editDiv.innerHTML = `
			<div>Label:</div>
			<input type="text">
		`
		this.editDiv.querySelector('input').value = this.data.Attributes.Label
		return [this.editDiv]
	}// }}}
	async editUpdate() {// {{{
		console.log('hum')
		this.data.Attributes.Label = this.editDiv.querySelector('input').value
	}// }}}
}// }}}
class SmonWidgetOnOff extends SmonWidget {// {{{
	static {// {{{
		this.tmpl = document.createElement('template')
		this.tmpl.innerHTML = `
			<style>
				:host {
					display: grid;
					grid-template-columns: min-content 1fr;
					align-items: center;
					grid-gap: 8px;
				}

				img {
					height: 24px;
					width: 24px;
				}

			</style>
			<img data-el="status">
			<div data-el="label"></div>
		`
	}// }}}
	constructor(data) {// {{{
		super(data)
		this.render()
	}// }}}
	render() {// {{{
		super.render()

		let img
		switch (this.value) {
			case 1:
			case 'ON':
				img = 'widget_light_green.svg'
				break

			case 0:
			case 'OFF':
				img = 'widget_light_red.svg'
				break

			default:
				img = 'widget_light_off.svg'
		}

		this.elStatus.setAttribute('src', `/images/${_VERSION}/${img}`)
		this.elLabel.innerText = this.data.Attributes.Label
		this.applyLabelAttributes(this.elLabel)
	}// }}}
	editWidget() {// {{{
		this.editDiv = document.createElement('div')
		this.editDiv.innerHTML = `
			<div>Label:</div>
			<input type="text">
		`
		this.editDiv.querySelector('input').value = this.data.Attributes.Label
		return [this.editDiv]
	}// }}}
	async editUpdate() {// {{{
		this.data.Attributes.Label = this.editDiv.querySelector('input').value
	}// }}}
}// }}}


customElements.define("smon-widget", SmonWidget)
customElements.define("smon-widget-empty", SmonWidgetEmpty)
customElements.define("smon-widget-label", SmonWidgetLabel)
customElements.define("smon-widget-onoff", SmonWidgetOnOff)
customElements.define("smon-dashboard", SmonDashboard)
