using System;
using System.Collections.Generic;
using Volpe.Cafe;
using Volpe.Cafe.Utils;
namespace Volpe.Cafe.Data
{
    [Serializable]
    public abstract class Component
    {
        #region 
        [Serializable]
        public class CModelData
        {
            #region 
            internal CModelData() { }
            #endregion
            #region 
            internal Component.CModelData Clone()
            {
                Component.CModelData value = new Component.CModelData();
                value.TechUsed           = (bool[])Interaction.CloneArray(this.TechUsed          , typeof(bool));
                value.TechApplied        = (bool[])Interaction.CloneArray(this.TechApplied       , typeof(bool));
                value.TechAppliedYear    = (int [])Interaction.CloneArray(this.TechAppliedYear   , typeof(int ));
                value.TechSuperseded     = (bool[])Interaction.CloneArray(this.TechSuperseded    , typeof(bool));
                value.TechSupersededYear = (int [])Interaction.CloneArray(this.TechSupersededYear, typeof(int ));
                return value;
            }
            #endregion
            #region 
            public bool[] TechUsed;
            public bool[] TechApplied;
            public int[] TechAppliedYear;
            public bool[] TechSuperseded;
            public int[] TechSupersededYear;
            #endregion
        }
        [Serializable]
        class ComponentVersion
        {
            #region 
            ComponentVersion() { }
            public ComponentVersion(ulong techKey, int year, int revision)
            {
                this.TechKey   = techKey;
                this.Year      = year;
                this.Revision  = revision;
                this.Revisions = new List<ComponentVersion>();
            }
            #endregion
            #region 
            internal ComponentVersion Clone()
            {
                ComponentVersion value = new ComponentVersion();
                value.TechKey   = this.TechKey;
                value.Year      = this.Year;
                value.Revision  = this.Revision;
                value.Revisions = new List<ComponentVersion>();
                for (int i = 0; i < this.Revisions.Count; i++)
                {
                    value.Revisions.Add(this.Revisions[i].Clone());
                }
                return value;
            }
            public override string ToString()
            {
                return "{TechKey=" + this.TechKey + ", Year=" + this.Year + ", Revision=" + this.Revision + "}";
            }
            #endregion
            #region 
            public string Version
            {
                get
                {
                    return ((this.Year     > 0) ? "v." + this.Year     : "baseline") +
                           ((this.Revision > 0) ? "-R" + this.Revision : string.Empty);
                }
            }
            public ulong TechKey { get; private set; }
            public int Year { get; private set; }
            public int Revision { get; private set; }
            public bool HasRevisions { get { return this.Revisions.Count > 0; } }
            public List<ComponentVersion> Revisions { get; private set; }
            #endregion
        }
        #endregion
        #region 
        protected Component()
        {
            this._modelData    = new Component.CModelData();
            this._versions     = new List<ComponentVersion>();
            this._manufacturer = null;
            this._vehicles     = new List<Vehicle>();
            this._parent       = null;
        }
        #endregion
        #region 
        internal virtual void Initialize(Manufacturer manufacturer)
        {
            this._manufacturer = manufacturer;
            if (this._vehicles == null) { this._vehicles = new List<Vehicle>(); } else { this._vehicles.Clear(); }
            this._parent = null;
        }
        public string GetComponentVersion(Vehicle veh)
        {
            ulong vehTechKey = this.ComputeTechnologyKey(this.TechnologyList, veh.ModelData.TechUsed, veh.ModelData.TechSuperseded);
            ComponentVersion version = this.FindComponentVersion(vehTechKey, false);
            if (version == null)
            {
                return "UNDEFINED";
            }
            return version.Version;
        }
        public void UpdateComponentVersion(ModelYear year, Component prev)
        {
            if (prev != null)
            {   
                this._versions.Clear();
                for (int i = 0; i < prev._versions.Count; i++)
                {
                    this._versions.Add(prev._versions[i].Clone());
                }
            }
            this.UpdateComponentVersion(year);
        }
        void UpdateComponentVersion(ModelYear year)
        {
            ulong techKey = this.ComputeTechnologyKey(this.TechnologyList, this._modelData.TechUsed, this._modelData.TechSuperseded);
            if (!this.HasComponentVersion(techKey))
            {
                this.AddComponentVersion(this._versions, techKey, (year == null) ? 0 : year.Year, 0);
            }
            foreach (Vehicle veh in this._vehicles)
            {   
                ulong vehTechKey = this.ComputeTechnologyKey(this.TechnologyList, veh.ModelData.TechUsed, veh.ModelData.TechSuperseded);
                if (!this.HasComponentVersion(vehTechKey))
                {   
                    ComponentVersion baseVersion = this.FindComponentVersion(vehTechKey, true);
                    if (baseVersion != null)
                    {
                        this.AddComponentVersion(baseVersion.Revisions,
                                                 vehTechKey, baseVersion.Year, baseVersion.Revisions.Count + 1);
                    }
                }
            }
        }
        void AddComponentVersion(List<ComponentVersion> versions, ulong techKey, int year, int revision)
        {
            ComponentVersion version = new ComponentVersion(techKey, year, revision);
            versions.Add(version);
            versions.Sort((x, y) => { return x.TechKey.CompareTo(y.TechKey); });
        }
        ulong ComputeTechnologyKey(int[] techList, bool[] techUsed, bool[] techSuperseded)
        {
            ulong techKey = 0;
            for (int i = 0; i < techList.Length; i++)
            {
                int techIndex = techList[i];
                if (techUsed[techIndex] && !techSuperseded[techIndex])
                {
                    techKey += (ulong)Math.Pow(2, i);
                }
            }
            return techKey;
        }
        ComponentVersion FindComponentVersion(ulong techKey, bool closestBaseVersion)
        {
            return this.FindComponentVersion_Internal(techKey, this._versions, closestBaseVersion);
        }
        ComponentVersion FindComponentVersion_Internal(ulong techKey, List<ComponentVersion> versions, bool closestBaseVersion)
        {
            for (int i = versions.Count - 1; i >= 0; i--)
            {
                ComponentVersion version = versions[i];
                if ((techKey == version.TechKey) || (closestBaseVersion && (techKey & version.TechKey) == version.TechKey))
                {
                    return version;
                }
                else if (!closestBaseVersion && version.HasRevisions)
                {
                    version = this.FindComponentVersion_Internal(techKey, version.Revisions, closestBaseVersion);
                    if (version != null) { return version; }
                }
            }
            if (closestBaseVersion)
            {
                for (int i = 0; i < versions.Count; i++)
                {
                    if (techKey > versions[i].TechKey) { return versions[i]; }
                }
            }
            return null;
        }
        bool HasComponentVersion(ulong techKey)
        {
            return (this.FindComponentVersion(techKey, false) != null);
        }
        protected void CopyTo(Component value)
        {
            value._modelData    = this._modelData.Clone();
            value._manufacturer = this._manufacturer;
            value._vehicles     = new List<Vehicle>(this._vehicles);
            value._parent       = this._parent;
            for (int i = 0; i < this._versions.Count; i++)
            {
                value._versions.Add(this._versions[i].Clone());
            }
        }
        protected void SplitTo(List<Vehicle> vehs, Component value)
        {
            for (int i = 0, vehCount = vehs.Count; i < vehCount; i++)
            {
                this._vehicles.Remove(vehs[i]);
            }
            for (int i = 0, vehCount = this._vehicles.Count; i < vehCount; i++)
            {
                value._vehicles.Remove(this._vehicles[i]);
            }
            value._parent = this;
        }
        internal virtual void RemoveVehicle(Vehicle veh)
        {
            this._vehicles.Remove(veh);
            if (this.IsPlatformLeader(veh))
            {
                this._platformLeader = null;
            }
        }
        public bool IsPlatformLeader(Vehicle veh)
        {
            return (veh == this.GetPlatformLeader());
        }
        public Vehicle GetPlatformLeader()
        {
            if (this._vehicles == null || this._vehicles.Count == 0) { return null; }
            if (this._platformLeader == null) { this._platformLeader = this.SelectPlatformLeader(this._vehicles); }
            return this._platformLeader;
        }
        protected virtual Vehicle SelectPlatformLeader(List<Vehicle> vehicles)
        {
            Vehicle leader = vehicles[0];
            double  lSales = leader.Description.CalcAverageSales();
            double  lMSRP  = leader.Description.CalcAverageMSRP ();
            for (int i = 1; i < vehicles.Count; i++)
            {
                double vSales = vehicles[i].Description.CalcAverageSales();
                double vMSRP  = vehicles[i].Description.CalcAverageMSRP ();
                if (vSales < lSales || (vSales == lSales && vMSRP > lMSRP))
                {
                    leader = vehicles[i];
                    lSales = vSales;
                    lMSRP  = vMSRP;
                }
            }
            return leader;
        }
        public int[] GetRedesignYears()
        {
            Vehicle veh = this.GetPlatformLeader();
            return (veh == null) ? new int[] { } : veh.Description.RedesignYears;
        }
        public bool IsAtRedesign(ModelYear year)
        {
            int[] redesignYears = this.GetRedesignYears();
            for (int i = 0; i < redesignYears.Length; i++)
            {
                if (redesignYears[i] == year.Year) { return true; }
            }
            return false;
        }
        #endregion
        #region 
        public abstract int[] TechnologyList { get; }
        public Component.CModelData ModelData { get { return this._modelData; } }
        public Manufacturer Manufacturer { get { return this._manufacturer; } }
        public List<Vehicle> Vehicles { get { return this._vehicles; } }
        #endregion
        #region 
        protected Component.CModelData _modelData;
        protected Manufacturer _manufacturer;
        protected List<Vehicle> _vehicles;
        Vehicle _platformLeader;
        protected Component _parent;        
        List<ComponentVersion> _versions;
        #endregion
    }
}

