var GameManagerStatics = {DrawableUpdatePaused:false,instance:null};


class GameManager {

    constructor()
    {
        GameManagerStatics.instance = this;
        this.ready = false;
        this.timeScale = 1;
        
        this.gameFramesPerRealFrame = 1;
        this.nextGameFramesPerRealFrame = null; //int?
        this.forceFrameTime = null;             //number?
        this.onMainLoopException = new Action();

        this.frameTime = 0;
    }

    init(jsRoot, gameClass)
    {
        this.gameClass = gameClass;
        EngineSettings.AssetRoot = nw ? "shared/images/" : "images/";
        EngineSettings.Pixi.ticker.add(delta => { this.lastFrameTime = performance.now(); this.update(delta); });

        //Engine.GraphicFactoryInstance.addGraphicData(new Engine.GraphicDef("ForestTiles","images/ForestTiles.json"));

        this.loadCount = 0;
        this.expectedLoadCount = 0;

        /*
        Engine.GraphicFactoryInstance.loadGraphicDataFromURL("images/championsUI.json", () => { this.UILoaded(); }); this.expectedLoadCount++;
        Engine.GraphicFactoryInstance.loadGraphicDataFromURL("images/fusionUI.json", () => { this.UILoaded(); }); this.expectedLoadCount++;

        Engine.GraphicFactoryInstance.loadGraphicDataFromURL("images/BushWhackerSheets/BW2BaseCustoms.json", () => { this.UILoaded(); }); this.expectedLoadCount++;
        Engine.GraphicFactoryInstance.loadGraphicDataFromURL("images/BushWhackerSheets/Doodads.json", () => { this.UILoaded(); }); this.expectedLoadCount++;
        Engine.GraphicFactoryInstance.loadGraphicDataFromURL("images/BushWhackerSheets/Doodads2.json", () => { this.UILoaded(); }); this.expectedLoadCount++;
        //Engine.GraphicFactoryInstance.loadGraphicDataFromURL("images/BushWhacker/AnimatedDoodads.json", () => { this.UILoaded(); }); this.expectedLoadCount++;
        //Engine.GraphicFactoryInstance.loadGraphicDataFromURL("images/BushWhacker/AnimatedDoodads2.json", () => { this.UILoaded(); }); this.expectedLoadCount++;
        Engine.GraphicFactoryInstance.loadGraphicDataFromURL("images/BushWhackerSheets/tilePack.json", () => { this.UILoaded(); }); this.expectedLoadCount++;
        //Engine.GraphicFactoryInstance.loadGraphicDataFromURL("images/BushWhackerSheets/Pets.json", () => { this.UILoaded(); }); this.expectedLoadCount++;
        //Engine.GraphicFactoryInstance.loadGraphicDataFromURL("images/BushWhackerSheets/Pets2.json", () => { this.UILoaded(); }); this.expectedLoadCount++;
        //Engine.GraphicFactoryInstance.loadGraphicDataFromURL("images/BushWhackerSheets/Mounts.json", () => { this.UILoaded(); }); this.expectedLoadCount++;
        Engine.GraphicFactoryInstance.loadGraphicDataFromURL("images/BushWhackerSheets/CustomItems.json", () => { this.UILoaded(); }); this.expectedLoadCount++;

        Engine.GraphicFactoryInstance.loadGraphicDataFromURL("images/ShardsSheets/Doodads.json", () => { this.UILoaded(); }); this.expectedLoadCount++;
        Engine.GraphicFactoryInstance.loadGraphicDataFromURL("images/ShardsSheets/AnimatedDoodads.json", () => { this.UILoaded(); }); this.expectedLoadCount++;
        //Engine.GraphicFactoryInstance.loadGraphicDataFromURL("images/ShardsSheets/Monsters.json", () => { this.UILoaded(); }); this.expectedLoadCount++;
        Engine.GraphicFactoryInstance.loadGraphicDataFromURL("images/ShardsSheets/Tiles.json", () => { this.UILoaded(); }); this.expectedLoadCount++;
        //Engine.GraphicFactoryInstance.loadGraphicDataFromURL("images/ShardsSheets/CustomItems.json", () => { this.UILoaded(); }); this.expectedLoadCount++;


        Engine.GraphicFactoryInstance.addGraphicData(new Engine.GraphicDef("Hero_Female","images/Character/Hero_Female.json"));
        Engine.GraphicFactoryInstance.addGraphicData(new Engine.GraphicDef("Hero_Male","images/Character/Hero_Male.json"));
        Engine.GraphicFactoryInstance.addGraphicData(new Engine.GraphicDef("Hero_Male_Full","images/ShardsSheets/Hero_Male_Full.json"));
        Engine.GraphicFactoryInstance.addGraphicData(new Engine.GraphicDef("Hero_Female_Full","images/ShardsSheets/Hero_Female_Full.json"));
        Engine.GraphicFactoryInstance.addGraphicData(new Engine.GraphicDef("GenericFlag","images/GenericFlag.json"));
        */
        
        this.gameDrawable = new Engine.Drawable();
        Engine.rootDrawable.addChild(this.gameDrawable);

        let style = new PIXI.TextStyle({
          fontFamily: "Verdana",
          fontSize: 12,
          fill: "white",
          stroke: '#ff3300',
          strokeThickness: 4,
          dropShadow: true,
          dropShadowColor: "#000000",
          dropShadowBlur: 4,
          dropShadowAngle: Math.PI / 6,
          dropShadowDistance: 6,
        });

        this.fps = new PIXI.Text("Hello Pixi!", style);
        EngineSettings.Pixi.stage.addChild(this.fps);

        this.lastFrameTime = performance.now();
        this.gameTime = 0;
        this.outOfFocusUpdateCheck();
        
        var w = new Worker(jsRoot + "out_of_focus_update_worker.js");
        
        w.onmessage = e =>
        {
            if (e.data == "outOfFocusUpdate")
            {
                this.outOfFocusUpdateCheck();
            }
        };
        
        GameManager.adminTimeScale = 1;

        if (this.expectedLoadCount == 0) this.UILoaded();

    }
    
    UILoaded()
    {
        if (++this.loadCount >= this.expectedLoadCount) this.game = Game.InitGame(Engine.rootDrawable, this.gameClass);
    }

    /*
    update (dt)
    {
        dt = (dt / 60) * this.timeScale;
        
        //For proper ordering do the frame events from the previous frame now.
        Engine.Drawable.doQueuedFrameEvents();

        Engine.EnterFrameManagerInstance.enterFrame(dt, dt);

        Engine.rootDrawable.drawableUpdate(dt);
        this.fps.text = "FPS:"+Math.round(Engine.Pixi.ticker.FPS);

        Engine.EnterFrameManagerInstance.postFrame(dt, dt);

        KeyboardManager.update(dt);
    }*/

    update (dt)
    {
        var realDT = dt / (60 * EngineSettings.Pixi.ticker.speed);
        
        this.gameTime += realDT;
        
        this.updateInner(realDT);

        KeyboardManager.update(dt);
    }
    
    updateInner(realDT)
    {
        var hadMultiGameFrames = this.gameFramesPerRealFrame > 1;
        
        var dt = realDT * this.timeScale * GameManager.adminTimeScale;
        
        for (var i = 0; i < this.gameFramesPerRealFrame; i++)
        {
            if (this.forceFrameTime != null)
            {
                realDT = this.forceFrameTime;
                dt = dt = realDT * this.timeScale * GameManager.adminTimeScale;
            }

            //For proper ordering do the frame events from the previous frame now.
            Engine.Drawable.doQueuedFrameEvents();
            Engine.EnterFrameManagerInstance.enterFrame(dt, realDT);

            Engine.rootDrawable.drawableUpdate(dt, GameManagerStatics.DrawableUpdatePaused, i >= this.gameFramesPerRealFrame - 1);
            this.fps.text = "FPS:"+Math.round(Engine.Pixi.ticker.FPS);

            Engine.EnterFrameManagerInstance.postFrame(dt, realDT);
            MultiFilterStatics.frameDone();
        }
        
        if (this.nextGameFramesPerRealFrame != null)
        {
            this.gameFramesPerRealFrame = this.nextGameFramesPerRealFrame;
            this.nextGameFramesPerRealFrame = null;
        }
    }

    inFocusUpdateCheck()
    {
        this.frameTime += (performance.now() - this.lastFrameTime);     
        this.lastFrameTime = performance.now();
        
        if (this.frameTime > (1000 / 60) * (60 / GameSettings.targetFramerate)) // 16.67
        {           
            var framesPassed = this.frameTime / (1000 / 60);
            
            while (framesPassed > 0)
            {
                var use = Math.min(10, framesPassed);
                framesPassed -= use;
                this.update(use);
            }

            Debug.Log("In Focus Update!");
            this.frameTime = 0;
        }
    }

    outOfFocusUpdateCheck()
    {
        var timeSinceFrameUpdate = performance.now() - this.lastFrameTime;      
        
        if (timeSinceFrameUpdate > 500)
        {
            this.lastFrameTime = performance.now();
            
            var framesPassed = (timeSinceFrameUpdate * 60) / 1000;
            
            while (framesPassed > 0)
            {
                var use = Math.min(10, framesPassed);
                framesPassed -= use;
                this.update(use);
            }
        }
    }

};
