#region << Using Directives >>
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Threading;
using Volpe.Cafe;
using Volpe.Cafe.Data;
using Volpe.Cafe.IO;
using Volpe.Cafe.Settings;
using TI = Volpe.Cafe.TechnologyIndexes;
using Volpe.Cafe.Generic;
using System.Text;
#endregion
namespace Volpe.Cafe.Model
{
    [Serializable]
    [ModelDescription("Standard Compliance Model", "Runs a regular compliance model that estimates technology costs and " +
         "benefits under scenarios with predefined flat or reformed CAFE standards.", 1.0F)]
    public class Compliance : ComplianceBase
    {
        #region 
        public Compliance() : base() { }
        #endregion
        #region 
        #region 
        public override ICompliance CreateNew()
        {
            return new Compliance();
        }
        protected override void StartInternal()
        {
            if (this.PreProcess())
            {   
                this.ScenarioLoop();
                this.PostProcess();
            }
        }
        #endregion
        #region 
        protected virtual bool PreProcess()
        {
            if (this.State == ModelingState.Completed) { return false; }
            for (int i = 0; i < this.MfrCount; i++)
            {
                Manufacturer mfr = this.Data.Manufacturers[i];
                mfr.ModelData = new Manufacturer.CModelData();
                Estimates priceEstimates = this.Settings.OperatingModes.FuelPriceEstimates;
                mfr.ModelData.KMY    = KMYType.GetKmyValues(this.Settings.Parameters, priceEstimates, mfr, false);
                mfr.ModelData.KMY_OC = KMYType.GetKmyValues(this.Settings.Parameters, priceEstimates, mfr, true);
            }
            this._scen = null;
            this._year = null;
            this._mfr  = null;
            this._effectsYear = -1;
            this._effectsModel = new EffectsModel(this.Settings, this.MinYear, this.MaxYear, this.MaxEffectsYear);
            return true;
        }
        protected virtual void PostProcess()
        {
        }
        #endregion
        #region 
        #region 
        protected virtual void ScenarioLoop()
        {
            for (int i = 0; i < this.ScenCount; i++)
            {   
                Scenario scen = this.Settings.Scenarios[i];
                Industry data = this.Data.Clone();
                this.RunScenario(scen, data);
                if (this.AbortRequested) { break; }
            } 
        }
        protected virtual void RunScenario(Scenario scen, Industry data)
        {
            this.PreProcessScenario (scen, data);
            this.ModelYearLoop      (scen, data);
            this.PostProcessScenario(scen);
        }
        protected virtual void PreProcessScenario(Scenario scen, Industry data)
        {
            this._scen = scen;
            this._year = null;
            this._mfr  = null;
            this._effectsYear = -1;
            this.ResetActiveScenarioData();
            this.OnScenarioStarted();
            this._lcf = new LinearComplianceFinder(scen, this.MinYear, this.MaxYear, this.Settings, this.LogWriter);
            data.ModelData.TechUsedSales = new RCDouble[TI.TechnologyCount];
            for (int j = 0; j < TI.TechnologyCount; j++)
            {
                data.ModelData.TechUsedSales[j] = new RCDouble();
            }
            for (int i = 0; i < this.MfrCount; i++)
            {
                Manufacturer            mfr = data.Manufacturers[i];
                Manufacturer.CModelData mmd = mfr.ModelData;
                mmd.TechUsedSales     = new RCDouble[TI.TechnologyCount];
                mmd.TechAppliedSales  = new RCDouble[TI.TechnologyCount];
                mmd.TechExhausted     = new bool    [TI.TechnologyCount];
                mmd.TechPhaseInOffset = new int     [TI.TechnologyCount];
                for (int j = 0; j < TI.TechnologyCount; j++)
                {
                    mmd.TechUsedSales    [j] = new RCDouble();
                    mmd.TechAppliedSales [j] = new RCDouble();
                }
                foreach (Vehicle veh in mfr.Vehicles)
                {
                    veh.RegClass = Standards.GetRegClass(veh);              
                    TechnologyApplicability.Initialize(veh, this.Settings); 
                }
                foreach (Engine       eng in mfr.Engines      ) { TechnologyApplicability.Initialize(eng, this.Settings); }
                foreach (Transmission trn in mfr.Transmissions) { TechnologyApplicability.Initialize(trn, this.Settings); }
                foreach (Platform     plt in mfr.Platforms    ) { TechnologyApplicability.Initialize(plt, this.Settings); }
            } 
        }
        protected virtual void PostProcessScenario(Scenario scen)
        {
            if (this.AbortRequested) { return; }
            for (int i = 0; i < this.YearCount; i++)
            {   
                ModelYear year     = this.ModelYears[i];
                Industry  data     = this.GetData(scen, year);
                Industry  prevData = (i == 0) ? null : this.GetData(scen, this.ModelYears[i - 1]);
                for (int j = 0; j < this.MfrCount; j++)
                {   
                    Manufacturer mfr     =                   data    .Manufacturers[j];
                    Manufacturer prevMfr = (i == 0) ? null : prevData.Manufacturers[j];
                    for (int k = 0; k < mfr.Engines.Count; k++)
                    {
                        mfr.Engines[k].UpdateComponentVersion(year, (i == 0) ? null : prevMfr.Engines[k]);
                    }
                    for (int k = 0; k < mfr.Transmissions.Count; k++)
                    {
                        mfr.Transmissions[k].UpdateComponentVersion(year, (i == 0) ? null : prevMfr.Transmissions[k]);
                    }
                    for (int k = 0; k < mfr.Platforms.Count; k++)
                    {
                        mfr.Platforms[k].UpdateComponentVersion(year, (i == 0) ? null : prevMfr.Platforms[k]);
                    }
                }
                if (this.AbortRequested) { break; }
            } 
            if (this.Settings.OperatingModes.FleetAnalysis)
            {   
                ModelYear year  = new ModelYear(this.MaxYear);
                Scenario  bScen = this.Settings.Scenarios[0];
                Industry  bData = this.GetData(bScen, year);
                Industry  data  = this.GetData(scen , year);
                for (int i = this.MinEffectsYear; i < this.MinYear; i++)
                {
                    EffectsData effectsData;
                    this._effectsYear = i;
                    this._effectsModel.CalculateHistoricEffects(scen, i, data.VehClasses, out effectsData);
                    this.SetEffectsData(i, effectsData);
                    if (this.AbortRequested) { return; }
                }
                for (int i = this.MaxYear + 1; i <= this.MaxEffectsYear; i++)
                {
                    EffectsData effectsData;
                    this._effectsYear = i;
                    this._effectsModel.CalculateEffects(scen, i, bData.Vehicles, data.Vehicles, out effectsData);
                    this.SetEffectsData(i, effectsData);
                    if (this.AbortRequested) { return; }
                }
                this._effectsYear = -1;
            }
            this._lcf = null;
            this.OnScenarioCompleted();
        }
        #endregion
        #region 
        protected virtual void ModelYearLoop(Scenario scen, Industry data)
        {
            this.SetActiveScenarioData(this.ModelYears[0], data);
            for (int i = 0; i < this.YearCount; i++)
            {   
                ModelYear year = this.ModelYears[i];
                this.RunModelYear(scen, year, true);
                if (this.AbortRequested) { break; }
            } 
        }
        protected virtual void RunModelYear(Scenario scen, ModelYear year, bool delayPostProcessing)
        {
            this.PreProcessModelYear(scen, year);
            this.ManufacturerLoop   (scen, year);
            if (!delayPostProcessing || !this.Settings.OperatingModes.MultiYearModeling || year.Year == this.MaxYear)
            {
                this.PostProcessModelYear(scen, year);
            }
        }
        protected virtual void PreProcessModelYear(Scenario scen, ModelYear year)
        {
            this._year = year;
            this.OnModelYearStarted();
            int yrIndex = year.Index;
            Industry prevData = null, data = null;
            if (yrIndex != this.MinYearIndex)
            {   
                prevData = this.GetData(scen, this.ModelYears[yrIndex - this.MinYearIndex - 1]);
                this.SetActiveScenarioData(year, prevData.Clone());
            }
            data = this.GetData(scen, year);
            if (this.Settings.OperatingModes.DynamicFleetShare)
            {
                DynamicFleetShare.AdjustFleetShare(data, prevData, this.Settings, scen, year);
            }
            Industry.CModelData imd = data.ModelData;
            for (int i = 0; i < TI.TechnologyCount; i++) { imd.TechUsedSales[i].Clear(); }
            List<Technology> technologies = this.Settings.Technologies;
            for (int i = 0; i < this.MfrCount; i++)
            {   
                Manufacturer            mfr = data.Manufacturers[i];
                Manufacturer.CModelData mmd = mfr.ModelData;
                mmd.PreliminaryStandard.Clear();
                mmd.Standard           .Clear();
                mmd.TCreditsInCapped   .Clear();
                mmd.TCreditsIn         .Clear();
                mmd.TCreditsOut        .Clear();
                mmd.TechCost           .Clear();
                mmd.RegCost            .Clear();
                mmd.LossOfValue        .Clear();
                mmd.RelativeLossOfValue.Clear();
                mmd.MaintenanceCost    .Clear();
                mmd.RepairCost         .Clear();
                mmd.TaxesAndFees       .Clear();
                mmd.FinancingCost      .Clear();
                mmd.InsuranceCost      .Clear();
                for (int j = 0; j < TI.TechnologyCount; j++)
                {   
                    mmd.TechUsedSales   [j].Clear();
                    mmd.TechAppliedSales[j].Clear();
                    mmd.TechExhausted   [j] = false;
                }
                List<Vehicle> mfrVehs = mfr.Vehicles;
                for (int j = 0, mfrVehCount = mfrVehs.Count; j < mfrVehCount; j++)
                {
                    TechnologyApplicability.Enable(mfrVehs[j], year, -1, this.Settings);
                }
                for (int j = 0, mfrVehCount = mfrVehs.Count; j < mfrVehCount; j++)
                {   
                    Vehicle              veh = mfrVehs[j];
                    Vehicle.CDescription vd  = veh.Description;
                    Vehicle.CModelData   vmd = veh.ModelData;
                    RegulatoryClass regClass = veh.RegClass;
                    for (int k = 0; k < TI.TechnologyCount; k++)
                    {
                        if (vmd.TechUsed[k])
                        {   
                            imd.TechUsedSales[k][regClass] += vd.Sales[yrIndex];
                            mmd.TechUsedSales[k][regClass] += vd.Sales[yrIndex];
                            if (vmd.TechApplied[k])
                            {   
                                mmd.TechAppliedSales[k][regClass] += vd.Sales[yrIndex];
                            }
                        }
                    } 
                } 
                if (yrIndex == this.MinYearIndex)
                {
                    TechnologyApplicability.ComputeMfrAdjustedPhaseIn(mfr, year, this.Settings);
                } 
            } 
            RCDouble indStandard = Standards.GetIndustryStandard(data, scen, year);
            for (int i = 0; i < this.MfrCount; i++)
            {   
                Manufacturer            mfr     = data.Manufacturers[i];
                Manufacturer.CModelData mmd     = mfr .ModelData;
                List<Vehicle>           mfrVehs = mfr .Vehicles;
                for (int j = 0, mfrVehCount = mfrVehs.Count; j < mfrVehCount; j++)
                {   
                    Vehicle              veh           = mfrVehs[j];
                    Vehicle.CDescription vd            = veh.Description;
                    Vehicle.CModelData   vmd           = veh.ModelData;
                    double               vehCost       = 0;
                    double               vehMaintCost  = 0;
                    double               vehRepairCost = 0;
                    double               vehLossValue  = 0;
                    TechnologyClass      techClass     = veh.TechnologyClass;
                    RegulatoryClass      regClass      = veh.RegClass;
                    VehicleClass         vehClass      = veh.VehicleClass;
                    for (int k = 0; k < TI.TechnologyCount; k++)
                    {
                        Technology tech = technologies[k];
                        if (vmd.TechUsed[k] && vmd.TechApplied[k])
                        {   
                            vehCost       += TechnologyApplicability.GetCost           (tech, veh, data, year, this.MinYear, this.MaxYear);
                            vehMaintCost  += TechnologyApplicability.GetMaintenanceCost(tech, veh,       year);
                            vehRepairCost += TechnologyApplicability.GetRepairCost     (tech, veh,       year);
                            vehLossValue  += tech.Attributes[veh].LossOfValue;
                        }
                    } 
                    if (scen.ScenInfo[regClass].IncludeAC[year.Index])
                    {
                        vehCost += scen.ScenInfo[regClass].ACCost[year.Index];
                    }
                    double vehSales = vd.Sales[yrIndex];
                    vmd.TechCost                   =             vehCost       ;
                    vmd.LossOfValue                =             vehLossValue  ;
                    vmd.MaintenanceCost            =             vehMaintCost  ;
                    vmd.RepairCost                 =             vehRepairCost ;
                    mmd.TechCost       [regClass] += (vehSales * vehCost      );
                    mmd.LossOfValue    [regClass] += (vehSales * vehLossValue );
                    mmd.MaintenanceCost[regClass] += (vehSales * vehMaintCost );
                    mmd.RepairCost     [regClass] += (vehSales * vehRepairCost);
                } 
                mmd.AverageStandard = indStandard;                      
                mmd.Sales           = Standards.GetSales(mfr, year);    
                if (!this.Settings.OperatingModes.IgnorePhaseIn)
                {   
                    for (int j = 0; j < TI.TechnologyCount; j++)
                    {
                        TechnologyApplicability.CheckPhaseIn(this.Settings, mmd, technologies[j], year, this.MinYearIndex);
                    } 
                }
            } 
        }
        protected virtual void PostProcessModelYear(Scenario scen, ModelYear year)
        {
            if (this.AbortRequested) { return; }
            int      techCount = TI.TechnologyCount;
            Industry data      = this.GetData(scen, year);
            Industry bData     = (scen.Index == 0) ? data : this.GetData(this.Settings.Scenarios[0], year);
            Standards.CalcVehicleTaxCredit(data, scen, year);
            MarketSimulation.AllocateCosts(data, this.Settings, scen, year);
            EconomicValues economicValues = this.Settings.Parameters.EconomicValues;
            for (int i = 0, mfrCount = this.MfrCount; i < mfrCount; i++)
            {
                Manufacturer            mfr  = data.Manufacturers[i];
                Manufacturer.CModelData mmd  = mfr .ModelData;
                List<Vehicle>           vehs = mfr .Vehicles;
                mmd.DiscCost           .Clear();
                mmd.LossOfValue        .Clear();
                mmd.RelativeLossOfValue.Clear();
                mmd.TaxesAndFees       .Clear();
                mmd.FinancingCost      .Clear();
                mmd.InsuranceCost      .Clear();
                for (int j = 0, vehCount = vehs.Count; j < vehCount; j++)
                {
                    Vehicle              veh      = vehs[j];
                    Vehicle.CModelData   vmd      = veh.ModelData;
                    Vehicle.CDescription vd       = veh.Description;
                    double               vehMsrp  = vd .Msrp [year.Index] + vmd.RegCost;
                    double               vehSales = vd .Sales[year.Index];
                    RegulatoryClass      vehRC    = veh.RegClass;
                    VehicleClass         vehClass = veh.VehicleClass;
                    OperatingCosts       opCosts  = economicValues.OperatingCosts[vehClass];
                    double               drExp    = Math.Max(0, year.Year - economicValues.DRBaseYear[vehClass]);
                    double               discRate = (economicValues.DRBaseYear[vehClass] <= 0) ? 1 : Math.Pow(1 + economicValues.DiscountRate[vehClass], -drExp);
                    vmd.DiscCost      = vmd.TechCost    * discRate;
                    vmd.LossOfValue   = vmd.LossOfValue * discRate;
                    if (vehs[j].HEVType == HEVType.PureElectric)
                    {
                        vmd.RelativeLossOfValue = vehMsrp * opCosts.RelativeValueLoss;
                    }
                    vmd.TaxesAndFees  = vehMsrp * opCosts.TaxesAndFees;
                    vmd.FinancingCost = vehMsrp * opCosts.Financing;
                    vmd.InsuranceCost = vehMsrp * opCosts.Insurance;
                    mmd.DiscCost           [vehRC] += (vehSales * vmd.DiscCost           );
                    mmd.LossOfValue        [vehRC] += (vehSales * vmd.LossOfValue        );
                    mmd.RelativeLossOfValue[vehRC] += (vehSales * vmd.RelativeLossOfValue);
                    mmd.TaxesAndFees       [vehRC] += (vehSales * vmd.TaxesAndFees       );
                    mmd.FinancingCost      [vehRC] += (vehSales * vmd.FinancingCost      );
                    mmd.InsuranceCost      [vehRC] += (vehSales * vmd.InsuranceCost      );
                }
                mmd.TotalConsumerCosts = mmd.DiscCost + mmd.Fines + mmd.TaxesAndFees + mmd.FinancingCost + mmd.InsuranceCost +
                                         mmd.MaintenanceCost + mmd.RepairCost + mmd.LossOfValue + mmd.RelativeLossOfValue;
                mmd.TotalSocialCosts   = mmd.DiscCost +
                                         mmd.MaintenanceCost + mmd.RepairCost + mmd.LossOfValue + mmd.RelativeLossOfValue;
            }
            EffectsData effectsData;
            this._effectsModel.CalculateEffects(scen, year.Year, bData.Vehicles, data.Vehicles, out effectsData);
            this.SetEffectsData(year.Year, effectsData);
            if (this.Settings.OperatingModes.MultiYearModeling)
            {   
                if (year.Year > this.MinYear)
                {   
                    this.PostProcessModelYear(scen, this.ModelYears[year.Year - this.MinYear - 1]);
                }
            }
            if (year.Year == this._year.Year)
            {
                this.OnModelYearCompleted();
            }
        }
        #endregion
        #region 
        protected virtual void ManufacturerLoop(Scenario scen, ModelYear year)
        {
            for (int i = 0; i < this.MfrCount; i++)
            {   
                this.RunManufacturer(scen, year, i);
                if (this.AbortRequested) { break; }
            } 
        }
        protected virtual void RunManufacturer(Scenario scen, ModelYear year, int mfrIndex)
        {
            this.PreProcessManufacturer(scen, year, mfrIndex);
            OperatingModes            opModes      = this.Settings.OperatingModes;
            int                       yrIndex      = year.Index;
            Industry                  data         = this.GetData(scen, year);
            Manufacturer              mfr          = data.Manufacturers[mfrIndex];
            Manufacturer.CModelData   mmd          = mfr.ModelData;
            Manufacturer.CDescription mfrDescr     = mfr.Description;
            ModelingApproach          approach     = opModes.ModelingApproach;
            bool                      noFines      = opModes.NoFines || !mfrDescr.WillingToPayFines[year.Index];
            double                    mfrTechCost  = mmd.TechCost.Total;
            double                    mfrFines     = mmd.Fines   .Total;
            bool                      ignoredReset = false;
            bool                      allowCT      = opModes.AllowCreditTrading;
            bool                      allowOC      = opModes.Overcomply && (opModes.OvercomplyInBaseline || !scen.IsBaseline);
            bool                      doOvercomply = allowOC && (mfrFines <= 0);
            ComplianceFinding finding = null;
            mmd.CFExhausted = false;
            while (mfrFines > 0 || doOvercomply)
            {   
                finding = this.ComplianceFindingLoop(scen, year, mfrIndex, noFines, ref approach, doOvercomply, allowCT);
                if (finding == null)
                {   
                    if (ignoredReset)
                    {   
                        mmd.CFExhausted = true;
                        break;
                    }
                    else
                    {   
                        List<Vehicle> mfrVehs = mfr.Vehicles;
                        for (int i = 0, vehCount = mfrVehs.Count; i < vehCount; i++)
                        {
                            Vehicle.CModelData vmd = mfrVehs[i].ModelData;
                            for (int j = 0; j < TI.TechnologyCount; j++) { vmd.TechIgnored[j] = false; }
                        }
                        ignoredReset = true;    
                    }
                }
                else if (!finding.IsValid)
                {   
                    throw new Exception("The compliance finding is not valid!\r\n\r\n" +
                        "This should never happen and typically indicates an internal error in the modeling system.");
                }
                else
                {   
                    List<RegulatoryClass> findingRCs = finding.RegClasses;
                    bool applyCredits = false;
                    if (!allowOC && allowCT)
                    {
                        foreach (RegulatoryClass fRC in findingRCs)
                        {
                            double fineRate = scen.ScenInfo[fRC].FineRate[yrIndex];
                            if (finding.CostPerMPG > fineRate &&
                                CreditTrading.CanCarryForward(fRC, this.GetData(scen), scen, year, mfrIndex, this.Settings))
                            {
                                applyCredits = true;
                                break;
                            }
                        }
                        if (!applyCredits && findingRCs.Count == 1)
                        {
                            double fineRate = scen.ScenInfo[findingRCs[0]].FineRate[yrIndex];
                            applyCredits = CreditTrading.CanFleetTransfer(findingRCs[0], this.GetData(scen), scen, year, mfrIndex, this.Settings);
                            if (applyCredits)
                            {
                                RegulatoryClass eRC = (findingRCs[0] == RegulatoryClass.LightTruck) ? RegulatoryClass.PassengerCar : RegulatoryClass.LightTruck;
                                double adjFactor = CreditTrading.GetCreditAdjFactorFT(eRC, findingRCs[0], mfr, year, this.Settings.Parameters.CreditTradingValues);
                                applyCredits = (finding.CostPerMPG > fineRate * adjFactor);
                            }
                        }
                    }
                    if (applyCredits)
                    {   
                        foreach (RegulatoryClass fRC in findingRCs)
                        {
                            CreditTrading.CT_CarryForward(fRC, this.GetData(scen), scen, year, mfrIndex, this.Settings, false);
                        }
                        if (findingRCs.Count == 1)
                        {
                            CreditTrading.CT_FleetTransfers(findingRCs[0], this.GetData(scen), scen, year, mfrIndex, this.Settings, false);
                        }
                        this.Recomply(scen, year, mfr);
                    }
                    else if (mfrFines > 0 || (doOvercomply && finding.IsEfficient)) 
                    {   
                        finding.ApplyFinding();
                        this.Recomply(scen, year, mfr);
                    }
                    else { break; }
                }
                mfrTechCost = mmd.TechCost.Total;
                mfrFines    = mmd.Fines   .Total;
                if (mfrFines <= 0)
                {   
                    doOvercomply = allowOC;
                }
                if (this.AbortRequested) { break; }
            } 
            if (mfrFines > 0)
            {
                CreditTrading.ExamineCredits(this.GetData(scen), scen, year, mfrIndex, this.Settings);
                this.Recomply(scen, year, mfr);
            }
            this.PostProcessManufacturer(scen, year, mfrIndex);
        }
        protected virtual void PreProcessManufacturer(Scenario scen, ModelYear year, int mfrIndex)
        {
            Manufacturer mfr = this.GetData(scen, year).Manufacturers[mfrIndex];
            this._mfr = mfr;
            this.OnManufacturerStarted();
            this.Recomply(scen, year, mfr);
            this.InheritTechnologies(scen, year, mfr, mfrIndex);
            this.Recomply(scen, year, mfr);
            CreditTrading.CT_CarryForward  (this.GetData(scen), scen, year, mfrIndex, this.Settings, true);
            CreditTrading.CT_FleetTransfers(this.GetData(scen), scen, year, mfrIndex, this.Settings, true);
            this.Recomply(scen, year, mfr);
        }
        protected virtual void PostProcessManufacturer(Scenario scen, ModelYear year, int mfrIndex)
        {
            if (this.AbortRequested) { return; }
            int          yrIndex = year.Index;
            Industry     data    = this.GetData(scen, year);
            Manufacturer mfr     = data.Manufacturers[mfrIndex];
            this.Recomply(scen, year, mfr);
            this.InheritTechnologies(scen, year, mfr, mfrIndex);
            this.Recomply(scen, year, mfr);
            CreditTrading.CT_CarryBackward(this.GetData(scen), scen, year, mfrIndex, this.Settings);
            this.Recomply(scen, year, mfr);
            if (this.Settings.OperatingModes.MultiYearModeling && this.Settings.OperatingModes.MultiYearStartYear <= year.Year)
            {   
                List<Vehicle> mfrVehs = mfr.Vehicles;
                for (int i = 0, mfrVehCount = mfrVehs.Count; i < mfrVehCount; i++)
                {   
                    Vehicle              veh = mfrVehs[i];
                    Vehicle.CDescription vd  = veh.Description;
                    Vehicle.CModelData   vmd = veh.ModelData;
                    if (veh.IsAtRedesign(year))
                    {
                        vmd.TechLingerYear.Add(year);
                    } 
                }
                for (int i = this.MinYearIndex; i < yrIndex; i++)
                {
                    ModelYear prevYear = this.ModelYears[i - this.MinYearIndex];
                    Industry prevData = this.GetData(scen, prevYear);
                    Manufacturer lMfr = prevData.Manufacturers[mfrIndex];
                    this.Recomply(scen, prevYear, lMfr);
                }
            } 
            this.OnManufacturerCompleted();
        }
        protected virtual void Recomply(Scenario scen, ModelYear year, Manufacturer mfr)
        {
            Manufacturer.CModelData mmd = mfr.ModelData;
            Standards.CalcStandard(mfr, scen, year               );
            Standards.CalcCAFE    (mfr, scen, year, this.Settings);     
            Standards.CalcCredits (mfr, scen, year, this.Settings);
            Standards.CalcFines   (mfr, scen, year, this.Settings);
        }
        void InheritTechnologies(Scenario scen, ModelYear year, Manufacturer mfr, int mfrIndex)
        {
            if (this.Settings.OperatingModes.TechnologyInheriting)
            {   
                for (int i = 0; i < 3; i++)
                {
                    foreach (Technology tech in this.Settings.Technologies)
                    {
                        if (tech.Type == TechnologyType.Engine || tech.Type == TechnologyType.DieselEngine)
                        {
                            foreach (Engine value in mfr.Engines)
                            {
                                this.InheritTechnology(scen, year, mfr, mfrIndex, value, tech);
                            }
                        }
                        else if (tech.Type == TechnologyType.Transmission)
                        {
                            foreach (Transmission value in mfr.Transmissions)
                            {
                                this.InheritTechnology(scen, year, mfr, mfrIndex, value, tech);
                            }
                        }
                        else if (tech.Type == TechnologyType.Aerodynamics || tech.Type == TechnologyType.MR)
                        {
                            foreach (Platform value in mfr.Platforms)
                            {
                                this.InheritTechnology(scen, year, mfr, mfrIndex, value, tech);
                            }
                        }
                    } 
                }
            } 
        }
        void InheritTechnology(Scenario scen, ModelYear year, Manufacturer mfr, int mfrIndex, Component component, Technology tech)
        {
            if (!component.ModelData.TechUsed[tech.Index]) { return; }
            TechnologyType   techType  = tech.Type;
            List<Technology> techGroup = new List<Technology> { tech };
            ComplianceFinding cf = new ComplianceFinding();
            cf.Initialize(scen, year, this.MinYear, this.MaxYear, this.GetData(scen), mfrIndex, this.Settings, this.LogWriter,
                techType, techGroup);
            for (int i = 0; i < component.Vehicles.Count; i++)
            {
                Vehicle veh = component.Vehicles[i];
                if (!veh.ModelData.TechEnabled[tech.Index] || veh.Description.Sales[year.Index] == 0) { continue; }
                cf.ExamineTechInheriting(0, component.Vehicles, i);
                cf.ApplyFinding();
                this.Recomply(scen, year, mfr);
            } 
        }
        #endregion
        #region 
        protected virtual ComplianceFinding ComplianceFindingLoop(Scenario scen, ModelYear year, int mfrIndex, bool noFines,
            ref ModelingApproach approach, bool overcomply, bool creditTrade)
        {
            int                     snIndex = scen.Index;
            int                     yrIndex = year.Index;
            Industry                data    = this.GetData(scen, year);
            Manufacturer            mfr     = data.Manufacturers[mfrIndex];
            Manufacturer.CModelData mmd     = mfr.ModelData;
            ComplianceFinding finding = null;
            bool lowCostFirst    = (approach == ModelingApproach.LowCostFirst);
            bool allowSecondBest = !lowCostFirst;   
            bool loop            = true;
            while (loop)
            {   
                finding = this._lcf.GetNextFinding(year, this.GetData(scen), mfrIndex, lowCostFirst, allowSecondBest,
                    overcomply, creditTrade);
                if (lowCostFirst && (finding == null || !finding.IsEfficient))
                {   
                    if (this.Settings.OperatingModes.AutoToggleApproach)
                    {   
                        approach        = ModelingApproach.EconomicEfficiency;
                        lowCostFirst    = false;
                        allowSecondBest = true;
                        continue;
                    }
                    else if (!allowSecondBest)
                    {   
                        allowSecondBest = true;
                        continue;
                    }
                } 
                double mfrTechCost = mmd.TechCost.Total;
                double mfrFines    = mmd.Fines   .Total;
                if (finding == null || !finding.IsValid || noFines || finding.IsEfficient)
                {   
                    loop = false;
                }
                else
                {   
                    finding = null;
                    loop    = false;
                }
            } 
            return finding;
        }
        #endregion
        #endregion
        #endregion
        #region 
        #region 
        public override IModelingProgress Progress
        {
            get
            {
                bool   xlReporting          = (this.XlReportGenerator  != null && this.XlReportGenerator .Reporting);
                bool   csvReporting         = (this.CsvReportGenerator != null && this.CsvReportGenerator.Reporting);
                bool   reporting            =  xlReporting || csvReporting;
                string xlReportingProgress  = (xlReporting) ? this.XlReportGenerator.ReportingProgress : null;
                string csvReportingProgress = (csvReporting) ? this.CsvReportGenerator.ReportingProgress : null;
                string reportingProgress    =  xlReportingProgress + ((xlReporting) ? "\n" : "") + csvReportingProgress;
                bool   effectsModelRunning  = (this._effectsYear != -1 && (this._effectsYear < this.MinYear || this._effectsYear > this.MaxYear));
                string effectsModelProgress = (effectsModelRunning) ? "Running effects analysis for additional year: " + this._effectsYear : "";
                string additionalInfo       = effectsModelProgress + ((effectsModelRunning) ? "\n" : "") + reportingProgress;
                return (this.Running || reporting) ? new ModelingProgress(this._scen, this._year, this._mfr, additionalInfo) : null;
            }
        }
        #endregion
        #endregion
        #region 
        [NonSerialized] internal LinearComplianceFinder _lcf;
        [NonSerialized] EffectsModel _effectsModel;
        #endregion
    }
}

