import { Editor } from '@editor'
import { MessageBus } from '@mbus'

export class App {
	constructor() {// {{{
		window.mbus = new MessageBus()
		this.editor = null
		this.typesList = null
		this.currentNodeID = null

		const events = [
			'MENU_ITEM_SELECTED',
			'NODE_SELECTED',
			'EDITOR_NODE_SAVE',
			'TYPES_LIST_FETCHED',
		]
		for (const eventName of events)
			mbus.subscribe(eventName, event => this.eventHandler(event))

		mbus.dispatch('MENU_ITEM_SELECTED', 'node')
	}// }}}

	eventHandler(event) {// {{{
		switch (event.type) {
			case 'MENU_ITEM_SELECTED':
				const item = document.querySelector(`#menu [data-section="${event.detail}"]`)
				this.section(item, event.detail)
				break

			case 'NODE_SELECTED':
				this.currentNodeID = event.detail
				this.edit(this.currentNodeID)
				break

			case 'EDITOR_NODE_SAVE':
				this.nodeUpdate()
				break

			case 'TYPES_LIST_FETCHED':
				const types = document.getElementById('types')
				types.replaceChildren(this.typesList.render())

				break

			default:
				console.log(event)
		}
	}// }}}
	section(item, name) {// {{{
		for (const el of document.querySelectorAll('#menu .item'))
			el.classList.remove('selected')
		item.classList.add('selected')

		for (const el of document.querySelectorAll('.section.show'))
			el.classList.remove('show')

		switch (name) {
			case 'node':
				document.getElementById('nodes').classList.add('show')
				document.getElementById('editor-node').classList.add('show')
				break

			case 'type':
				document.getElementById('types').classList.add('show')
				document.getElementById('editor-type-schema').classList.add('show')

				if (this.typesList === null)
					this.typesList = new TypesList()
				this.typesList.fetchTypes().then(() => {
					mbus.dispatch('TYPES_LIST_FETCHED')
				})

				break
		}
	}// }}}
	edit(nodeID) {// {{{
		console.log(nodeID)
		fetch(`/nodes/${nodeID}`)
			.then(data => data.json())
			.then(json => {
				if (!json.OK) {
					showError(json.Error)
					return
				}

				const editorEl = document.querySelector('#editor-node .editor')
				this.editor = new Editor(json.Node.TypeSchema)
				editorEl.replaceChildren(this.editor.render(json.Node.Data))

			})
	}// }}}
	nodeUpdate() {// {{{
		if (this.editor === null)
			return

		const btn = document.querySelector('#editor-node .controls button')
		btn.disabled = true
		const buttonPressed = Date.now()

		const nodeData = this.editor.data()

		fetch(`/nodes/update/${this.currentNodeID}`, {
			method: 'POST',
			body: JSON.stringify(nodeData),
		})
			.then(data => data.json())
			.then(json => {
				if (!json.OK) {
					showError(json.Error)
					return
				}

				const timePassed = Date.now() - buttonPressed
				if (timePassed < 250)
					setTimeout(()=>btn.disabled = false, 250 - timePassed)
				else
					btn.disabled = false
			})
	}// }}}
}

export class TreeNode {
	constructor(parent, data) {// {{{
		this.data = data
		this.parent = parent
		this.childrenFetched = false
		this.children = null

		this.sortChildren()
	}// }}}

	render() {// {{{
		const nodeHTML = `
			<div class="node">
				<div class="expand-status"><img /></div>
				<div class="type-icon"><img /></div>
				<div class="name">${this.name()}</div>
				<div class="children"></div>
			</div>
		`

		const tmpl = document.createElement('template')
		tmpl.innerHTML = nodeHTML
		this.children = tmpl.content.querySelector('.children')

		tmpl.content.querySelector('.name').addEventListener('click', () => mbus.dispatch('NODE_SELECTED', this.data.ID))

		// data.NumChildren is set regardless of having fetched the children or not.
		if (this.hasChildren()) {
			const img = tmpl.content.querySelector('.expand-status img')
			img.setAttribute('src', `/images/${_VERSION}/node_modules/@mdi/svg/svg/plus-box-outline.svg`)
			img.addEventListener('click', event => this.toggleExpand(event))
		} else
			tmpl.content.querySelector('.expand-status').classList.add('leaf')

		if (this.data.TypeIcon) {
			const img = tmpl.content.querySelector('.type-icon img')
			img.setAttribute('src', `/images/${_VERSION}/node_modules/@mdi/svg/svg/${this.data.TypeIcon}.svg`)
		}

		this.parent.appendChild(tmpl.content)

		for (const c of this.data.Children || []) {
			(new TreeNode(this.children, c)).render()
		}
	}// }}}
	name() {// {{{
		if (this.data.TypeName === 'root_node')
			return 'Start'
		return this.data.Name
	}// }}}
	hasChildren() {// {{{
		return this.data.NumChildren > 0
	}// }}}
	sortChildren() {// {{{
		this.data.Children.sort((a, b) => {
			if (a.TypeName < b.TypeName) return -1
			if (a.TypeName > b.TypeName) return 1

			if (a.Name < b.Name) return -1
			if (a.Name > b.Name) return 1

			return 0
		})
	}// }}}

	toggleExpand(event) {// {{{
		const node = event.target.closest('.node')
		node?.classList.toggle('expanded')

		const img = node?.classList.contains('expanded') ? 'minus-box-outline' : 'plus-box-outline'
		event.target.setAttribute('src', `/images/${_VERSION}/node_modules/@mdi/svg/svg/${img}.svg`)

		if (!this.childrenFetched && this.data.NumChildren > 0 && this.data.Children.length == 0) {
			this.fetchChildren()
				.then(data => {
					this.childrenFetched = true
					this.data.Children = data.Children
					this.sortChildren()

					for (const nodeData of this.data.Children) {
						const node = new TreeNode(this.children, nodeData)
						node.render()
					}
				})
				.catch(err => {
					alert(err)
					console.error(err)
				})
		}
	}// }}}
	async fetchChildren() {// {{{
		return new Promise((resolve, reject) => {
			fetch(`/nodes/tree/${this.data.ID}?depth=2`)
				.then(data => data.json())
				.then(json => {
					if (json.OK)
						resolve(json.Nodes)
					else
						reject(json.Error)
				})
				.catch(err => reject(err))
		})
	}// }}}
}

export class TypesList {
	constructur() {// {{{
		this.types = []
	}// }}}
	async fetchTypes() {// {{{
		return new Promise((resolve, reject) => {
			fetch('/types/')
				.then(data => data.json())
				.then(json => {
					if (!json.OK) {
						showError(json.Error)
						return
					}
					this.types = json.Types
					resolve()
				})
				.catch(err => reject(err))
		})
	}// }}}
	render() {// {{{
		const div = document.createElement('div')

		this.types.sort((a,b)=> {
			if (a.Schema['x-group'] === undefined)
				a.Schema['x-group'] = 'No group'

			if (b.Schema['x-group'] === undefined)
				b.Schema['x-group'] = 'No group'

			if (a.Schema['x-group'] < b.Schema['x-group']) return -1
			if (a.Schema['x-group'] > b.Schema['x-group']) return 1

			if ((a.Schema.title || a.Name) < (b.Schema.title || b.Name)) return -1
			if ((a.Schema.title || a.Name) > (b.Schema.title || b.Name)) return 1

			return 0
		})

		let prevGroup = null

		for (const t of this.types) {
			if (t.Name == 'root_node')
				continue

			if (t.Schema['x-group'] != prevGroup) {
				prevGroup = t.Schema['x-group']
				const group = document.createElement('div')
				group.classList.add('group')
				group.innerText = t.Schema['x-group']
				div.appendChild(group)
			}

			const tDiv = document.createElement('div')
			tDiv.classList.add('type')
			tDiv.innerHTML = `
				<div class="img"><img src="/images/${_VERSION}/node_modules/@mdi/svg/svg/file-document-check-outline.svg" /></div>
				<div class="title">${t.Schema.title || t.Name}</div>
				`

			div.appendChild(tDiv)
		}

		return div
	}// }}}
}

// vim: foldmethod=marker
