#region << Using Directives >>
using System;
using System.Collections;
using System.Collections.Generic;
using Volpe.Cafe.Data;
using Volpe.Cafe.IO;
using Volpe.Cafe.Settings;
using TI = Volpe.Cafe.TechnologyIndexes;
using TA = Volpe.Cafe.Model.TechnologyApplicability;
#endregion
namespace Volpe.Cafe.Model
{
    [Serializable]
    public class LinearComplianceFinder
    {
        #region 
        public LinearComplianceFinder(Scenario scen, int startYear, int endYear, ModelingSettings settings, LogWriter logWriter)
        {
            this._scen        = scen;
            this._startYear   = startYear;
            this._endYear     = endYear;
            this._settings    = settings;
            this._logWriter   = logWriter;
            this._techs       = settings.Technologies;
            this._techGroups  = new List<Technology>[TA.TechTypeCount];
            for (int i = 0; i < TA.TechTypeCount; i++)
            {
                this._techGroups[i] = TA.GetTechnologyGroup(this._techs, TA.TechTypes[i]);
            }
            this._cfCount   = 0;
            this._cfEff   = new ComplianceFinding();
            this._cfGroup = new ComplianceFinding();
            this._cfTmp   = new ComplianceFinding();
        }
        #endregion
        #region 
        public ComplianceFinding GetNextFinding(ModelYear year, Industry[] modelYearData, int mfrIndex, bool lowCostFirst,
            bool allowSecondBest, bool overcomply, bool creditTrade)
        {
            this._cfEff.Clear();
            for (int i = 0; i < TA.TechTypeCount; i++)
            {
                this.FindComplianceFindingByTechType(TA.TechTypes[i], this._techGroups[i], year, modelYearData, mfrIndex,
                    lowCostFirst, overcomply, creditTrade);
                ComplianceFinding eff = this.GetMostEfficient(this._cfEff, this._cfGroup);
                if (eff != this._cfEff) { this._cfEff.CopyFrom(eff); }
            }
            return (this._cfEff.IsEfficient || (this._cfEff.IsValid && allowSecondBest)) ? this._cfEff : null;
        }
        void FindComplianceFindingByTechType(TechnologyType techType, List<Technology> techGroup, ModelYear year,
            Industry[] modelYearData, int mfrIndex, bool lowCostFirst, bool overcomply, bool creditTrade)
        {
            this._cfGroup.Clear();
            this._cfTmp.Initialize(this._scen, year, this._startYear, this._endYear, modelYearData, mfrIndex, this._settings,
                this._logWriter, techType, techGroup);
            int          yrIndex = year.Index;
            Manufacturer mfr     = modelYearData[yrIndex].Manufacturers[mfrIndex];
            IList componentList =
                (techType == TechnologyType.Engine      ) ? (IList)mfr.Engines       :
                (techType == TechnologyType.DieselEngine) ? (IList)mfr.Engines       :
                (techType == TechnologyType.Transmission) ? (IList)mfr.Transmissions :
                (techType == TechnologyType.Aerodynamics) ? (IList)mfr.Platforms     :
                (techType == TechnologyType.MR          ) ? (IList)mfr.Platforms     : (IList)mfr.Vehicles;
            for (int i = 0, techCount = techGroup.Count; i < techCount; i++)
            {   
                this.ExamineTechnology(techType, techGroup, i, componentList, year, overcomply, creditTrade);
                if (this._cfGroup.IsEfficient && lowCostFirst) { break; }
            } 
        }
        void ExamineTechnology(TechnologyType techType, List<Technology> techGroup, int techIdx, IList componentList,
            ModelYear year, bool overcomply, bool creditTrade)
        {
            int t2Idx, t3Idx, t4Idx, t5Idx;    
            bool isMainBranch = this.FindTechBranches(techGroup, techGroup[techIdx].Index, out t2Idx, out t3Idx, out t4Idx, out t5Idx);
            if (isMainBranch)
            {   
                for (int i = 0, componentCount = componentList.Count; i < componentCount; i++)
                {   
                    List<Vehicle> vehs;
                    int vehIdx;     
                    this.GenerateComponentVehicles(componentList, i, techType, out vehs, out vehIdx);
                    if (vehs == null || vehs.Count == 0) { continue; }
                    this.ExamineComponent(techType, techGroup, techIdx, vehs, vehIdx, t2Idx, t3Idx, t4Idx, year, overcomply, creditTrade);
                } 
            } 
        }
        void ExamineComponent(TechnologyType techType, List<Technology> techGroup, int techIdx, List<Vehicle> vehs, int vehIdx,
            int t2Idx, int t3Idx, int t4Idx, ModelYear year, bool overcomply, bool creditTrade)
        {
            this._cfTmp.ExamineFinding(techIdx, vehs, vehIdx, ref this._cfCount, overcomply, creditTrade);
            ComplianceFinding eff = this.GetMostEfficient(this._cfGroup, this._cfTmp);
            if (eff != this._cfGroup) { this._cfGroup.CopyFrom(eff); }
            if (t2Idx != -1)
            {   
                this.ExamineComponent(techType, techGroup, t2Idx, vehs, vehIdx, t3Idx, t4Idx, -1, year, overcomply, creditTrade);
            }
        }
        ComplianceFinding GetMostEfficient(ComplianceFinding cf1, ComplianceFinding cf2)
        {
            if      (!cf2.IsValid) { return cf1; }
            else if (!cf1.IsValid) { return cf2; }
            else
            {   
                if (cf2._efficiency <= cf1._efficiency) { if (!this._logWriter.IsEmpty) { cf1.LogFinding(); } return cf2; }
                else                                    { if (!this._logWriter.IsEmpty) { cf2.LogFinding(); } return cf1; }
            }
        }
        bool FindTechBranches(List<Technology> techGroup, int techIdx, out int t2Idx, out int t3Idx, out int t4Idx, out int t5Idx)
        {
            t2Idx = t3Idx = t4Idx = t5Idx = -1;
            switch (techIdx)
            {
                case TI.DVVLD:                               t2Idx = this.FindTechInGroup(techGroup, TI.CVVL); break;
                case TI.EV1:                                 t2Idx = this.FindTechInGroup(techGroup, TI.EV2 );
                                                             t3Idx = this.FindTechInGroup(techGroup, TI.EV3);
                                                             t4Idx = this.FindTechInGroup(techGroup, TI.EV4);
                                                             t5Idx = this.FindTechInGroup(techGroup, TI.FCV ); break;
                case TI.CVVL: case TI.EV2: case TI.EV3: case TI.EV4: case TI.FCV:
                    return false;
            }
            return true;
        }
        int FindTechInGroup(List<Technology> techGroup, int techIdx)
        {
            for (int i = 0, techCount = techGroup.Count; i < techCount; i++)
            {
                if (techGroup[i].Index == techIdx) { return i; }
            }
            return -1;
        }
        void GenerateComponentVehicles(IList componentList, int componentIndex, TechnologyType techType, out List<Vehicle> vehs, out int vehIdx)
        {
            object component = componentList[componentIndex];
            vehIdx = -1;
            switch (techType)
            {
                case TechnologyType.Engine      :
                case TechnologyType.DieselEngine: vehs = ((Engine      )component).Vehicles; break;
                case TechnologyType.Transmission: vehs = ((Transmission)component).Vehicles; break;
                case TechnologyType.Aerodynamics:
                case TechnologyType.MR          : vehs = ((Platform    )component).Vehicles; break;
                default:
                    vehs = (List<Vehicle>)componentList;
                    vehIdx = componentIndex;
                    break;
            }
        }
        void Swap(Vehicle[] arr, int i, int j) { Vehicle tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; }
        void Swap(ulong  [] arr, int i, int j) { ulong   tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; }
        void Swap(int    [] arr, int i, int j) { int     tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; }
        #endregion
        #region 
        public int CfCount { get { return this._cfCount; } }
        #endregion
        #region 
        Scenario         _scen;
        int              _startYear;
        int              _endYear;
        ModelingSettings _settings;
        LogWriter        _logWriter;
        List<Technology>   _techs;
        List<Technology>[] _techGroups;
        int _cfCount;
        ComplianceFinding _cfEff;
        ComplianceFinding _cfGroup;
        ComplianceFinding _cfTmp;
        #endregion
    }
}

