using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Newtonsoft.Json;
using TouchyTickets.Upgrades;
using static TouchyTickets.Attractions.AttractionFlags;

namespace TouchyTickets.Attractions; 

[JsonConverter(typeof(Converter))]
public class AttractionType {

    public static readonly Dictionary<string, AttractionType> Attractions = new();
    public static readonly AttractionType Carousel = AttractionType.Register(new AttractionType("Carousel", AttractionType.RectArea(1, 1), new Rectangle(0, 0, 1, 1), 0.5F, 50, AttractionFlags.Relaxed | AttractionFlags.Cars | AttractionFlags.Small));
    public static readonly AttractionType MirrorHouse = AttractionType.Register(new AttractionType("MirrorHouse", AttractionType.RectArea(1, 1), new Rectangle(3, 0, 1, 1), 1, 150, AttractionFlags.Relaxed | AttractionFlags.Walking | AttractionFlags.NonTechnology | AttractionFlags.Small));
    public static readonly AttractionType FoodCourt = AttractionType.Register(new AttractionType("FoodCourt", AttractionType.RectArea(2, 1), new Rectangle(1, 0, 2, 1), 2F, 300, AttractionFlags.None));
    public static readonly AttractionType SpiralSlide = AttractionType.Register(new AttractionType("SpiralSlide", AttractionType.RectArea(1, 2), new Rectangle(5, 0, 1, 2), 4, 1200, AttractionFlags.Relaxed | AttractionFlags.Walking));
    public static readonly AttractionType HedgeMaze = AttractionType.Register(new AttractionType("HedgeMaze", AttractionType.RectArea(2, 2), new Rectangle(3, 3, 2, 2), 8, 2500, AttractionFlags.Relaxed | AttractionFlags.Walking | AttractionFlags.NonTechnology));
    public static readonly AttractionType FerrisWheel = AttractionType.Register(new AttractionType("FerrisWheel", AttractionType.RectArea(2, 2), new Rectangle(0, 1, 2, 2), 12, 4000, AttractionFlags.Relaxed | AttractionFlags.Cars));
    public static readonly AttractionType FreefallCoaster = AttractionType.Register(new AttractionType("FreefallCoaster", new[,] {{true, false, true}, {true, true, true}}, new Rectangle(6, 0, 3, 2), 24, 8000, AttractionFlags.FastCars));
    public static readonly AttractionType HauntedHouse = AttractionType.Register(new AttractionType("HauntedHouse", AttractionType.RectArea(2, 2), new Rectangle(3, 5, 2, 2), 30, 12000, AttractionFlags.FastCars));
    public static readonly AttractionType GoKarts = AttractionType.Register(new AttractionType("GoKarts", AttractionType.RectArea(2, 2), new Rectangle(5, 2, 2, 2), 50, 24000, AttractionFlags.Cars | AttractionFlags.Relaxed));
    public static readonly AttractionType MiniGolf = AttractionType.Register(new AttractionType("MiniGolf", AttractionType.RectArea(2, 3), new Rectangle(9, 0, 2, 3), 75, 35000, AttractionFlags.Relaxed | AttractionFlags.Walking | AttractionFlags.NonTechnology));
    public static readonly AttractionType WildMouse = AttractionType.Register(new AttractionType("WildMouse", AttractionType.RectArea(3, 2), new Rectangle(2, 1, 3, 2), 100, 60000, AttractionFlags.FastCars));
    public static readonly AttractionType LogFlume = AttractionType.Register(new AttractionType("LogFlume", new[,] {{true, true, false}, {true, true, true}}, new Rectangle(0, 3, 3, 2), 160, 90000, AttractionFlags.FastCars));
    public static readonly AttractionType HeartlineTwister = AttractionType.Register(new AttractionType("HeartlineTwister", AttractionType.RectArea(4, 2), new Rectangle(5, 4, 4, 2), 250, 150000, AttractionFlags.FastCars));
    public static readonly AttractionType WoodCoaster = AttractionType.Register(new AttractionType("WoodCoaster", AttractionType.RectArea(3, 3), new Rectangle(0, 5, 3, 3), 300, 215000, AttractionFlags.FastCars));
    public static readonly AttractionType SafariZone = AttractionType.Register(new AttractionType("SafariZone", AttractionType.RectArea(5, 3), new Rectangle(11, 0, 5, 3), 600, 750000, AttractionFlags.Relaxed | AttractionFlags.Walking | AttractionFlags.NonTechnology));

    public readonly string Name;
    public readonly bool[,] Area;
    public int Width => this.Area.GetLength(1);
    public int Height => this.Area.GetLength(0);
    public readonly Rectangle TextureRegion;
    private readonly float generationPerSecond;
    public readonly long InitialPrice;
    public readonly AttractionFlags Flags;

    public AttractionType(string name, bool[,] area, Rectangle textureRegion, float generationPerSecond, long initialPrice, AttractionFlags flags) {
        this.Name = name;
        this.Area = area;
        this.TextureRegion = textureRegion;
        this.generationPerSecond = generationPerSecond;
        this.InitialPrice = initialPrice;
        this.Flags = flags;
    }

    public Attraction Create() {
        return new Attraction(this);
    }

    public double GetGenerationRate() {
        var genRate = this.generationPerSecond;
        foreach (var upgrade in Upgrade.Upgrades.Values.OfType<ModifierUpgrade>())
            genRate *= upgrade.GetCurrentMultiplier(this);
        return genRate;
    }

    public IEnumerable<Point> GetCoveredTiles() {
        for (var x = 0; x < this.Width; x++) {
            for (var y = 0; y < this.Height; y++) {
                if (this.Area[y, x])
                    yield return new Point(x, y);
            }
        }
    }

    private static AttractionType Register(AttractionType type) {
        AttractionType.Attractions.Add(type.Name, type);
        return type;
    }

    private static bool[,] RectArea(int width, int height) {
        var ret = new bool[height, width];
        for (var x = 0; x < width; x++) {
            for (var y = 0; y < height; y++)
                ret[y, x] = true;
        }
        return ret;
    }

    public delegate Attraction Constructor(AttractionType type);

    public class Converter : JsonConverter<AttractionType> {

        public override void WriteJson(JsonWriter writer, AttractionType value, JsonSerializer serializer) {
            if (value != null)
                writer.WriteValue(value.Name);
        }

        public override AttractionType ReadJson(JsonReader reader, Type objectType, AttractionType existingValue, bool hasExistingValue, JsonSerializer serializer) {
            return reader.Value != null ? AttractionType.Attractions[reader.Value.ToString()] : null;
        }

    }

}