import GameAssets from '../classes/Assets';
import { Engine } from '../classes/Bastion';
import { MissionDefinition } from '../classes/Definitions';
import Creep from '../classes/game/Creep';
import { Grid } from '../classes/game/Grid';
import WaveManager from '../classes/game/WaveManager';
import { WaveManagerEvents, CreepEvents, GemEvents } from '../classes/Events';
import Sidebar from '../classes/gui/Sidebar';
import Button, { ButtonTexture } from '../classes/gui/Button';
import Scene from './Scene';
import * as PIXI from 'pixi.js';
import MissionStats from '../classes/game/MissionStats';
import TowerManager from '../classes/game/TowerManager';
import { MissionPickerScene } from './MissionPicker';
import GameUIConstants from '../classes/GameUIConstants';
import Tooltip from '../classes/gui/Tooltip';
import TowerPanel, { VisualGemSlot } from '../classes/gui/TowerPanel';
import Gem from '../classes/game/Gem';
import EndGameDialog from '../classes/gui/EndGameDialog';
import HighScoreDialog, { HighScoreDialogButtons } from '../classes/gui/HighScoreDialog';
import GamePausedDialog from '../classes/gui/GamePausedDialog';

export enum RoundMode {
    Purchase = 0,
    Combat = 1,
    Misc = 2,
}

export class GameScene extends Scene {
    public isGameOver: boolean = false;
    public mission: MissionDefinition;
    public missionIndex: number;
    public MissionStats: MissionStats;
    public roundMode: RoundMode;
    public ticker: PIXI.Ticker;
    public changeRoundButton: Button;
    public sidebar: Sidebar;
    public tooltip: Tooltip;
    public towerPanel: TowerPanel;
    public isPaused: boolean = false;
    public gameSpeedMultiplier: number = 1;

    private pauseButton: Button;
    private visualGems: VisualGemSlot[] = [];
    private currentRound: number = 0;
    private isWaveManagerFinished: boolean = false;
    private playerWon: boolean = false;
    private destroyTicker: boolean = false;
    private offerGemsSprite: PIXI.NineSliceSprite;
    private dimGraphics: PIXI.Graphics = new PIXI.Graphics({
        x: 0,
        y: 0,
        zIndex: 120,
    });
    private windowTitle: string;

    constructor(name: string) {
        super();
        Engine.GameScene = this;
        this.windowTitle = document.title;
        GameAssets.Missions.forEach((mission, index) => {
            if (mission.name == name) {
                this.mission = mission;
                this.missionIndex = index;
            }
        });
    }
    public init() {
        Engine.latestGemId = 0;
        new Grid(this.mission.gameMap, this.missionIndex);
        new TowerManager();
        new WaveManager(this.mission.rounds, this.mission.gameMap.paths);
        Engine.Grid.onGridCellClicked = (row, column) => {
            if (Engine.TowerManager.isPlacingTower) {
                Engine.TowerManager.PlayerClickOnGrid(row, column);
            }
        };
        Engine.WaveManager.events.on(WaveManagerEvents.CreepSpawned, (creep: Creep) => {
            Engine.Grid.addCreep(creep);
            creep.events.on(CreepEvents.Escaped, () => {
                this.onCreepEscaped(creep);
            });
        });
        Engine.WaveManager.events.on(WaveManagerEvents.Finished, () => {
            this.isWaveManagerFinished = true;
        });
        this.events.on(CreepEvents.Died, (playerAward, creepThatDied) => {
            this.MissionStats.damageDealt += playerAward;
            this.MissionStats.creepsKilled++;
            this.MissionStats.earnGold(playerAward);
        });
        this.towerPanel = new TowerPanel(GameUIConstants.SidebarRect);
        this.sidebar = new Sidebar(GameUIConstants.SidebarRect);
        this.changeRoundButton = new Button(GameUIConstants.ChangeRoundButtonRect, '', ButtonTexture.Button01, true);
        this.changeRoundButton.container.removeFromParent();
        this.sidebar.container.addChild(this.changeRoundButton.container);
        Engine.GameMaster.currentScene.stage.addChildAt(this.dimGraphics, 0);
        this.tooltip = new Tooltip(new PIXI.Rectangle(0, 0, 350, 160));
        // Added custom button logic to still keep all the regular events for the button, just have an icon instead of text.
        this.changeRoundButton.CustomButtonLogic = () => {
            this.changeRoundButton.buttonIcon = new PIXI.Sprite({
                texture: GameAssets.PlayIconTexture,
                x: this.changeRoundButton.container.width / 2,
                y: this.changeRoundButton.container.height / 2,
                scale: 0.2,
            });
            this.changeRoundButton.buttonIcon.anchor.set(0.5, 0.5);
            this.changeRoundButton.container.addChild(this.changeRoundButton.buttonIcon);
        };
        this.changeRoundButton.CustomButtonLogic();
        this.changeRoundButton.onClick = () => {
            if (this.playerWon) return this.ReturnToMain();
            if (this.roundMode == RoundMode.Combat) {
                if (this.gameSpeedMultiplier !== 1) {
                    this.UpdateGameSpeedMultiplier(1);
                } else {
                    this.UpdateGameSpeedMultiplier(2);
                }
                return;
            }
            if (this.roundMode == RoundMode.Misc) return;
            this.setRoundMode(RoundMode.Combat);
            this.changeRoundButton.buttonIcon.texture = GameAssets.FastForwardIconTexture;
            this.events.emit(WaveManagerEvents.NewWave, `${this.currentRound + 1}`);
        };
        this.MissionStats = new MissionStats(100, 75);
        this.events.on(GemEvents.TowerPanelSelectGem, (gem, index, tower) => {
            if (gem == null) {
                if (!this.MissionStats.checkIfPlayerHasAnyGems())
                    return Engine.NotificationManager.Notify(
                        'You require atleast 1 Gem in your inventory to slot it in a Gem slot.',
                        'warn'
                    );
            }
            this.sidebar.gemTab.TowerPanelSelectingGem(gem, index, tower);
        });

        this.pauseButton = new Button(new PIXI.Rectangle(5, 5, 120, 80), '', ButtonTexture.Button01, true);
        this.pauseButton.container.removeFromParent();
        this.stage.addChild(this.pauseButton.container);
        this.pauseButton.CustomButtonLogic = () => {
            this.pauseButton.buttonIcon = new PIXI.Sprite({
                texture: GameAssets.PauseIconTexture,
                x: this.pauseButton.container.width / 2,
                y: this.pauseButton.container.height / 2,
                scale: 0.2,
            });
            this.pauseButton.buttonIcon.anchor.set(0.5, 0.5);
            this.pauseButton.container.addChild(this.pauseButton.buttonIcon);
        };
        this.pauseButton.CustomButtonLogic();
        this.pauseButton.onClick = () => {
            if (this.isPaused) {
                this.UnpauseGame();
            } else {
                this.ShowPauseDialog();
                this.PauseGame();
            }
        };

        this.ticker = new PIXI.Ticker();
        this.ticker.maxFPS = 60;
        this.ticker.minFPS = 30;
        // fix tooltip behaving weirdly for some reason
        this.tooltip.SetContentTower(0, 0, 0, 0);
        this.tooltip.Show(Engine.MouseX, Engine.MouseY);
        this.tooltip.Hide();

        this.ticker.add(() => {
            if (this.update) this.update(this.ticker.elapsedMS);
        });
        this.ticker.start();
    }
    public update(elapsedMS) {
        if (this.isGameOver) {
            if (this.destroyTicker) {
                this.destroyTicker = false;
                this.ticker.destroy();
            }
            return;
        }
        Engine.WaveManager.update(elapsedMS);
        Engine.Grid.update(elapsedMS);
        Engine.TowerManager.update(elapsedMS);
        // Means the round is finished.
        if (this.isWaveManagerFinished && Engine.Grid.creeps.length == 0) {
            this.isWaveManagerFinished = false;
            this.setRoundMode(RoundMode.Purchase);
            this.changeRoundButton.buttonIcon.texture = GameAssets.PlayIconTexture;
            this.MissionStats.wavesSurvived++;
            Engine.NotificationManager.Notify(
                `Round ${this.currentRound + 1}/${this.mission.rounds.length} completed.`,
                'info'
            );
            if (this.currentRound + 1 == this.mission.rounds.length) {
                Engine.NotificationManager.Notify(`Mission victory!!`, 'reward');
                this.playerWon = true;
            } else {
                this.OfferPlayerGems();
                this.currentRound++;
            }
        }

        if (this.MissionStats.getHP() <= 0) {
            this.isGameOver = true;
            this.ShowEndgameDialog(true);
        } else if (this.playerWon) {
            this.isGameOver = true;
            this.ShowEndgameDialog(false);
        }
    }
    public DarkenScreen() {
        this.dimGraphics.rect(0, 0, Engine.app.canvas.width, Engine.app.canvas.height);
        this.dimGraphics.fill({ color: 0x000000, alpha: 0.5 });
    }
    public UndarkenScreen() {
        this.dimGraphics.clear();
    }
    private OfferPlayerGems() {
        Engine.Grid.gridInteractionEnabled = false;
        Engine.GameScene.sidebar.towerTab.resetTint();
        Engine.TowerManager.ResetChooseTower();
        this.setRoundMode(RoundMode.Misc);
        let gemsToOffer = this.mission.rounds[this.currentRound].offeredGems;
        this.DarkenScreen();
        this.offerGemsSprite = new PIXI.NineSliceSprite({
            width: 380,
            height: 150,
            texture: GameAssets.Frame01Texture,
            leftWidth: 100,
            topHeight: 100,
            rightWidth: 100,
            bottomHeight: 100,
            zIndex: this.dimGraphics.zIndex + 1,
            x: Engine.app.canvas.width / 2 - 190,
            y: Engine.app.canvas.height / 2 - 75,
        });
        Engine.GameMaster.currentScene.stage.addChildAt(this.offerGemsSprite, 0);
        let offerText = new PIXI.Text({
            x: Engine.app.canvas.width / 4,
            y: Engine.app.canvas.height / 4,
            zIndex: this.dimGraphics.zIndex + 1,
            text: 'Choose a Gem as your reward for beating this round!',
            style: {
                fontSize: 40,
                fill: 'orange',
                fontWeight: 'bold',
                stroke: {
                    color: 0x000000,
                    width: 5,
                },
            },
        });
        Engine.GameMaster.currentScene.stage.addChildAt(offerText, 0);
        gemsToOffer.forEach((gType, index) => {
            let _Gem = new Gem(gType, true);
            let vGem = new VisualGemSlot(0, Engine.app.stage, _Gem);
            this.visualGems.push(vGem);
            vGem.container.x = this.offerGemsSprite.x - 15 + 69 * (index + 1);
            vGem.container.y = this.offerGemsSprite.y + 40;
            vGem.container.onpointermove = () => {
                Engine.GameScene.tooltip.SetContentGem(_Gem);
                Engine.GameScene.tooltip.Show(Engine.MouseX, Engine.MouseY);
            };
            vGem.container.onpointerleave = () => {
                Engine.GameScene.tooltip.Hide();
            };
            vGem.onClick = () => {
                Engine.GameScene.tooltip.Hide();
                offerText.destroy();
                this.PlayerPickedGem(new Gem(gType));
            };
        });
    }
    private PlayerPickedGem(gem: Gem) {
        this.offerGemsSprite.destroy();
        this.UndarkenScreen();
        this.visualGems.forEach((item) => item.destroy());
        Engine.Grid.gridInteractionEnabled = true;
        this.MissionStats.giveGem(gem);
        this.setRoundMode(RoundMode.Purchase);
    }

    public PauseGame() {
        this.isPaused = true;
        this.ticker.stop();
        document.title = '[PAUSED] ' + this.windowTitle;
    }
    public UnpauseGame() {
        this.isPaused = false;
        this.ticker.start();
        document.title = this.windowTitle;
    }
    public ShowPauseDialog() {
        const gamePausedDialog = new GamePausedDialog();
        gamePausedDialog.show();
    }

    private async ShowEndgameDialog(lost) {
        const endGameDialog = new EndGameDialog(this.mission.name, this.MissionStats, lost);
        await endGameDialog.show();
        const highScore = new HighScoreDialog(
            this.mission.name,
            lost,
            !lost && this.missionIndex + 1 < GameAssets.Missions.length
        );
        const result = await highScore.show();
        if (result === HighScoreDialogButtons.MainMenu) {
            this.ReturnToMain();
        } else if (result === HighScoreDialogButtons.NextMission) {
            this.destroy();
            Engine.GameMaster.changeScene(new MissionPickerScene());
            Engine.GameMaster.changeScene(new GameScene(GameAssets.Missions[this.missionIndex + 1].name));
            Engine.NotificationManager.Notify('Loading next mission. Good luck.', 'green');
        } else if (result === HighScoreDialogButtons.Retry) {
            this.destroy();
            Engine.GameMaster.changeScene(new MissionPickerScene());
            Engine.GameMaster.changeScene(new GameScene(this.mission.name));
            Engine.NotificationManager.Notify('Retrying mission.', 'green');
        }
    }

    public onCreepEscaped(creep: Creep) {
        this.MissionStats.takeDamage(creep.health);
    }

    private setRoundMode(roundMode: RoundMode) {
        this.roundMode = roundMode;
        if (this.roundMode == RoundMode.Combat) {
            Engine.WaveManager.start(this.currentRound);
        } else {
            Engine.WaveManager.end();
        }
    }

    public destroy(): void {
        super.destroy();
        this.isGameOver = true;
        this.destroyTicker = true;
        Engine.GameScene = null;
    }

    private ReturnToMain() {
        Engine.GameMaster.changeScene(new MissionPickerScene());
    }

    private UpdateGameSpeedMultiplier(newMultiplier: number) {
        this.gameSpeedMultiplier = newMultiplier;
        if (newMultiplier === 1) Engine.NotificationManager.Notify('Regular speed.', 'info');
        else Engine.NotificationManager.Notify('Fast forward activated.', 'info');
    }
}
