﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Arugula.Math;

namespace Arugula.Games
{


    [System.Serializable]
    public struct Stats
    {
        public enum CombineMode { Add, Multiply, Ignore }

        public static Stats operator +(Stats a, Stats b)
        {
            Stats c = new Stats(a);
            foreach (var t in b.types)
                c[t] += b[t];

            return c;
        }

        public static Stats operator *(Stats a, Stats b)
        {
            Stats c = new Stats(a);
            foreach (var t in b.types)
                c[t] *= b[t];

            return c;
        }

        public static Stats Combine(params Stats[] others)
        {
            Stats c = new Stats(others[0]);

            for (int i = 1; i < others.Length; i++)
            {
                Stats other = others[i];
                foreach (var t in other.types)
                {
                    switch (other.GetCombineMode(t))
                    {
                        case CombineMode.Add:
                            c[t] += other[t];
                            break;
                        case CombineMode.Multiply:
                            c[t] *= other[t];
                            break;
                        default:
                            break;
                    }
                }
            }

            return c;
        }

        [SerializeField] private StatType[] types;
        [SerializeField] private float[] values;
        [SerializeField] private CombineMode[] modes;

        public Stats(Stats source)
        {
            if (source.types == null || source.types.Length == 0)
            {
                types = new StatType[0];
                values = new float[0];
                modes = new CombineMode[0];
                return;
            }

            types = new StatType[source.types.Length];
            values = new float[source.values.Length];
            modes = new CombineMode[source.modes.Length];
            Array.Copy(source.types, types, types.Length);
            Array.Copy(source.values, values, values.Length);
            Array.Copy(source.modes, modes, modes.Length);
        }

        public float this[StatType type]
        {
            get
            {
                int index = GetIndex(type);
                if (index >= 0)
                    return values[index];
                return 0;
            }
            set
            {
                int index = GetIndex(type);
                if (index >= 0)
                    values[index] = value.Clamp(type.min, type.max).Snap(type.snap);
                else
                {
                    Array.Resize(ref types, types.Length + 1);
                    types[types.Length - 1] = type;
                    Array.Resize(ref values, values.Length + 1);
                    values[values.Length - 1] = value.Clamp(type.min, type.max).Snap(type.snap);
                    Array.Resize(ref modes, modes.Length + 1);
                    modes[modes.Length - 1] = CombineMode.Add;
                }
            }
        }

        public CombineMode GetCombineMode(StatType type)
        {
            int index = GetIndex(type);
            if (index >= 0)
                return modes[index];

            return CombineMode.Ignore;
        }

        public void SetCombineMode(StatType type, CombineMode mode)
        {
            int index = GetIndex(type);
            if (index >= 0)
            {
                modes[index] = mode;
            }
            else
            {
                throw new Exception($"StatType {type} not present on this object.  Add using the StatType indexer first!");
            }
        }


        public float this[string id]
        {
            get
            {
                int index = GetIndex(id);
                if (index >= 0)
                    return values[index];
                return 0;
            }
            set
            {
                int index = GetIndex(id);
                if (index >= 0)
                    values[index] = value.Clamp(types[index].min, types[index].max).Snap(types[index].snap);
                else
                {
                    throw new Exception($"StatType {id} not present on this object.  Add using the StatType indexer first!");
                }
            }
        }

        

        int GetIndex(string id)
        {
            for (int i = 0; i < types.Length; i++)
            {
                if (types[i].id == id)
                    return i;
            }

            return -1;
        }

        int GetIndex(StatType s) => Array.IndexOf(types, s);


        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < types.Length; i++)
            {
                sb.AppendLine(types[i].displayName + "   " + values[i]);
            }

            return sb.ToString();
        }
    }
}