/*
 * Copyright 2022 James Lyne
 *
 * Some portions of this file were taken from https://github.com/webbukkit/dynmap.
 * These portions are Copyright 2020 Dynmap Contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {ControlOptions, LeafletMouseEvent, Control, Map, DomUtil, Util} from 'leaflet';
import {useStore} from "@/store";
import {Coordinate} from "@/index";

const store = useStore();

export interface CoordinatesControlOptions extends ControlOptions {
	showY: boolean;
	showRegion: boolean;
	showChunk: boolean;
	label: string;
}

/**
 * Leaflet map control which displays in-game block coordinates when hovering over or tapping the map
 */
export class CoordinatesControl extends Control {
	declare options: CoordinatesControlOptions;
	declare _map ?: Map;

	private _location?: Coordinate;
	private _locationChanged: boolean = false;
	private readonly _coordsContainer: HTMLSpanElement;
	private readonly _regionContainer: HTMLSpanElement;
	private readonly _chunkContainer: HTMLSpanElement;

	constructor(options: CoordinatesControlOptions) {
		super(options);

		this._coordsContainer = DomUtil.create('span', 'value coordinates');
		this._chunkContainer = DomUtil.create('span', 'value chunk');
		this._regionContainer = DomUtil.create('span', 'value region');

		options.position = 'bottomleft';
		Util.setOptions(this, options);
	}

	onAdd(map: Map) {
		const container = DomUtil.create('div', 'leaflet-control-coordinates');

		this._coordsContainer.textContent = this.options.showY ? '-----, ---, -----' : '-----, -----';
		this._coordsContainer.dataset.label = this.options.label;
		container.appendChild(this._coordsContainer);

		if (this.options.showRegion) {
			this._regionContainer.textContent = '--------------';
			this._regionContainer.dataset.label = store.state.messages.locationRegion;
			container.appendChild(this._regionContainer);
		}

		if (this.options.showChunk) {
			this._chunkContainer.textContent = '----, ----';
			this._chunkContainer.dataset.label = store.state.messages.locationChunk;
			container.appendChild(this._chunkContainer);
		}

		map.on('mousemove', this._onMouseMove, this);
		map.on('mouseout', this._onMouseOut, this);

		return container;
	}

	remove() {
		if (!this._map) {
			return this;
		}

		this._map.off('mousemove', this._onMouseMove, this);
		this._map.off('mouseout', this._onMouseOut, this);
		super.remove();

		return this;
	}

	_onMouseMove(event: LeafletMouseEvent) {
		if (!this._map || !store.state.currentMap) {
			return;
		}

		this._location = store.state.currentMap.latLngToLocation(event.latlng, store.state.currentWorld!.seaLevel + 1);

		if(!this._locationChanged) {
			this._locationChanged = true;
			requestAnimationFrame(() => this._update());
		}
	}

	_onMouseOut() {
		if (!this._map) {
			return;
		}

		this._location = undefined;

		if(!this._locationChanged) {
			this._locationChanged = true;
			requestAnimationFrame(() => this._update());
		}
	}

	_update() {
		if (!this._map || !store.state.currentWorld || !this._locationChanged) {
			return;
		}

		this._locationChanged = false;

		if(!this._location) {
			if (this.options.showY) {
				this._coordsContainer.textContent = '-----, ---, -----';
			} else {
				this._coordsContainer.textContent = '-----, -----';
			}

			if (this.options.showRegion) {
				this._regionContainer.textContent = '--------------';
			}

			if (this.options.showChunk) {
				this._chunkContainer.textContent = '----, ----';
			}

			return;
		}

		const x = Math.round(this._location.x).toString().padStart(5, ' '),
			y = this._location.y.toString().padStart(3, ' '),
			z = Math.round(this._location.z).toString().padStart(5, ' '),
			regionX = Math.floor(this._location.x / 512).toString().padStart(3, ' '),
			regionZ = Math.floor(this._location.z / 512).toString().padStart(3, ' '),
			chunkX = Math.floor(this._location.x / 16).toString().padStart(4, ' '),
			chunkZ = Math.floor(this._location.z / 16).toString().padStart(4, ' ');

		if (this.options.showY) {
			this._coordsContainer.textContent = `${x}, ${y}, ${z}`;
		} else {
			this._coordsContainer.textContent = `${x}, ${z}`;
		}

		if (this.options.showRegion) {
			this._regionContainer.textContent = `r.${regionX}, ${regionZ}.mca`;
		}

		if (this.options.showChunk) {
			this._chunkContainer.textContent = `${chunkX}, ${chunkZ}`;
		}
	}
}