import {LoadLevelDialog} from "../../js/ui/LoadLevelDialog.js";
import GraphicSet from "../../js/GraphicSet.js";
import Setting from "../../js/Setting.js";
import Brush from "./Brush.js";
import Tileset from "./Tileset.js";
import WidgetBar from "./menu/WidgetBar.js";
import TilesetPickerWidget from "./menu/TilesetPickerWidget.js";
import BackgroundPickerWidget from "./menu/BackgroundPickerWidget.js";
import EntrancePointWidget from "./menu/EntrancePointWidget.js";
import TargetPointWidget from "./menu/TargetPointWidget.js";
import Mouse from "./Mouse.js";
import MainMenu from "./menu/MainMenu.js";
import MenuGroup from "./menu/MenuGroup.js";
import MainMenuEntry from "./menu/MainMenuEntry.js";
import TilorswiftMenuNewTerrainClickedEvent from "./events/TilorswiftMenuNewTerrainClickedEvent.js";
import {TilorswiftMenuOpenCLickedEvent} from "./events/TilorswiftMenuOpenCLickedEvent.js";
import TilorswiftMenuSaveClickedEvent from "./events/TilorswiftMenuSaveClickedEvent.js";
import TilorswiftAddRowsClickedEvent from "./events/TilorswiftAddRowsClickedEvent.js";
import TilorswiftAddColumnsClickedEvent from "./events/TilorswiftAddColumnsClickedEvent.js";
import TilorswiftMenuGravityClickedEvent from "./events/TilorswiftMenuGravityClickedEvent.js";
import TilorswiftMenuEffectsClickedEvent from "./events/TilorswiftMenuEffectsClickedEvent.js";
import TilorswiftEvent from "./events/TilorswiftEvent.js";
import BrushMode from "./BrushMode.js";
import DialogNewTerrain from "./dialog/DialogNewTerrain.js";
import DialogGravity from "./dialog/DialogGravity.js";
import DialogEffects from "./dialog/DialogEffects.js";
import DialogAddRows from "./dialog/DialogAddRows.js";
import DialogAddColumns from "./dialog/DialogAddColumns.js";
import Terrain from "./Terrain.js";
import TilorswiftSavedEvent from "./events/TilorswiftSavedEvent.js";
import Level from "../../js/Level.js";
import {IntelligentBrushSwitch} from "./menu/IntelligentBrushSwitch.js";
import {SnowEffect} from "../../js/effects/SnowEffect.js";
import {RainEffect} from "../../js/effects/RainEffect.js";
import {ThunderstormEffect} from "../../js/effects/ThunderstormEffect.js";
import {FullscreenEffectFactory} from "../../js/effects/FullscreenEffectFactory.js";

export default class Tilorswift
{
    static EFFECT_NAMES = {
        [SnowEffect.NAME]: 'Schnee',
	    [RainEffect.NAME]: 'Regen',
	    [ThunderstormEffect.NAME]: 'Gewitter',
    }

    constructor(level) {
        this.level = level;
        this.map = document.getElementById('map');
        this.brush = new Brush();
        this.tileset = new Tileset(this.level.terrain.tileset.setId);
        this.widgetBar = new WidgetBar('widget-bar');
        this.tilesetPicker = new TilesetPickerWidget(this.tileset, this.brush);
        this.widgetBar.addWidget(this.tilesetPicker);
        this.intelligentBrushSwitch = new IntelligentBrushSwitch(this.tilesetPicker, this.brush);
        this.widgetBar.addWidget(this.intelligentBrushSwitch);
        this.backgroundPicker = new BackgroundPickerWidget(this.tileset, this.brush);
        this.widgetBar.addWidget(this.backgroundPicker);
        this.entrancePicker = new EntrancePointWidget(this.widgetBar, this.brush);
        this.widgetBar.addWidget(this.entrancePicker);
        this.targetPicker = new TargetPointWidget(this.widgetBar, this.brush);
        this.widgetBar.addWidget(this.targetPicker);
        this.mouse = new Mouse();
        this.mainbar = new MainMenu('mainbar');
    }

    init()
    {
        document.body.appendChild(this.widgetBar.getElement());

        const menuFile = new MenuGroup('Datei');
        menuFile.addMenuEntry(new MainMenuEntry('Neu...', TilorswiftMenuNewTerrainClickedEvent));
        menuFile.addMenuEntry(new MainMenuEntry('Öffnen...', TilorswiftMenuOpenCLickedEvent));
        menuFile.addMenuEntry(new MainMenuEntry('Speichern...', TilorswiftMenuSaveClickedEvent));
        this.mainbar.addMenuGroup(menuFile);

        const menuEdit = new MenuGroup('Bearbeiten');
        menuEdit.addMenuEntry(new MainMenuEntry('Zeilen einfügen...', TilorswiftAddRowsClickedEvent));
        menuEdit.addMenuEntry(new MainMenuEntry('Spalten einfügen...', TilorswiftAddColumnsClickedEvent));
        this.mainbar.addMenuGroup(menuEdit);

        const menuLevel = new MenuGroup('Level');
        menuLevel.addMenuEntry(new MainMenuEntry('Gravitation...', TilorswiftMenuGravityClickedEvent));
	    menuLevel.addMenuEntry(new MainMenuEntry('Effekte...', TilorswiftMenuEffectsClickedEvent));
        this.mainbar.addMenuGroup(menuLevel);

        document.body.appendChild(this.mainbar.getElement());

        this.addEventListeners();
    }

    getLevelAsJson()
    {
        let matrix = [];

        for (let y = 0; y < this.level.terrain.fields.length; y++) {
            let row = [];

            for (let x = 0; x < this.level.terrain.fields[y].length; x++) {
                row.push(this.level.terrain.fields[y][x].index);
            }

            matrix.push(row);
        }

        let data = {
            tileset: this.level.getTilesetId(),
            rows: this.level.getRows(),
            columns: this.level.getColumns(),
            startX: this.level.getStartX(),
            startY: this.level.getStartY(),
            targetX: this.level.getTargetX(),
            targetY: this.level.getTargetY(),
            gravity: this.level.gravity,
            matrix: matrix,
	        effects: this.level.fullscreenEffects.map((effect) => {return effect.getName()}),
        };

        return JSON.stringify(data);
    }

    openLevelFromFile()
    {
        const dialog = new LoadLevelDialog();
        dialog.onLoad = (json) => {
            this.tileset = new Tileset(JSON.parse(json).tileset);
            this.level = Level.createFromJson(json);
            this.loadLevel();
            dialog.close();
        }
        dialog.openFileBrowser();
    }

    loadLevel()
    {
        this.tileset = new Tileset(this.level.terrain.tileset.setId);

        document.body.style.backgroundColor = this.level.getBackgroundColor();

        if (GraphicSet[this.level.terrain.tileset.setId].backgroundImage !== null) {
            document.body.style.backgroundImage = 'url("../' + Setting.GRAPHICS_LOCATION + GraphicSet[this.level.terrain.tileset.setId].backgroundImage + '")';
        } else {
            document.body.style.backgroundImage = 'none';
        }

        this.map.innerHTML = '';
        this.map.appendChild(this.level.terrain.getElement());
        this.tilesetPicker.reloadTileset(this.tileset);
        this.backgroundPicker.reloadTileset(this.tileset);

        this.initializeIntelligentBrushWidget();
    }

    saveLevelToFile()
    {
        if (!this.level.hasEntrancePoint()) {
            alert('Es muss ein Startpunkt definiert sein!');
            return false;
        }

        if (!this.level.hasTargetPoint()) {
            alert('Es muss ein Zielpunkt definiert sein!');
            return false;
        }

        const filename = prompt('Dateiname', 'terrain.json');

        if (filename === null) {
            return false;
        }

        const json = this.getLevelAsJson(level);
        const download = document.createElement('a');

        download.setAttribute('download', filename);
        download.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(json));
        download.click();
        download.remove();

        return true;
    }

    initializeIntelligentBrushWidget()
    {
        if (this.tileset.hasExtendedTiles()) {
            this.intelligentBrushSwitch.enable();
            this.intelligentBrushSwitch.switchOn();
        } else {
            this.intelligentBrushSwitch.switchOff();
            this.intelligentBrushSwitch.disable();
        }
    }

    createNewTerrain(tilesetIndex, tilesX, tilesY)
    {
        this.tileset = new Tileset(tilesetIndex);
        this.level.terrain = new Terrain(this.tileset, tilesX, tilesY, GraphicSet[tilesetIndex].backgroundColor);
        this.level.setGravity(GraphicSet[tilesetIndex].gravity);
        document.body.style.backgroundColor = this.level.getBackgroundColor();

        if (GraphicSet[tilesetIndex].backgroundImage !== null) {
            document.body.style.backgroundImage = 'url("../' + Setting.GRAPHICS_LOCATION + GraphicSet[tilesetIndex].backgroundImage + '")';
        } else {
            document.body.style.backgroundImage = 'none';
        }

        this.map.innerHTML = '';
        this.map.appendChild(this.level.terrain.getElement());

        this.tilesetPicker.reloadTileset(this.tileset);
        this.backgroundPicker.reloadTileset(this.tileset);

        this.initializeIntelligentBrushWidget();
    }

    addTerrain(field)
    {
        if (this.brush.isIntelligent) {
            const index = this.level.terrain.brushTileIndex + this.tileset.primaryTiles * this.tileset.getTileIndexFactor(
                this.level.terrain.getFieldNeighbourCode(field)
            );

            field.setIndex(index);

            this.updateNeighbours(field);
        } else {
            field.setIndex(this.level.terrain.brushTileIndex);
        }
    }

    addBackground(field)
    {
        field.setIndex(this.level.terrain.brushTileIndex);

        this.updateNeighbours(field);
    }

    updateNeighbours(field)
    {
        for (const neighbour of this.level.terrain.getFieldNeighbours(field)) {
            if (neighbour.index <= -1) {
                continue;
            }

            const neighbourIndex = (neighbour.index % this.tileset.primaryTiles) + this.tileset.primaryTiles * this.tileset.getTileIndexFactor(
                this.level.terrain.getFieldNeighbourCode(neighbour)
            );

            neighbour.setIndex(neighbourIndex);
        }
    }

    removeTerrain(field)
    {
        field.setIndex(-1);

        if (this.brush.isIntelligent) {
            for (const neighbour of this.level.terrain.getFieldNeighbours(field)) {
                this.updateNeighbours(field);
            }
        }
    }

    addEventListeners()
    {
        window.addEventListener(
            TilorswiftEvent.FIELD_CLICKED,
            (event) => {
                if (this.brush.mode === BrushMode.TERRAIN && !event.getField().isEntrancePoint) {
                    switch (event.button) {
                        case 0:
                            this.addTerrain(event.getField());
                            break;
                        case 2:
                            this.removeTerrain(event.getField());
                            break;
                    }
                } else if (this.brush.mode === BrushMode.BACKGROUND && !event.getField().isEntrancePoint) {
                    switch (event.button) {
                        case 0:
                            this.addBackground(event.getField());
                            break;
                        case 2:
                            this.removeTerrain(event.getField());
                            break;
                    }
                } else if (this.brush.mode === BrushMode.ENTRANCE) {
                    if (event.getField().index < 0) {
                        const coordinates = this.level.terrain.getFieldCoordinates(event.getField());
                        this.level.terrain.setEntrancePoint(coordinates.x, coordinates.y);
                        this.brush.mode = BrushMode.TERRAIN;
                        this.widgetBar.enableWidgets();
                    }
                }  else if (this.brush.mode === BrushMode.EXIT) {
                    if (event.getField().index < 0) {
                        const coordinates = this.level.terrain.getFieldCoordinates(event.getField());
                        this.level.terrain.setTargetPoint(coordinates.x, coordinates.y);
                        this.brush.mode = BrushMode.TERRAIN;
                        this.widgetBar.enableWidgets();
                    }
                }
            }
        );

        window.addEventListener(
            'contextmenu',
            (event) => {
                event.preventDefault();
            }
        );

        window.addEventListener(
            TilorswiftEvent.FIELD_ENTERED,
            (event) => {
                if (this.mouse.isPressedLeft) {
                    switch (this.brush.mode) {
                        case BrushMode.TERRAIN:
                            this.addTerrain(event.getField());
                            break;
                        case BrushMode.BACKGROUND:
                            this.addBackground(event.getField());
                            break;
                    }
                } else if (this.mouse.isPressedRight) {
                    event.getField().setIndex(-1);
                }
            }
        );

        window.addEventListener(
            TilorswiftEvent.NEW_TERRAIN_CLICKED,
            () => {
                new DialogNewTerrain();
            }
        );

        window.addEventListener(
            TilorswiftEvent.MENU_GRAVITY_CLICKED,
            () => {
                new DialogGravity(this.level.gravity);
            }
        );

	window.addEventListener(
            TilorswiftEvent.MENU_EFFECTS_CLICKED,
	    () => {
	    	const effects = [];

		for (const effect of this.level.fullscreenEffects) {
			effects.push(effect.getName());
		}

		new DialogEffects(
			FullscreenEffectFactory.getNames(),
			effects,
			Tilorswift.EFFECT_NAMES,
		);
	    }
	);

        window.addEventListener(
            TilorswiftEvent.ADD_ROWS_CLICKED,
            () => {
                new DialogAddRows();
            }
        );

        window.addEventListener(
            TilorswiftEvent.ADD_COLUMNS_CLICKED,
            () => {
                new DialogAddColumns();
            }
        );

        window.addEventListener(
            TilorswiftEvent.GRAVITY_UPDATED,
            (event) => {
                this.level.gravity = event.gravity;
            }
        );

	window.addEventListener(
		TilorswiftEvent.EFFECTS_UPDATED,
		(event) => {
			this.level.fullscreenEffects = [];

			const effectFactory = new FullscreenEffectFactory();

			for (const effectName of event.effectNames) {
				this.level.fullscreenEffects.push(
					effectFactory.getEffect(effectName)
				);
			}
		}
	);

        window.addEventListener(
            TilorswiftEvent.ADD_ROWS,
            (event) => {
                this.level.terrain.addRows(event.beforeRow, event.rowCount);
            }
        );

        window.addEventListener(
            TilorswiftEvent.ADD_COLUMNS,
            (event) => {
                this.level.terrain.addColumns(event.beforeColumn, event.columnCount);
            }
        );

        window.addEventListener(
            TilorswiftEvent.NEW_TERRAIN,
            (event) => {
                this.createNewTerrain(event.tilesetIndex, event.tilesX, event.tilesY);
            }
        );

        /* Prevents Firefox's annoying default drag and drop behavior for images */
        document.addEventListener(
            'dragstart',
            (event) => {
                event.preventDefault();
            }
        );

        window.addEventListener(
            TilorswiftEvent.MENU_OPEN_CLICKED,
            () => {
                this.openLevelFromFile();
            }
        );

        window.addEventListener(
            TilorswiftEvent.MENU_SAVE_CLICKED,
            () => {
                if (this.saveLevelToFile()) {
                    window.dispatchEvent(new TilorswiftSavedEvent());
                }
            }
        );
    }
}
