using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.Serialization;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MLEM.Misc;
using MLEM.Textures;
using TouchyTickets.Upgrades;
using Vector2 = Microsoft.Xna.Framework.Vector2;

namespace TouchyTickets.Attractions; 

[DataContract]
public class Attraction {

    [DataMember]
    public readonly List<ActiveModifier> Modifiers = new();
    [DataMember]
    public readonly AttractionType Type;
    [DataMember]
    private double ticketPercentage;
    private float animationSizeModifier;

    public Attraction(AttractionType type) {
        this.Type = type;
    }

    public double Update(TimeSpan passed, ParkMap map, Point position) {
        var genRate = this.GetGenerationRate(map, position);
        // apply generation rate to ticket amount
        this.ticketPercentage += genRate * passed.TotalSeconds;
        var total = (BigInteger) this.ticketPercentage;
        if (total > 0) {
            GameImpl.Instance.Tickets += total;
            this.ticketPercentage -= (double) total;
        }

        // animation stuff
        if (this.animationSizeModifier > 0)
            this.animationSizeModifier = Math.Max(this.animationSizeModifier - 0.2F, 0);

        // return the generation rate per second
        return genRate;
    }

    public void Draw(SpriteBatch batch, Vector2 position, float alpha, float scale) {
        var drawScale = scale;
        if (this.animationSizeModifier > 0)
            drawScale += (float) Math.Sin(this.animationSizeModifier) * 0.05F;
        var tex = Assets.AttractionTexture[this.Type.TextureRegion];
        var center = tex.Size.ToVector2() / 2;
        batch.Draw(tex, position + center * scale, Color.White * alpha, 0, center, drawScale, SpriteEffects.None, 0);
    }

    public double GetGenerationRate(ParkMap map, Point position) {
        var genRate = this.Type.GetGenerationRate();

        // apply attraction modifiers
        var mod = 1D;
        foreach (var modifier in this.Modifiers)
            mod += Math.Pow(modifier.Modifier.Multiplier, modifier.Amount) - 1;
        genRate *= mod;

        // apply star upgrades
        foreach (var upgrade in Upgrade.Upgrades.Values.OfType<NeighborModifierUpgrade>())
            genRate *= upgrade.GetCurrentMultiplier(this, map, position);

        return genRate;
    }

    public void ApplyModifier(AttractionModifier modifier) {
        // increase the amount of existing modifiers
        foreach (var mod in this.Modifiers) {
            if (mod.Modifier == modifier) {
                mod.Amount++;
                return;
            }
        }
        // or add a new modifier
        this.Modifiers.Add(new ActiveModifier(modifier, 1));
    }

    public int GetModifierAmount(AttractionModifier modifier) {
        return this.Modifiers.Where(m => modifier == null || m.Modifier == modifier).Sum(m => m.Amount);
    }

    public BigInteger GetModifierPrice(AttractionModifier modifier) {
        var amount = this.GetModifierAmount(modifier);
        return (BigInteger) Math.Ceiling(modifier.InitialPrice * Math.Pow(1 + 0.45F, amount));
    }

    public void Wobble() {
        this.animationSizeModifier = MathHelper.Pi;
    }

    public IEnumerable<Attraction> GetSurrounding(ParkMap map, Point position, AttractionType type) {
        foreach (var tile in this.Type.GetCoveredTiles()) {
            foreach (var dir in Direction2Helper.Adjacent) {
                var other = map.GetAttractionAt(position + tile + dir.Offset());
                if (other != null && other != this && other.Type == type)
                    yield return other;
            }
        }
    }

}