using System;
using System.Collections.Generic;
using System.Numerics;
using Coroutine;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MLEM.Cameras;
using MLEM.Startup;
using TouchyTickets.Upgrades;
using Vector2 = Microsoft.Xna.Framework.Vector2;

namespace TouchyTickets; 

public class GameImpl : MlemGame {

    public static GameImpl Instance { get; private set; }
    public readonly ISet<Upgrade> AppliedUpgrades = new HashSet<Upgrade>();
    public readonly Platform Platform;
    public BigInteger Tickets;
    public int TimesRestarted;
    public int Stars;
    public ParkMap Map;
    public Tutorial Tutorial { get; private set; }
    public Camera Camera { get; private set; }
    public Ui Ui { get; private set; }
    public bool DrawMap;
    public DateTime LastUpdate;
    public TimeSpan PlayTime;
    private double saveCounter;

    public GameImpl(Platform platform) {
        this.Platform = platform;
        GameImpl.Instance = this;
    }

    protected override void LoadContent() {
        base.LoadContent();
        Assets.Load();
        Options.Load();

        // start the load sequence
        Ui.SetupUiSystem(this.UiSystem);
        CoroutineHandler.Start(Ui.DisplaySplash(this.LoadGame));
    }

    private void LoadGame() {
        this.Tutorial = new Tutorial();

        if (!SaveHandler.Load(this))
            this.Map = new ParkMap(20, 20);

        // load other stuff
        this.Ui = new Ui(this.UiSystem);
        this.Camera = new Camera(this.GraphicsDevice) {
            Scale = 4,
            AutoScaleWithScreen = true,
            AutoScaleReferenceSize = new Point(720, 1280),
            MaxScale = 24,
            MinScale = 2
        };

        // update the map once to make sure that catching up happens during loading
        this.UpdateMapOnce();
    }

    protected override void DoUpdate(GameTime gameTime) {
        base.DoUpdate(gameTime);

        if (this.Map != null) {
            // update the map
            this.UpdateMapOnce();

            // save every 3 seconds
            this.saveCounter += gameTime.ElapsedGameTime.TotalSeconds;
            if (this.saveCounter >= 3) {
                this.saveCounter = 0;
                SaveHandler.Save(this);
            }
        }

        // play time stuff
        var lastTime = this.PlayTime;
        this.PlayTime += gameTime.ElapsedGameTime;
        if (lastTime.TotalHours >= 1 != this.PlayTime.TotalHours >= 1)
            Ui.DisplayRatePlease();

        this.Ui?.Update(gameTime);
        this.Tutorial?.Update(this);
    }

    protected override void DoDraw(GameTime gameTime) {
        this.GraphicsDevice.Clear(Color.Black);
        if (this.DrawMap) {
            this.SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, transformMatrix: this.Camera.ViewMatrix);
            this.Map.Draw(gameTime, this.SpriteBatch, Vector2.Zero, 1, 1, true, this.Camera.GetVisibleRectangle());
            this.SpriteBatch.End();
        }
        base.DoDraw(gameTime);
    }

    public BigInteger GetStarPrice() {
        return 1000000000 * BigInteger.Pow(100, this.TimesRestarted);
    }

    public int GetBuyableStars() {
#if DEBUG
        return 3;
#else
            return (int) BigInteger.Min(3, this.Tickets / this.GetStarPrice());
#endif
    }

    private void UpdateMapOnce() {
        var now = DateTime.Now;
        if (this.LastUpdate != default) {
            var lastTickets = this.Tickets;
            var lastTps = this.Map.TicketsPerSecond;

            var passed = now - this.LastUpdate;
            this.Map.Update(passed, passed.TotalSeconds >= 1);

            // if 10 or more seconds passed, we display a message
            if (Options.Instance.WhileYouWereAwayMessage && passed.TotalSeconds >= 10)
                Ui.DisplayWhileYouWereAway(passed, this.Tickets - lastTickets, this.Map.TicketsPerSecond - lastTps);
        }
        this.LastUpdate = now;
    }

}