// import * as YAML from 'yaml'
import jsYaml from '/js/js-yaml.mjs'

class App {
	constructor() {//{{{
		window._app = this

		this.theme = ''
		this.themes = { default: Theme.default() }
		this.sections = []

		this.retrieveVersion()

		this.retrieveThemes()
		.then(()=>this.retrieveConfig())
		.then(config=>this.parseConfig(config))
		.then(()=>this.setTheme(this.theme))
		.then(()=>this.renderPage())
		.catch(alert)
	}//}}}
	async retrieveThemes() {//{{{
		return fetch(`/themes.yaml`)
		.then(res=>{
			if(res.ok)
				return res.text()
			throw `Error when fetching /theme.yaml: ${res.status} ${res.statusText}`
		})
		.then(text=>{
			let themes = jsYaml.load(text)
			Object.keys(themes).forEach(themeName=>{
				this.themes[themeName] = new Theme(themeName, themes[themeName])
			})
		})
	}//}}}
	retrieveVersion() {//{{{
		return fetch('/VERSION', {cache: "no-store"})
		.then(res=>{
			if(res.ok)
				return res.text()
			throw `Error when fetching /${page}.yaml: ${res.status} ${res.statusText}`
		})
		.then(version=>{
			this.version = version
			document.querySelector('footer').innerHTML = `Dashie ${version}`
		})
	}//}}}
	retrieveConfig() {//{{{
		const urlParams = new URLSearchParams(window.location.search);
		let page = urlParams.get('page')
		if(page === null)
			page = 'start'

		return fetch(`/${page}.yaml`, {cache: "no-store"})
		.then(res=>{
			if(res.ok)
				return res.text()
			throw `Error when fetching /${page}.yaml: ${res.status} ${res.statusText}`
		})
		.then(text=>{
			return jsYaml.load(text)
		})
	}//}}}
	parseConfig(config) {//{{{
		// Pages to switch between
		this.pages = config.pages ? config.pages : []

		// Settings for this page.
		this.page = config.page

		// Theme to be used for the initial page render.
		// Can be changed later.
		this.theme = this.themes[config.page.theme ? config.page.theme : 'default']
		if(this.theme === undefined)
			throw new Error(`Theme '${config.page.theme}' isn't defined in themes.yaml.`)

		// Sections are created; items are created within the Section objects.
		if(config.sections === undefined)
			config.sections = []
		this.sections = config.sections.map((d,idx)=>new Section(idx, d))
	}//}}}
	renderPage() {//{{{
		// Page header
		document.querySelector('header').style.backgroundColor = this.theme.colors.page.header

		// Pages to switch between
		let pageStyle = `color: ${this.theme.colors.page_select.text}; background: ${this.theme.colors.page_select.background}`
		let pages = this.pages.map(page=>`<div class="page" style="${pageStyle}" onclick="window.open('/?page=${page.name}', '_self')">${page.label}</div>`)
		document.getElementById('pages').innerHTML = pages.join('')

		// Page label
		let pageLabel = document.getElementById('page-label')
		pageLabel.innerHTML = this.page.label
		pageLabel.style.color = this.theme.colors.page.text

		// The theme selector is sorted by name for easier usage.
		let themeOptions = Object.keys(this.themes)
		.sort((a, b)=>{
			if(a < b) return -1
			if(a > b) return  1
			return 0
		})
		.map(themeName=>`<option value="${themeName}" ${this.theme.name == themeName ? 'selected' : ''}>${themeName}</option>`)
		.join('')

		// Theme selector also has prev and next buttons to
		// faster switch between themes (probably trying them out).
		let t = this.theme
		let themes = document.getElementById('theme')
		let style = `color: ${t.colors.theme_select.text}; background: ${t.colors.theme_select.background}; border: 1px solid ${t.colors.theme_select.border}`
		themes.innerHTML = `
			<button style="${style}" onclick="window._app.previousTheme()">&lt;</button>
			<select id="theme-select" onchange="window._app.selectTheme()" style="${style}">
				${themeOptions}
			</select>
			<button style="${style}" onclick="window._app.nextTheme()">&gt;</button>
			`

		let sectionHTML = this.sections.map(s=>s.html())
		document.getElementById('sections').innerHTML = sectionHTML.join('')

		// Footer
		let footer = document.querySelector('footer')
		footer.style.display = t.colors.page.footer.show ? 'block' : 'none';
		footer.style.color = t.colors.page.footer.text
		footer.style.backgroundColor = t.colors.page.footer.background
	}//}}}
	setTheme(theme) {//{{{
		this.theme = theme
		document.body.style.color = theme.colors.page.text
		document.body.style.background = theme.colors.page.background

		let sectionClasses = document.getElementById('sections').classList
		sectionClasses.remove('theme-brighter')
		sectionClasses.remove('theme-darker')
		if(theme.hover == 'brighter')
			sectionClasses.add('theme-brighter')
		else if(theme.hover == 'darker')
			sectionClasses.add('theme-darker')
	}//}}}
	selectTheme() {//{{{
		let option = document.querySelector('#theme option:checked')
		this.setThemeByName(option.value)

		// Focus the theme select box after page render, since
		// user could be trying themes out and wanting to use the
		// keyboard up/down.
		document.querySelector('#theme select').focus()
	}//}}}
	setThemeByName(name) {//{{{
		this.setTheme(this.themes[name])
		this.renderPage()
	}//}}}
	previousTheme() {//{{{
		let selected = document.querySelector('#theme option:checked')
		if(!selected)
			return
		let prevOption = selected.previousElementSibling
		if(prevOption) {
			this.setThemeByName(prevOption.value)
		}
	}//}}}
	nextTheme() {//{{{
		let selected = document.querySelector('#theme option:checked')
		if(!selected)
			return
		let nextOption = selected.nextElementSibling
		if(nextOption) {
			this.setThemeByName(nextOption.value)
		}
	}//}}}
	open(url) {//{{{
		window.open(url, '_blank')
	}//}}}
}

class Theme {
	static default() {//{{{
		return new Theme('default', {
			page: {
				text: '#fff',
				header: '#333',
				background: '#fff',
				footer: {
					show: true,
					text: "#aaa",
					background: "#eee"
				}
			},

			page_select: {
				text: "#ccc",
				background: "#666"
			},

			theme_select: {
				text: "#ccc",
				background: "#333",
				border: "#888"
			},

			section: {
				background: '#fff',
				borders: [
					['#fff', '#22a511']
				]
			},

			item: {
				label: '#333',
				background: '#eaeaea',
				description: '#555',
				url: '#666',
				icon: '#000',
				hover: 'darker'
			}
		})
	}//}}}
	constructor(name, data) {//{{{
		this.name = name
		this.hover = data.item.hover
		this.colors = data
	}//}}}
	colorNo(i) {//{{{
		// Returns a wrapping color from the list of 
		let idx = i % this.colors.section.borders.length
		return this.colors.section.borders[idx]
	}//}}}
}

class Section {
	constructor(idx, data) {//{{{
		this.section_index = idx
		this.label = 'Section'
		this.icon = 'head-question'
		this.items = []

		Object.keys(data).forEach(key=>
			this[key] = data[key]
		)
	}//}}}
	html() {//{{{
		let theme = window._app.theme
		let colorIndex = this.color ? this.color : this.section_index
		let color = theme.colorNo(colorIndex)

		let itemHTML = this.items.map(itemData=>
			new Item(this, itemData).html()
		)

		return `
			<div class="section" style="border: 4px solid ${color[0]}; background: ${theme.colors.section.background}; box-shadow: ${theme.colors.section.shadow}">
				<div class="name" style="background: ${color[0]}; color: ${color[1]}">${this.label}</div>
				<div class="items" style="background: ${theme.colors.section.background}">
					${itemHTML.join('')}
				</div>
			</div>
		`
	}//}}}
}

class Item {
	constructor(section, data) {//{{{
		this.section = section
		this.label = 'Item'
		this.icon = 'head-question'
		this.url = 'https://example.com'

		Object.keys(data).forEach(key=>
			this[key] = data[key]
		)
	}//}}}
	html() {//{{{
		let theme = window._app.theme
		let colorIndex = this.section.color ? this.section.color : this.section.section_index
		let sectionColor = theme.colorNo(colorIndex)[0]

		let colorLabel = theme.colors.item.label
		if(!colorLabel)
			colorLabel = sectionColor

		let colorIcon = theme.colors.item.icon
		if(!colorIcon)
			colorIcon = sectionColor

		let description = ''
		if(this.description)
			description = `<div class="description" style="color: ${theme.colors.item.description}">${this.description}</div>`

		return `
			<div class="item" style="background: ${theme.colors.item.background}" onclick="window._app.open('${this.url}')">
				<div class="icon mdi-set mdi-${this.icon}" style="color: ${colorIcon}"></div>
				<div class="label" style="color: ${colorLabel}">${this.label}</div>
				${description}
				<div class="url" style="color: ${theme.colors.item.url}">${this.url}</div>
			</div>
		`
	}//}}}
}

let app = new App()

// vim: foldmethod=marker
