using System;
using System.Collections.Generic;
using Volpe.Cafe;
using Volpe.Cafe.Utils;
using RC = Volpe.Cafe.RegulatoryClass;
namespace Volpe.Cafe.Data
{
    [Serializable]
    public sealed class Vehicle
    {
        #region 
        [Serializable]
        public sealed class CDescription
        {
            #region 
            internal CDescription(double baseWeight, double baseGVWR, double baseGCWR)
            {
                this.BaseWeight = baseWeight;
                this.BaseGVWR   = baseGVWR;
                this.BaseGCWR   = baseGCWR;
            }
            #endregion
            #region 
            internal CDescription Clone()
            {
                Vehicle.CDescription vd = new Vehicle.CDescription(this.BaseWeight, this.BaseGVWR, this.BaseGCWR);
                vd.Code                = this.Code;
                vd.Manufacturer        = this.Manufacturer;
                vd.Model               = this.Model;
                vd.Nameplate           = this.Nameplate;
                vd.Platform            = this.Platform;
                vd.EngineCode          = this.EngineCode;
                vd.TransmissionCode    = this.TransmissionCode;
                vd.Origin              = this.Origin;
                vd.BaseFE              = this.BaseFE;
                vd.FuelEconomy         = this.FuelEconomy;
                vd.FuelEconomy2        = this.FuelEconomy2;
                vd.FuelShare           = this.FuelShare;
                vd.Sales               = (double[])Interaction.CloneArray(this.Sales, typeof(double));
                vd.Msrp                = (double[])Interaction.CloneArray(this.Msrp , typeof(double));
                vd.Price               = (double[])Interaction.CloneArray(this.Price, typeof(double));
                vd.RegulatoryIndicator = this.RegulatoryIndicator;
                vd.TechnologyClass     = this.TechnologyClass;
                vd.SafetyClass         = this.SafetyClass;
                vd.MarketSegment       = this.MarketSegment;
                vd.SmallCar            = this.SmallCar;
                vd.MLCar               = this.MLCar;
                vd.Truck               = this.Truck;
                vd.Van                 = this.Van;
                vd.SUV                 = this.SUV;
                vd.AWD                 = this.AWD;
                vd.Prestige            = this.Prestige;
                vd.MM                  = this.MM;
                vd.ASC1                = this.ASC1;
                vd.ASC2                = this.ASC2;
                vd.Class               = this.Class;
                vd.Style               = this.Style;
                vd.Structure           = this.Structure;
                vd.Drive               = this.Drive;
                vd.Wheelbase           = this.Wheelbase;
                vd.Footprint           = this.Footprint;
                vd.CurbWeight          = this.CurbWeight;
                vd.GVWR                = this.GVWR;
                vd.GCWR                = this.GCWR;
                vd.MaxGVWRToCW         = this.MaxGVWRToCW;
                vd.MaxGCWRToGVWR       = this.MaxGCWRToGVWR;
                vd.Seating             = this.Seating;
                vd.FuelCapacity        = this.FuelCapacity;
                vd.HybridType          = this.HybridType;
                vd.ElectricPower       = this.ElectricPower;
                vd.ElectricRange       = this.ElectricRange;
                vd.RefreshYears        = (int[])Interaction.CloneArray(this.RefreshYears , typeof(int));
                vd.RedesignYears       = (int[])Interaction.CloneArray(this.RedesignYears, typeof(int));
                vd.Percent2Dr          = this.Percent2Dr;
                vd.EmploymentHours     = this.EmploymentHours;
                vd.UsedTechnologies      = (int[])Interaction.CloneArray(this.UsedTechnologies     , typeof(int));
                vd.AvailableTechnologies = (int[])Interaction.CloneArray(this.AvailableTechnologies, typeof(int));
                return vd;
            }
            public override string ToString()
            {
                return (this.Model + "(Eng: " + this.EngineCode + ", Trn: " + this.TransmissionCode + ")");
            }
            public double CalcAverageSales()
            {
                int    count = 0;
                double sales = 0;
                for (int i = 0; i < this.Sales.Length; i++)
                {
                    if (this.Sales[i] > 0)
                    {
                        sales += this.Sales[i];
                        count++;
                    }
                }
                return (count == 0) ? 0 : sales / count;
            }
            public double CalcAverageMSRP()
            {
                int    count = 0;
                double msrp  = 0;
                for (int i = 0; i < this.Msrp.Length; i++)
                {
                    if (this.Msrp[i] > 0)
                    {
                        msrp += this.Msrp[i];
                        count++;
                    }
                }
                return (count == 0) ? 0 : msrp / count;
            }
            #endregion
            #region 
            public double PayloadCapacity { get { return this.GVWR - this.CurbWeight; } }
            public double TowingCapacity { get { return this.GCWR - this.GVWR; } }
            public double LVW { get { return this.CurbWeight + 300; } }
            public double ALVW { get { return (this.CurbWeight + this.GVWR) / 2; } }
            #endregion
            #region 
            public int Code;
            public string Manufacturer;
            public string Model;
            public string Nameplate;
            public string Platform;
            public int EngineCode;
            public int TransmissionCode;
            public char Origin;
            public FuelValue BaseFE;
            public FuelValue FuelEconomy;
            public FuelValue FuelEconomy2;
            public FuelValue FuelShare;
            public double[] Sales;
            public double[] Msrp;
            public double[] Price;
            public string RegulatoryIndicator;
            public string TechnologyClass;
            public string SafetyClass;
            public int MarketSegment;
            public int SmallCar;
            public int MLCar;
            public int Truck;
            public int Van;
            public int SUV;
            public int AWD;
            public int Prestige;
            public double MM;
            public double ASC1;
            public double ASC2;
            public string Class;
            public string Style;
            public string Structure;
            public char Drive;
            public double Wheelbase;
            public double Footprint;
            readonly public double BaseWeight;
            readonly public double BaseGVWR;
            readonly public double BaseGCWR;
            public double CurbWeight;
            public double GVWR;
            public double GCWR;
            public double MaxGVWRToCW;
            public double MaxGCWRToGVWR;
            public int Seating;
            public double FuelCapacity;
            public string HybridType;
            public double ElectricPower;
            public double ElectricRange;
            public int[] RefreshYears;
            public int[] RedesignYears;
            public double Percent2Dr;
            public double EmploymentHours;
            public int[] UsedTechnologies;
            public int[] AvailableTechnologies;
            #endregion
        }
        [Serializable]
        public sealed class CModelData
        {
            #region 
            internal CModelData()
            {
                this.TechLingerYear = new List<ModelYear>();
            }
            #endregion
            #region 
            internal Vehicle.CModelData Clone()
            {
                Vehicle.CModelData cv = new Vehicle.CModelData();
                cv.TechCost              = this.TechCost;
                cv.RegCost               = this.RegCost;
                cv.DiscCost              = this.DiscCost;
                cv.LossOfValue           = this.LossOfValue;
                cv.RelativeLossOfValue   = this.RelativeLossOfValue;
                cv.MaintenanceCost       = this.MaintenanceCost;
                cv.RepairCost            = this.RepairCost;
                cv.TaxesAndFees          = this.TaxesAndFees;
                cv.FinancingCost         = this.FinancingCost;
                cv.InsuranceCost         = this.InsuranceCost;
                cv.TaxCredit             = this.TaxCredit;
                cv.OffCycleCredit        = this.OffCycleCredit;
                cv.SalesOverFE           = this.SalesOverFE;
                cv.TechAvailable         = (bool[])Interaction.CloneArray(this.TechAvailable     , typeof(bool));
                cv.TechEnabled           = (bool[])Interaction.CloneArray(this.TechEnabled       , typeof(bool));
                cv.TechUsed              = (bool[])Interaction.CloneArray(this.TechUsed          , typeof(bool));
                cv.TechInherited         = (bool[])Interaction.CloneArray(this.TechInherited     , typeof(bool));
                cv.TechApplied           = (bool[])Interaction.CloneArray(this.TechApplied       , typeof(bool));
                cv.TechAppliedYear       = (int [])Interaction.CloneArray(this.TechAppliedYear   , typeof(int ));
                cv.TechSuperseded        = (bool[])Interaction.CloneArray(this.TechSuperseded    , typeof(bool));
                cv.TechSupersededYear    = (int [])Interaction.CloneArray(this.TechSupersededYear, typeof(int ));
                cv.TechIgnored           = (bool[])Interaction.CloneArray(this.TechIgnored       , typeof(bool));
                for (int i = 0; i < this.TechLingerYear.Count; i++)
                {
                    cv.TechLingerYear.Add(this.TechLingerYear[i]);
                }
                cv.HHUtilities           = (double[])Interaction.CloneArray(this.HHUtilities       , typeof(double));
                cv.HHUtilitiesExp        = (double[])Interaction.CloneArray(this.HHUtilitiesExp    , typeof(double));
                cv.HHProbabilities       = (double[])Interaction.CloneArray(this.HHProbabilities   , typeof(double));
                cv.HHSales               = (double[])Interaction.CloneArray(this.HHSales           , typeof(double));
                return cv;
            }
            #endregion
            #region 
            public double TechCost;
            public double RegCost;
            public double DiscCost;
            public double LossOfValue;
            public double RelativeLossOfValue;
            public double MaintenanceCost;
            public double RepairCost;
            public double TaxesAndFees;
            public double FinancingCost;
            public double InsuranceCost;
            public double TaxCredit;
            public double OffCycleCredit;
            public double SalesOverFE;
            public bool[] TechAvailable;
            public bool[] TechEnabled;
            public bool[] TechUsed;
            public bool[] TechInherited;
            public bool[] TechApplied;
            public int[] TechAppliedYear;
            public bool[] TechSuperseded;
            public int[] TechSupersededYear;
            public bool[] TechIgnored;
            public List<ModelYear> TechLingerYear;
            public double[] HHUtilities;
            public double[] HHUtilitiesExp;
            public double[] HHProbabilities;
            public double[] HHSales;
            #endregion
        }
        #endregion
        #region 
        Vehicle()
        {
            this.Description  = null;
            this.ModelData    = null;
            this.Manufacturer = null;
            this.Engine       = null;
            this.Transmission = null;
            this.Platform     = null;
            this.LegacyEng    = null;
            this.LegacyTrn    = null;
            this.RegClass        = RegulatoryClass.None;
            this.TechnologyClass = TechnologyClass.None;
            this.SafetyClass     = SafetyClass    .None;
            this.VehicleClass    = VehicleClass   .None;
            this.VehicleStyle    = VehicleStyle   .None;
            this.HEVType         = HEVType        .None;
            this.ValidYears = null;
        }
        internal Vehicle(Vehicle.CDescription description) : this()
        {
            this.Description = description;
            this.ModelData = new Vehicle.CModelData();
        }
        #endregion
        #region 
        internal Vehicle Clone()
        {
            Vehicle veh = new Vehicle();
            veh.Description  = this.Description.Clone();
            veh.ModelData    = this.ModelData  .Clone();
            veh.Manufacturer = this.Manufacturer;
            veh.Engine       = this.Engine;
            veh.Transmission = this.Transmission;
            veh.Platform     = this.Platform;
            if (this.LegacyEng != null) { veh.LegacyEng = this.LegacyEng.Clone(); }
            if (this.LegacyTrn != null) { veh.LegacyTrn = this.LegacyTrn.Clone(); }
            veh.VehicleStyle    = this.VehicleStyle;
            veh.VehicleClass    = this.VehicleClass;
            veh.RegClass        = this.RegClass;
            veh.TechnologyClass = this.TechnologyClass;
            veh.SafetyClass     = this.SafetyClass;
            veh.HEVType         = this.HEVType;
            if (this.ValidYears != null)
            {
                veh.ValidYears = new ModelYear[this.ValidYears.Length];
                for (int i = 0; i < this.ValidYears.Length; i++)
                {
                    veh.ValidYears[i] = new ModelYear(this.ValidYears[i].Year);
                }
            }
            return veh;
        }
        internal void Initialize(Manufacturer manufacturer, Vehicle[] vehicles)
        {
            this.Manufacturer = manufacturer;
            this.Engine       = null;
            this.Transmission = null;
            Engine      [] engs = manufacturer.Engines      .ToArray();
            Transmission[] trns = manufacturer.Transmissions.ToArray();
            if (this.Description.EngineCode > 0)
            {
                for (int i = 0; i < engs.Length; i++)
                {
                    if (engs[i].Description.Code == this.Description.EngineCode)
                    {   
                        this.Engine = engs[i];
                        this.Engine.Vehicles.Add(this);
                        break;
                    }
                }
            }
            if (this.Description.TransmissionCode > 0)
            {
                for (int i = 0; i < trns.Length; i++)
                {
                    if (trns[i].Description.Code == this.Description.TransmissionCode)
                    {   
                        this.Transmission = trns[i];
                        this.Transmission.Vehicles.Add(this);
                        break;
                    }
                }
            }
            int yearCount = this.Description.Sales.Length;
            int slMsrpYrs = 0;
            for (int i = 0; i < yearCount; i++)
            {
                if (this.Description.Sales[i] > 0 && this.Description.Msrp[i] > 0) { slMsrpYrs++; }
            }
            this.ValidYears = new ModelYear[slMsrpYrs];
            for (int i = 0, j = 0; i < yearCount; i++)
            {
                bool isValid = (this.Description.Sales[i] > 0 && this.Description.Msrp[i] > 0);
                if (isValid) { this.ValidYears[j++] = new ModelYear(i + ModelYear.MinYear); }
            }
            bool isEV_FCV = (this.Description.FuelEconomy.PrimaryFuel == FuelType.Electricity ||
                this.Description.FuelEconomy.PrimaryFuel == FuelType.Hydrogen);
            bool isPHEV = false;
            if (this.ModelData.TechUsed != null)
            {
                for (int i = 0; i < TechnologyIndexes.TechnologyCount; i++)
                {
                    if (this.ModelData.TechUsed[i])
                    {
                        if (TechnologyIndexes.IsConversionToPHEV(i)) { isPHEV = true; }
                        if (TechnologyIndexes.IsConversionToEV  (i) ||
                            TechnologyIndexes.IsConversionToFCV (i)) { isEV_FCV = true; }
                    }
                }
            }
            for (int i = 0; i < this.Description.UsedTechnologies.Length; i++)
            {
                if (TechnologyIndexes.IsConversionToPHEV(this.Description.UsedTechnologies[i])) { isPHEV = true; }
                if (TechnologyIndexes.IsConversionToEV  (this.Description.UsedTechnologies[i]) ||
                    TechnologyIndexes.IsConversionToFCV (this.Description.UsedTechnologies[i])) { isEV_FCV = true; }
            }
            if (!isEV_FCV)
            {
                string hdr = "Vehicle (Code:" + this.Description.Code + ",Mfr:" + this.Description.Manufacturer + "):  ";
                if (this.Engine       == null           ) { throw new Exception("\n\n" + hdr + "Engine Code is not valid."); }
                if (this.Transmission == null && !isPHEV) { throw new Exception("\n\n" + hdr + "Transmission Code is not valid."); }
            }
            this.AssignVehStyleAndVehClass();
            this.AssignTechnologyClass();
            this.AssignSafetyClass();
        }
        void AssignVehStyleAndVehClass()
        {
            switch (this.Description.Style.ToUpper())
            {
                case "CONVERTIBLE"  : this.VehicleStyle = VehicleStyle.Convertible ; break;
                case "COUPE"        : this.VehicleStyle = VehicleStyle.Coupe       ; break;
                case "HATCHBACK"    : this.VehicleStyle = VehicleStyle.Hatchback   ; break;
                case "SEDAN"        : this.VehicleStyle = VehicleStyle.Sedan       ; break;
                case "WAGON"        : this.VehicleStyle = VehicleStyle.Wagon       ; break;
                case "MINIVAN"      : this.VehicleStyle = VehicleStyle.Minivan     ; break;
                case "VAN"          : this.VehicleStyle = VehicleStyle.Van         ; break;
                case "SPORT UTILITY": this.VehicleStyle = VehicleStyle.SportUtility; break;
                case "PICKUP"       : this.VehicleStyle = VehicleStyle.Pickup      ; break;
                case "CHASSIS CAB"  : this.VehicleStyle = VehicleStyle.ChassisCab  ; break;
                case "CUTAWAY"      : this.VehicleStyle = VehicleStyle.Cutaway     ; break;
                default             : this.VehicleStyle = VehicleStyle.None        ; break;
            }
            if (this.VehicleStyle == VehicleStyle.Convertible || this.VehicleStyle == VehicleStyle.Coupe ||
                this.VehicleStyle == VehicleStyle.Hatchback   || this.VehicleStyle == VehicleStyle.Sedan ||
                this.VehicleStyle == VehicleStyle.Wagon)
            {   
                this.VehicleClass = VehicleClass.LDV;
            }
            else
            {   
                this.VehicleClass = (this.Description.GVWR >= 33001) ? VehicleClass.HDT8  : 
                                    (this.Description.GVWR >= 26001) ? VehicleClass.HDT7  :
                                    (this.Description.GVWR >= 19501) ? VehicleClass.MDT6  :
                                    (this.Description.GVWR >= 16001) ? VehicleClass.MDT5  :
                                    (this.Description.GVWR >= 14001) ? VehicleClass.MDT4  :
                                    (this.Description.GVWR >= 10001) ? VehicleClass.LDT3  :
                                    (this.Description.GVWR >=  8501) ? VehicleClass.LDT2b :
                                    (this.Description.GVWR >=  6001) ? VehicleClass.LDT2a :
                                                                       VehicleClass.LDT1;
            }
        }
        void AssignTechnologyClass()
        {
            string[] techClasses =
                new string[] {
                                 "SUBCOMPACT PC", "SUBCOMPACT PERF. PC", "COMPACT PC", "COMPACT PERF. PC",
                                 "MIDSIZE PC"   , "MIDSIZE PERF. PC"   , "LARGE PC"  , "LARGE PERF. PC"  ,
                                 "MINIVAN LT"   , "SMALL LT"           , "MIDSIZE LT", "LARGE LT"        ,
                                 "TRUCK 2B3"    , "VAN 2B3"
                             };
            this.TechnologyClass = TechnologyClass.None;
            for (int i = 0; i < techClasses.Length; i++)
            {
                if (Interaction.StringCompare(this.Description.TechnologyClass, techClasses[i], true))
                {
                    this.TechnologyClass = (TechnologyClass)(i + 1);
                }
            }
        }
        void AssignSafetyClass()
        {
            switch (this.Description.SafetyClass.ToUpper())
            {
                case "PC": this.SafetyClass = SafetyClass.PC  ; break;
                case "LT": this.SafetyClass = SafetyClass.LT  ; break;
                case "CM": this.SafetyClass = SafetyClass.CM  ; break;
                default  : this.SafetyClass = SafetyClass.None; break;
            }
        }
        internal void SetEngine(Engine engine)
        {
            this.Engine = engine;
            this.Description.EngineCode = engine.Description.Code;
        }
        internal void SetTransmission(Transmission transmission)
        {
            this.Transmission = transmission;
            this.Description.TransmissionCode = transmission.Description.Code;
        }
        internal void SetPlatform(Platform platform)
        {
            this.Platform = platform;
            this.Description.Platform = platform.Name;
        }
        public void RemoveEngine()
        {
            if (this.Engine != null)
            {   
                this.LegacyEng = this.Engine.Description.Clone();
                this.Description.ElectricPower = this.LegacyEng.Horsepower;
                this.Engine.RemoveVehicle(this);
                this.Description.EngineCode = -1;
                this.Engine = null;
            }
        }
        public void RemoveTransmission()
        {
            if (this.Transmission != null)
            {   
                this.LegacyTrn = this.Transmission.Description.Clone();
                this.Transmission.RemoveVehicle(this);
                this.Description.TransmissionCode = -1;
                this.Transmission = null;
            }
        }
        void GetRedesignState(ModelYear modelYear, out bool atRedesign, out bool inShadowOfRedesign)
        {
            atRedesign = inShadowOfRedesign = false;    
            int year = modelYear.Year;
            int[] redesignYears = this.Description.RedesignYears;
            for (int i = 0, count = redesignYears.Length; i < count; i++)
            {
                if (redesignYears[i] == year) { atRedesign = true; break; }
            }
            if (!atRedesign)
            {
                for (int i = 0, count = redesignYears.Length; i < count; i++)
                {
                    int delta = Math.Abs(redesignYears[i] - year);    
                    if (0 < delta && delta < 5) { inShadowOfRedesign = true; break; }
                }
            }
        }
        void GetRefreshState(ModelYear modelYear, out bool atRefresh, out bool inShadowOfRefresh)
        {
            atRefresh = inShadowOfRefresh = false;
            int year = modelYear.Year;
            int[] refreshYears = this.Description.RefreshYears;
            for (int i = 0, count = refreshYears.Length; i < count; i++)
            {
                if (refreshYears[i] == year) { atRefresh = true; break; }
            }
            if (!atRefresh)
            {
                for (int i = 0, count = refreshYears.Length; i < count; i++)
                {
                    int delta = Math.Abs(refreshYears[i] - year);    
                    if (0 < delta && delta < 2) { inShadowOfRefresh = true; break; }
                }
            }
        }
        public bool IsAtRedesign(ModelYear year)
        {
            bool atRedesign, shadow;
            this.GetRedesignState(year, out atRedesign, out shadow);
            return atRedesign;
        }
        public bool IsAtRefresh(ModelYear year)
        {
            bool atRefresh, shadow;
            this.GetRefreshState(year, out atRefresh, out shadow);
            return atRefresh;
        }
        public double CalcLaborHours(ModelYear year)
        {
            return this.CalcLaborHours(year.Index);
        }
        public double CalcLaborHours(ModelYear minYear, ModelYear maxYear)
        {
            double hours = 0;
            for (int i = minYear.Index; i <= maxYear.Index; i++)
            {
                hours += this.CalcLaborHours(i);
            }
            return hours;
        }
        double CalcLaborHours(int yrIndex)
        {
            return this.Description.EmploymentHours * this.Description.Sales[yrIndex] / 1000;
        }
        #endregion
        #region 
        public Vehicle.CDescription Description { get; private set; }
        public Vehicle.CModelData ModelData { get; private set; }
        public Manufacturer Manufacturer { get; private set; }
        public Engine Engine { get; private set; }
        public Transmission Transmission { get; private set; }
        public Platform Platform { get; private set; }
        public Engine.CDescription LegacyEng { get; private set; }
        public Transmission.CDescription LegacyTrn { get; private set; }
        public RegulatoryClass RegClass { get; set; }
        public TechnologyClass TechnologyClass { get; private set; }
        public SafetyClass SafetyClass { get; private set; }
        public VehicleClass VehicleClass { get; private set; }
        public VehicleStyle VehicleStyle { get; private set; }
        public HEVType HEVType { get; set; }
        public ModelYear[] ValidYears { get; private set; }
        public ModelYear MinYear { get { return (this.ValidYears == null) ? null : this.ValidYears[0]; } }
        public ModelYear MaxYear { get { return (this.ValidYears == null) ? null : this.ValidYears[this.ValidYears.Length - 1]; } }
        #endregion
    }
}

