using System;
using System.Collections.Generic;
using Volpe.Cafe;
using Volpe.Cafe.Settings;
using Volpe.Cafe.Utils;
using Volpe.Cafe.Generic;
using Volpe.Cafe.Model;
namespace Volpe.Cafe.Data
{
    [Serializable]
    public sealed class Manufacturer
    {
        #region 
        [Serializable]
        public sealed class CDescription
        {
            #region 
            internal CDescription() { }
            #endregion
            #region 
            internal CDescription Clone()
            {
                Manufacturer.CDescription md = new Manufacturer.CDescription();
                md.Code                   = this.Code;
                md.Name                   = this.Name;
                md.ProperName             = this.ProperName;
                md.CostAllocationStrategy = this.CostAllocationStrategy;
                md.DiscountRate           = this.DiscountRate;
                md.PaybackPeriod          = this.PaybackPeriod;
                md.PaybackPeriod_OC       = this.PaybackPeriod_OC;
                md.WillingToPayFines      = this.WillingToPayFines.CloneArray();
                md.AvailableCredits       = this.AvailableCredits.Clone();
                md.CreditsApplyToBaseline = this.CreditsApplyToBaseline;
                return md;
            }
            public override string ToString()
            {
                return this.ProperName;
            }
            #endregion
            #region 
            public int Code;
            public string Name;
            public string ProperName;
            public int CostAllocationStrategy;
            public double DiscountRate;
            public double PaybackPeriod;
            public double PaybackPeriod_OC;
            public bool[] WillingToPayFines;
            public RCObject<double[]> AvailableCredits;
            public bool CreditsApplyToBaseline;
            #endregion
        }
        [Serializable]
        public sealed class CModelData
        {
            #region 
            internal CModelData() { }
            #endregion
            #region 
            internal Manufacturer.CModelData Clone()
            {
                Manufacturer.CModelData mmd = new Manufacturer.CModelData();
                mmd.KMY    = new KMYType[this.KMY   .Length];
                mmd.KMY_OC = new KMYType[this.KMY_OC.Length];
                for (int i = 0; i < this.KMY   .Length; i++) { mmd.KMY   [i] = this.KMY   [i].Clone(); }
                for (int i = 0; i < this.KMY_OC.Length; i++) { mmd.KMY_OC[i] = this.KMY_OC[i].Clone(); }
                if (this.DefaultStandard != null)
                {
                    mmd.DefaultStandard = new RCDouble[this.DefaultStandard.Length];
                    for (int i = 0; i < this.DefaultStandard.Length; i++)
                    {
                        mmd.DefaultStandard[i] = this.DefaultStandard[i].Clone();
                    }
                }
                mmd.AverageStandard     = this.AverageStandard      .Clone();
                mmd.PreliminaryStandard = this.PreliminaryStandard  .Clone();
                mmd.Standard            = this.Standard             .Clone();
                mmd.Sales               = this.Sales                .Clone();
                mmd.SalesOverFE         = this.SalesOverFE          .Clone();
                mmd.CAFE                = this.CAFE                 .Clone();
                mmd.CAFE_2Bag           = this.CAFE_2Bag            .Clone();
                mmd.Credits             = this.Credits              .Clone();
                mmd.Fines               = this.Fines                .Clone();
                mmd.TCreditsInCapped    = this.TCreditsInCapped     .Clone();
                mmd.TCreditsIn          = this.TCreditsIn           .Clone();
                mmd.TCreditsOut         = this.TCreditsOut          .Clone();
                mmd.TechCost            = this.TechCost             .Clone();
                mmd.RegCost             = this.RegCost              .Clone();
                mmd.DiscCost            = this.DiscCost             .Clone();
                mmd.LossOfValue         = this.LossOfValue          .Clone();
                mmd.RelativeLossOfValue = this.RelativeLossOfValue  .Clone();
                mmd.MaintenanceCost     = this.MaintenanceCost      .Clone();
                mmd.RepairCost          = this.RepairCost           .Clone();
                mmd.TotalConsumerCosts  = this.TotalConsumerCosts   .Clone();
                mmd.TotalSocialCosts    = this.TotalSocialCosts     .Clone();
                mmd.TaxesAndFees        = this.TaxesAndFees         .Clone();
                mmd.FinancingCost       = this.FinancingCost        .Clone();
                mmd.InsuranceCost       = this.InsuranceCost        .Clone();
                if (this.TechUsedSales != null)
                {
                    int len = TechUsedSales.Length;
                    mmd.TechUsedSales     = new RCDouble[len];
                    mmd.TechAppliedSales  = new RCDouble[len];
                    mmd.TechExhausted     = new bool    [len];
                    mmd.TechPhaseInOffset = new int     [len];
                    for (int i = 0; i < len; i++)
                    {
                        mmd.TechUsedSales    [i] = this.TechUsedSales    [i].Clone();
                        mmd.TechAppliedSales [i] = this.TechAppliedSales [i].Clone();
                        mmd.TechExhausted    [i] = this.TechExhausted    [i];
                        mmd.TechPhaseInOffset[i] = this.TechPhaseInOffset[i];
                    }
                }
                mmd.CFExhausted = this.CFExhausted;
                return mmd;
            }
            #endregion
            #region 
            public KMYType[] KMY;
            public KMYType[] KMY_OC;
            public RCDouble[] DefaultStandard;
            public RCDouble AverageStandard = new RCDouble();
            public RCDouble PreliminaryStandard = new RCDouble();
            public RCDouble Standard = new RCDouble();
            public RCDouble Sales = new RCDouble();
            public RCDouble SalesOverFE = new RCDouble();
            public RCDouble CAFE = new RCDouble();
            public RCDouble CAFE_2Bag = new RCDouble();
            public RCDouble Credits = new RCDouble();
            public RCDouble Fines = new RCDouble();
            public RCDouble TCreditsInCapped = new RCDouble();
            public RCDouble TCreditsIn = new RCDouble();
            public RCDouble TCreditsOut = new RCDouble();
            public RCDouble TechCost = new RCDouble();
            public RCDouble RegCost = new RCDouble();
            public RCDouble DiscCost = new RCDouble();
            public RCDouble LossOfValue = new RCDouble();
            public RCDouble RelativeLossOfValue = new RCDouble();
            public RCDouble MaintenanceCost = new RCDouble();
            public RCDouble RepairCost = new RCDouble();
            public RCDouble TotalConsumerCosts = new RCDouble();
            public RCDouble TotalSocialCosts = new RCDouble();
            public RCDouble TaxesAndFees = new RCDouble();
            public RCDouble FinancingCost = new RCDouble();
            public RCDouble InsuranceCost = new RCDouble();
            public RCDouble[] TechUsedSales;
            public RCDouble[] TechAppliedSales;
            public bool[] TechExhausted;
            public int[] TechPhaseInOffset;
            public bool CFExhausted;
            #endregion
        }
        #endregion
        #region 
        Manufacturer() { }
        internal Manufacturer(Manufacturer.CDescription description)
        {
            this._index = -1;
            this._description   = description;
            this._modelData     = null;
            this._vehicles      = new List<Vehicle>     (8);
            this._engines       = new List<Engine>      (8);
            this._transmissions = new List<Transmission>(8);
            this._platforms     = new List<Platform>    (8);
            this._lastEngCode = -1;
            this._lastTrnCode = -1;
        }
        #endregion
        #region 
        #region 
        internal Manufacturer Clone()
        {
            Manufacturer mfr = new Manufacturer();
            mfr._index         = this._index;
            mfr._lastEngCode   = this._lastEngCode;
            mfr._lastTrnCode   = this._lastTrnCode;
            mfr._description   = this._description.Clone();
            mfr._modelData     = (this._modelData   == null) ? null : this._modelData  .Clone();
            mfr._vehicles      = this.CloneVehicles     (this._vehicles);
            mfr._engines       = this.CloneEngines      (this._engines);
            mfr._transmissions = this.CloneTransmissions(this._transmissions);
            mfr._platforms     = this.ClonePlatforms    (this._platforms);
            Engine      [] engs = mfr._engines      .ToArray();
            Transmission[] trns = mfr._transmissions.ToArray();
            Platform    [] plts = mfr._platforms    .ToArray();
            Vehicle     [] vehs = mfr._vehicles     .ToArray();
            for (int i = 0; i < engs.Length; i++)
            {
                int parentCode = ((engs[i].Parent == null) ? -1 : engs[i].Parent.Description.Code);
                engs[i].Initialize(mfr);
                engs[i].SetParent(parentCode);
            }
            for (int i = 0; i < trns.Length; i++)
            {
                int parentCode = ((trns[i].Parent == null) ? -1 : trns[i].Parent.Description.Code);
                trns[i].Initialize(mfr);
                trns[i].SetParent(parentCode);
            }
            for (int i = 0; i < plts.Length; i++)
            {
                plts[i].Initialize(mfr);
            }
            for (int i = 0; i < vehs.Length; i++)
            {   
                vehs[i].Initialize(mfr, vehs);
                Platform plt = mfr.GetPlatform(vehs[i]);
                plt.Vehicles.Add(vehs[i]);
                vehs[i].SetPlatform(plt);
            }
            return mfr;
        }
        List<Vehicle> CloneVehicles(List<Vehicle> list)
        {
            List<Vehicle> newList = new List<Vehicle>(list.Count);
            for (int i = 0, count = list.Count; i < count; i++)
            {
                newList.Add(list[i].Clone());
            }
            return newList;
        }
        List<Engine> CloneEngines(List<Engine> list)
        {
            List<Engine> newList = new List<Engine>(list.Count);
            for (int i = 0, count = list.Count; i < count; i++)
            {
                newList.Add(list[i].Clone());
            }
            return newList;
        }
        List<Transmission> CloneTransmissions(List<Transmission> list)
        {
            List<Transmission> newList = new List<Transmission>(list.Count);
            for (int i = 0, count = list.Count; i < count; i++)
            {
                newList.Add(list[i].Clone());
            }
            return newList;
        }
        List<Platform> ClonePlatforms(List<Platform> list)
        {
            List<Platform> newList = new List<Platform>(list.Count);
            for (int i = 0, count = list.Count; i < count; i++)
            {
                newList.Add(list[i].Clone());
            }
            return newList;
        }
        #endregion
        public override string ToString()
        {
            return this._description.Name;
        }
        internal void Initialize(List<Vehicle> vehicles, List<Engine> engines, List<Transmission> transmissions, out int minYear, out int maxYear)
        {
            Engine      [] engs = engines      .ToArray();
            Transmission[] trns = transmissions.ToArray();
            Vehicle     [] vehs = vehicles     .ToArray();
            for (int i = 0; i < engs.Length; i++)
            {
                engs[i].Initialize(this);
                this._engines.Add(engs[i]);
                if (this._lastEngCode < engs[i].Description.Code)
                {
                    this._lastEngCode = engs[i].Description.Code;
                }
            }
            for (int i = 0; i < trns.Length; i++)
            {
                trns[i].Initialize(this);
                this._transmissions.Add(trns[i]);
                if (this._lastTrnCode < trns[i].Description.Code)
                {
                    this._lastTrnCode = trns[i].Description.Code;
                }
            }
            for (int i = 0; i < vehs.Length; i++)
            {
                vehs[i].Initialize(this, vehs);
                Platform plt = this.GetPlatform(vehs[i]);
                plt.Vehicles.Add(vehs[i]);
                vehs[i].SetPlatform(plt);
                this._vehicles.Add(vehs[i]);
            }
            List<Vehicle> vc = this.Vehicles;
            minYear = int.MaxValue;
            maxYear = -1;
            for (int i = 0, vehCount = this.VehicleCount; i < vehCount; i++)
            {
                Vehicle v = vc[i];
                if (v.ValidYears != null)
                {
                    minYear = Math.Min(v.MinYear.Year, minYear);
                    maxYear = Math.Max(v.MaxYear.Year, maxYear);
                }
            }
        }
        Platform GetPlatform(Vehicle veh)
        {
            string vehPlatformName = veh.Description.Platform;
            for (int i = 0; i < this._platforms.Count; i++)
            {
                if (this._platforms[i].Name.Equals(vehPlatformName, StringComparison.InvariantCultureIgnoreCase))
                {
                    return this._platforms[i];
                }
            }
            Platform value = new Platform(vehPlatformName);
            value.Initialize(this);
            this._platforms.Add(value);
            return value;
        }
        internal int GetNextEngineCode()
        {
            return ++this._lastEngCode;
        }
        internal int GetNextTransmissionCode()
        {
            return ++this._lastTrnCode;
        }
        public double CalcLaborHours(ModelYear year, RegulatoryClass regClass)
        {
            double hours = 0;
            for (int i = 0, vehCount = this._vehicles.Count; i < vehCount; i++)
            {
                if (regClass == RegulatoryClass.All || regClass == this._vehicles[i].RegClass)
                {
                    hours += this._vehicles[i].CalcLaborHours(year);
                }
            }
            return hours;
        }
        public double CalcLaborHours(ModelYear minYear, ModelYear maxYear, RegulatoryClass regClass)
        {
            double hours = 0;
            for (int i = 0, vehCount = this._vehicles.Count; i < vehCount; i++)
            {
                if (regClass == RegulatoryClass.All || regClass == this._vehicles[i].RegClass)
                {
                    hours += this._vehicles[i].CalcLaborHours(minYear, maxYear);
                }
            }
            return hours;
        }
        public RCDouble CalcAverageFootprint(Scenario scen, ModelYear year)
        {
            RCDouble sales = new RCDouble();
            RCDouble value = new RCDouble();
            foreach (Vehicle veh in this.Vehicles)
            {
                var    vd       = veh.Description;
                var    regClass = veh.RegClass;
                double vehSales = vd .Sales[year.Index];
                sales[regClass] += vehSales;
                value[regClass] += vehSales * vd.Footprint;
            }
            foreach (RegulatoryClass regClass in RCDouble.Classes)
            {
                if (sales[regClass] != 0) { value[regClass] /= sales[regClass]; }
            }
            return value;
        }
        public RCDouble CalcAverageCurbWeight(Scenario scen, ModelYear year)
        {
            RCDouble sales = new RCDouble();
            RCDouble value = new RCDouble();
            foreach (Vehicle veh in this.Vehicles)
            {
                var    vd       = veh.Description;
                var    regClass = veh.RegClass;
                double vehSales = vd .Sales[year.Index];
                sales[regClass] += vehSales;
                value[regClass] += vehSales * vd.CurbWeight;
            }
            foreach (RegulatoryClass regClass in RCDouble.Classes)
            {
                if (sales[regClass] != 0) { value[regClass] /= sales[regClass]; }
            }
            return value;
        }
        public RCDouble CalcAverageWorkFactor(Scenario scen, ModelYear year)
        {
            RCDouble sales = new RCDouble();
            RCDouble value = new RCDouble();
            foreach (Vehicle veh in this.Vehicles)
            {
                var    vd       = veh.Description;
                var    regClass = veh.RegClass;
                double vehSales = vd .Sales[year.Index];
                sales[regClass] += vehSales;
                value[regClass] += vehSales * Standards.GetWorkFactor(veh, scen, year);
            }
            foreach (RegulatoryClass regClass in RCDouble.Classes)
            {
                if (sales[regClass] != 0) { value[regClass] /= sales[regClass]; }
            }
            return value;
        }
        #endregion
        #region 
        public int Index { get { return this._index; } internal set { this._index = value; } }
        public Manufacturer.CDescription Description { get { return this._description; } }
        public Manufacturer.CModelData ModelData { get { return this._modelData; } set { this._modelData = value; } }
        public List<Vehicle> Vehicles { get { return this._vehicles; } }
        public int VehicleCount { get { return this._vehicles.Count; } }
        public List<Engine> Engines { get { return this._engines; } }
        public List<Transmission> Transmissions { get { return this._transmissions; } }
        public List<Platform> Platforms { get { return this._platforms; } }
        internal int LastEngCode { get { return this._lastEngCode; } set { this._lastEngCode = value; } }
        internal int LastTrnCode { get { return this._lastTrnCode; } set { this._lastTrnCode = value; } }
        #endregion
        #region 
        int _index; 
        Manufacturer.CDescription _description;
        Manufacturer.CModelData   _modelData;
        List<Vehicle>      _vehicles;
        List<Engine>       _engines;
        List<Transmission> _transmissions;
        List<Platform>     _platforms;
        int _lastEngCode;
        int _lastTrnCode;
        #endregion
    }
}

