<template>
	<div class="map" :style="{'background-color': mapBackground }">
		<MapLayer v-for="[name, map] in maps" :key="name" :map="map" :name="name" :leaflet="leaflet"></MapLayer>
		<PlayersLayer v-if="playerMarkersEnabled" :leaflet="leaflet"></PlayersLayer>
		<MarkerSetLayer v-for="[name, markerSet] in markerSets" :key="name" :markerSet="markerSet" :leaflet="leaflet"></MarkerSetLayer>

		<LogoControl v-for="(logo, index) in logoControls" :key="index" :options="logo" :leaflet="leaflet"></LogoControl>
		<CoordinatesControl v-if="coordinatesControlEnabled" :leaflet="leaflet"></CoordinatesControl>
		<LinkControl v-if="linkControlEnabled" :leaflet="leaflet"></LinkControl>
		<ClockControl v-if="clockControlEnabled" :leaflet="leaflet"></ClockControl>
	</div>
</template>

<script lang="ts">
import {computed, defineComponent} from "@vue/runtime-core";
import {CRS, LatLng} from 'leaflet';
import {useStore} from '@/store';
import MapLayer from "@/components/map/layer/MapLayer.vue";
import PlayersLayer from "@/components/map/layer/PlayersLayer.vue";
import MarkerSetLayer from "@/components/map/layer/MarkerSetLayer.vue";
import CoordinatesControl from "@/components/map/control/CoordinatesControl.vue";
import ClockControl from "@/components/map/control/ClockControl.vue";
import LinkControl from "@/components/map/control/LinkControl.vue";
import LogoControl from "@/components/map/control/LogoControl.vue";
import {MutationTypes} from "@/store/mutation-types";
import {Coordinate, DynmapPlayer} from "@/dynmap";
import {ActionTypes} from "@/store/action-types";
import DynmapMap from "@/leaflet/DynmapMap";
import {LoadingControl} from "@/leaflet/control/LoadingControl";

export default defineComponent({
	components: {
		MapLayer,
		PlayersLayer,
		MarkerSetLayer,
		CoordinatesControl,
		ClockControl,
		LinkControl,
		LogoControl
	},

	setup() {
		const store = useStore(),
			leaflet = undefined as DynmapMap | undefined,

			maps = computed(() => store.state.maps),
			markerSets = computed(() => store.state.markerSets),
			configuration = computed(() => store.state.configuration),

			playerMarkersEnabled = computed(() => store.getters.playerMarkersEnabled),
			coordinatesControlEnabled = computed(() => store.getters.coordinatesControlEnabled),
			clockControlEnabled = computed(() => store.getters.clockControlEnabled),
			linkControlEnabled = computed(() => store.state.components.linkControl),
			logoControls = computed(() => store.state.components.logoControls),

			currentWorld = computed(() => store.state.currentWorld),
			currentMap = computed(() => store.state.currentMap),
			currentProjection = computed(() => store.state.currentProjection),
			following = computed(() => store.state.following),
			mapBackground = computed(() => store.getters.mapBackground);

		return {
			leaflet,
			maps,
			markerSets,
			configuration,
			playerMarkersEnabled,
			coordinatesControlEnabled,
			clockControlEnabled,
			linkControlEnabled,
			logoControls,
			following,
			mapBackground,
			currentWorld,
			currentMap,
			currentProjection
		}
	},

	watch: {
		following: {
			handler(newValue, oldValue) {
				if (newValue) {
					this.updateFollow(newValue, !oldValue || newValue.account !== oldValue.account);
				}
			},
			deep: true
		},
		currentWorld(newValue, oldValue) {
			const store = useStore();

			if(newValue) {
				let location: Coordinate;
				let zoom: number;

				store.dispatch(ActionTypes.GET_MARKER_SETS, undefined);

				if(oldValue || !store.state.parsedUrl.location) {
					location = newValue.center;
				} else {
					location = store.state.parsedUrl.location;
				}

				if(!oldValue) {
					zoom = store.state.parsedUrl.zoom || store.state.configuration.defaultZoom;
				}

				//Delay the pan a frame, to allow the projection to be updated by the new world
				requestAnimationFrame(() => {
					this.leaflet!.panTo(this.currentProjection.locationToLatLng(location), {
						animate: false,
						noMoveStart: true,
					});

					this.leaflet!.setZoom(zoom, {
						animate: false,
					});
				});
			}
		},
		configuration: {
			handler(newValue) {
				if(this.leaflet) {
					this.leaflet.setZoom(newValue.defaultZoom, {
						animate: false,
						noMoveStart: true,
					});
				}
			},
			deep: true,
		},
	},

	mounted() {
		this.leaflet = new DynmapMap(this.$el, Object.freeze({
			zoom: this.configuration.defaultZoom,
			center: new LatLng(0, 0),
			fadeAnimation: false,
			zoomAnimation: true,
			zoomControl: true,
			layerControl: true,
			preferCanvas: true,
			attributionControl: false,
			crs: CRS.Simple,
			worldCopyJump: false,
			// markerZoomAnimation: false,
		}));

		this.leaflet.addControl(new LoadingControl({
			position: 'topleft',
			delayIndicator: 500,
		}));

		this.leaflet.on('moveend', () => {
			useStore().commit(MutationTypes.SET_CURRENT_LOCATION, this.currentProjection.latLngToLocation(this.leaflet!.getCenter(), 64));
		});

		this.leaflet.on('zoomend', () => {
			useStore().commit(MutationTypes.SET_CURRENT_ZOOM, this.leaflet!.getZoom());
		});
	},

	methods: {
		updateFollow(player: DynmapPlayer, newFollow: boolean) {
			const store = useStore(),
				currentWorld = store.state.currentWorld;

			if(!this.leaflet) {
				console.warn('Map isn\'t initialized yet. Ignoring follow');
			}

			if(!player.location.world) {
				console.error('Player isn\'t in a world somehow');
				return;
			}

			if(!currentWorld || currentWorld.name !== player.location.world) {
				const followMapName = store.state.configuration.followMap,
					world = store.state.worlds.get(player.location.world);

				if(!world) {
					console.error('Player isn\'t in a known world somehow');
					return;
				}

				let map = followMapName && world.maps.has(followMapName)
					? world.maps.get(followMapName)
					: world.maps.entries().next().value[1]

				if(map !== store.state.currentMap) {
					console.log(`Switching map to match player ${world.name} ${map.name}`);
					store.commit(MutationTypes.SET_CURRENT_MAP, {worldName: world.name, mapName: map.name});
				}
			}

			this.leaflet!.panTo(store.state.currentProjection.locationToLatLng(player.location));

			if(newFollow) {
				console.log(`Setting zoom for new follow ${store.state.configuration.followZoom}`);
				this.leaflet!.setZoom(store.state.configuration.followZoom);
			}
		}
	}
})
</script>

<style scoped>
	.map {
		width: 100%;
		height: 100%;
		background: #000;
		z-index: 0;
	}
</style>