#region << Using Directives >>
using System;
using System.Collections.Generic;
using Volpe.Cafe.Settings;
using Volpe.Cafe.Utils;
using TI = Volpe.Cafe.TechnologyIndexes;
#endregion
namespace Volpe.Cafe.IO.InputParsers
{
    sealed class XlTechnologiesParser : XlParser
    {
        #region 
        internal XlTechnologiesParser(string path, string password) : base(path, password, true) { }
        #endregion
        #region 
        protected override void ParseInternal()
        {
            this._techList = new List<Technology>(TechnologyIndexes.TechnologyCount + 1);
            string[] sheets = this.SheetNames;
            string[] names = new string[] {                                                                 
                    "Technologies", "SubcompactPC" , "SubcompactPerfPC", "CompactPC"  , "CompactPerfPC",    
                    "MidsizePC"   , "MidsizePerfPC", "LargePC"         , "LargePerfPC", "MinivanLT"    ,    
                    "SmallLT"     , "MidsizeLT"    , "LargeLT"         , "Truck2b3"   , "Van2b3"       ,    
                    "FCSynergies" , "CostSynergies" };                                                      
            int[] indexes = new int[names.Length];
            for (int i = 0; i < indexes.Length; i++) { indexes[i] = -1; }
            for (int i = 0; i < sheets.Length; i++)
            {
                string sheet = sheets[i].Trim().ToUpper();
                for (int j = 0; j < names.Length; j++)
                {
                    if (Interaction.StringCompare(sheet, names[j], true)) { indexes[j] = i; break; }
                }
            }
            if (!this.VerifyIndexes("Technologies workbook", indexes, names, 0, 14)) { return; }
            this.LoadTechnologies(indexes[0]);
            for (int i = 0, techClassCount = (int)TechnologyClass.Max; i < techClassCount; i++)
            {
                this.LoadTechnologyClasses(indexes[i + 1], TI.TechClasses[i]);
            }
            if (indexes[15] != -1) { this.LoadSynergies(indexes[15], TI.TechClasses, true ); }
            if (indexes[16] != -1) { this.LoadSynergies(indexes[16], TI.TechClasses, false); }
        }
        void LoadTechnologies(int wsIndex)
        {
            this.ActivateWorksheet(wsIndex);
            int rows = this.Rows;
            int cols = this.Columns;
            string[] names = new string[] {
                "Technology", "Abbr", "TechType",                                                           
                "Off-Cycle Credit PC", "Off-Cycle Credit LT", "Off-Cycle Credit LT 2b/3",                   
                "Phase-In Values" };                                                                        
            int   [] indexes = new int[names.Length];
            for (int i = 0; i < indexes.Length; i++) { indexes[i] = -1; }
            int phaseInCount = 0;   
            for (int i = 0; i < cols; i++)
            {
                string column = this.GetString(1, i);
                if (indexes[6] == -1 && Interaction.StringStartsWith(column, "PV-", false))
                {   
                    indexes[6] = i;    
                    while (Interaction.StringStartsWith(column, "PV-", false))
                    {   
                        phaseInCount++; i++;
                        if (i == cols) { break; }
                        column = this.GetString(1, i);
                    }
                    i--;
                }
                else
                {   
                    for (int j = 0; j < names.Length; j++)
                    {
                        if (Interaction.StringCompare(column, names[j], true)) { indexes[j] = i; break; }
                    }
                }
            }
            if (!this.VerifyIndexes(this.SheetNames[wsIndex] + " worksheet", indexes, names)) { return; }
            Technology[] techs = new Technology[TechnologyIndexes.TechnologyCount];
            for (int i = 2; i < rows; i++)
            {   
                Technology tech = new Technology();
                tech.Name  = this.GetString(i, indexes[0]);
                tech.Abbr  = this.GetString(i, indexes[1]);
                tech.Index = TechnologyIndexes.GetIndex(tech.Abbr);     
                if (tech.Index == -1)
                {
                    this.LogError("On the main Technologies worksheet, the technology with abbreviation \"" + tech.Abbr +
                        "\" is undefined.");
                    return;
                }
                string techType = this.GetString(i, indexes[2]);
                tech.Type =
                    (techType == "ENGMOD") ? TechnologyType.Engine :
                    (techType == "DSLMOD") ? TechnologyType.DieselEngine :
                    (techType == "TRMOD" ) ? TechnologyType.Transmission :
                    (techType == "ELEC"  ) ? TechnologyType.Electrification :
                    (techType == "MR"    ) ? TechnologyType.MR :
                    (techType == "ROLL"  ) ? TechnologyType.ROLL :
                    (techType == "DLR"   ) ? TechnologyType.DLR :
                    (techType == "AERO"  ) ? TechnologyType.Aerodynamics : TechnologyType.Other;
                tech.OffCycleCredit[RegulatoryClass.PassengerCar ] = this.GetDouble(i, indexes[3]);
                tech.OffCycleCredit[RegulatoryClass.LightTruck   ] = this.GetDouble(i, indexes[4]);
                tech.OffCycleCredit[RegulatoryClass.LightTruck2b3] = this.GetDouble(i, indexes[5]);
                tech.PhaseIn = new double[phaseInCount];
                for (int j = 0; j < phaseInCount; j++)
                {
                    tech.PhaseIn[j] = Math.Min(1.0, this.GetDouble(i, indexes[6] + j));
                }
                techs[tech.Index] = tech;
            }
            this._techList.AddRange(techs);
        }
        void LoadTechnologyClasses(int wsIndex, TechnologyClass techClass)
        {
            this.ActivateWorksheet(wsIndex);
            int rows = this.Rows;
            int cols = this.Columns;
            string[] names = new string[] {                                                                     
                "Abbr"            , "Applicable"        , "Year Avail"   , "Year Retired", "Electric Range",    
                "Delta Weight (%)", "Delta Weight (lbs)", "Loss Of Value", "FC"          , "Primary Fuel Share",
                "Cost Table"      , "Maint. Table"      , "Repair Table" , "Stranded Capital Table" };          
            int[] indexes = new int[names.Length];
            for (int i = 0; i < indexes.Length; i++) { indexes[i] = -1; }
            int costTableCount = 0, maintTableCount = 0, repairTableCount = 0, strandedCapitalCount = 0; 
            for (int i = 0; i < cols; i++)
            {
                string column = this.GetString(1, i);
                if (indexes[10] == -1 && Interaction.StringStartsWith(column, "COST 2", true) &&
                                         Interaction.StringCompare(this.GetString(0, i), names[10], false))
                {   
                    indexes[10] = i;    
                    while (Interaction.StringStartsWith(column, "COST 2", true))
                    {   
                        costTableCount++; i++;
                        if (i == cols) { break; }
                        column = this.GetString(1, i);
                    }
                }
                if (indexes[11] == -1 && Interaction.StringStartsWith(column, "MAINT. 2", true) &&
                                         Interaction.StringCompare(this.GetString(0, i), names[11], false))
                {   
                    indexes[11] = i;    
                    while (Interaction.StringStartsWith(column, "MAINT. 2", true))
                    {   
                        maintTableCount++; i++;
                        if (i == cols) { break; }
                        column = this.GetString(1, i);
                    }
                }
                if (indexes[12] == -1 && Interaction.StringStartsWith(column, "REPAIR 2", true) &&
                                         Interaction.StringCompare(this.GetString(0, i), names[12], false))
                {   
                    indexes[12] = i;    
                    while (Interaction.StringStartsWith(column, "REPAIR 2", true))
                    {   
                        repairTableCount++; i++;
                        if (i == cols) { break; }
                        column = this.GetString(1, i);
                    }
                }
                if (indexes[13] == -1 && Interaction.StringStartsWith(column, "SC-", false) &&
                                         Interaction.StringCompare(this.GetString(0, i), names[13], false))
                {   
                    indexes[13] = i;    
                    while (Interaction.StringStartsWith(column, "SC-", false))
                    {   
                        strandedCapitalCount++; i++;
                        if (i == cols) { break; }
                        column = this.GetString(1, i);
                    }
                }
                for (int j = 0; j < names.Length; j++)
                {
                    if (Interaction.StringCompare(column, names[j], true)) { indexes[j] = i; break; }
                }
            }
            if (!this.VerifyIndexes(this.SheetNames[wsIndex] + " worksheet", indexes, names)) { return; }
            int costTableMinYear = Interaction.GetInt32(
                Interaction.StringRemoveWhitespacePunctuationAndSpecialChars(this.GetString(1, indexes[10])).Replace("COST", ""));
            if (costTableMinYear < ModelYear.MinYear) { this.LogError("On the " + this.SheetNames[wsIndex] + " worksheet, the Cost Table section is not valid."); return; }
            int maintTableMinYear = Interaction.GetInt32(
                Interaction.StringRemoveWhitespacePunctuationAndSpecialChars(this.GetString(1, indexes[11])).Replace("MAINT", ""));
            if (maintTableMinYear < ModelYear.MinYear) { this.LogError("On the " + this.SheetNames[wsIndex] + " worksheet, the Maintance Table section is not valid."); return; }
            int repairTableMinYear = Interaction.GetInt32(
                Interaction.StringRemoveWhitespacePunctuationAndSpecialChars(this.GetString(1, indexes[12])).Replace("REPAIR", ""));
            if (repairTableMinYear < ModelYear.MinYear) { this.LogError("On the " + this.SheetNames[wsIndex] + " worksheet, the Repair Table section is not valid."); return; }
            for (int i = 2; i < rows; i++)
            {   
                TechnologyAttributes ta = new TechnologyAttributes();
                string     abbr    = this.GetString(i, indexes[0]);
                EngineSize engSize = (abbr.EndsWith("_SD")) ? EngineSize.Small  :
                                     (abbr.EndsWith("_MD")) ? EngineSize.Medium :
                                     (abbr.EndsWith("_LD")) ? EngineSize.Large  : EngineSize.None;
                if (engSize != EngineSize.None) { abbr = abbr.Substring(0, abbr.Length - 3); }
                int index = TechnologyIndexes.GetIndex(abbr);   
                if (index == -1)
                {
                    this.LogError("On the " + this.SheetNames[wsIndex] + " worksheet, the technology with abbreviation \"" +
                        abbr + "\" is undefined.");
                    return;
                }
                ta.TechnologyClass  = techClass;
                ta.Applicable       = this.GetBool  (i, indexes[ 1]);
                ta.YearAvailable    = this.GetInt32 (i, indexes[ 2]);
                ta.YearRetired      = this.GetInt32 (i, indexes[ 3]);
                ta.ElectricRange    = this.GetDouble(i, indexes[ 4]);
                ta.DeltaWeightPCT   = this.GetDouble(i, indexes[ 5]);
                ta.DeltaWeightLBS   = this.GetDouble(i, indexes[ 6]);
                ta.LossOfValue      = this.GetDouble(i, indexes[ 7]);
                ta.FC               = this.GetDouble(i, indexes[ 8]);
                ta.FCPrimaryShare   = this.GetDouble(i, indexes[ 9]);
                int costTableStart = costTableMinYear - ModelYear.MinYear;
                ta.CostTable = new double[costTableStart + costTableCount];
                for (int j = 0; j < costTableCount; j++)
                {
                    ta.CostTable[costTableStart + j] = this.GetDouble(i, indexes[10] + j);
                }
                int maintTableStart = maintTableMinYear - ModelYear.MinYear;
                ta.MaintenanceCostTable = new double[maintTableStart + maintTableCount];
                for (int j = 0; j < maintTableCount; j++)
                {
                    ta.MaintenanceCostTable[maintTableStart + j] = this.GetDouble(i, indexes[11] + j);
                }
                int repairTableStart = repairTableMinYear - ModelYear.MinYear;
                ta.RepairCostTable = new double[repairTableStart + repairTableCount];
                for (int j = 0; j < repairTableCount; j++)
                {
                    ta.RepairCostTable[repairTableStart + j] = this.GetDouble(i, indexes[12] + j);
                }
                ta.StrandedCapital = new double[strandedCapitalCount];
                for (int j = 0; j < strandedCapitalCount; j++)
                {
                    ta.StrandedCapital[j] = this.GetDouble(i, indexes[13] + j);
                }
                this._techList[index].Attributes.SetAttributes(techClass, engSize, ta);
            }
        }
        void LoadSynergies(int wsIndex, TechnologyClass[] techClasses, bool fcSynergies)
        {
            this.ActivateWorksheet(wsIndex);
            int rows = this.Rows;
            int cols = this.Columns;
            string[] names = new string[] {                                                                         
                "Type"          , "Technology A"    , "Technology B", "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" };                                                                      
            int[] indexes = new int[names.Length];
            for (int i = 0; i < indexes.Length; i++) { indexes[i] = -1; }
            for (int i = 0; i < cols; i++)
            {   
                string colName = this.GetString(1, i);
                for (int j = 0; j < names.Length; j++)
                {
                    if (Interaction.StringCompare(colName, names[j], true)) { indexes[j] = i; break; }
                }
            }
            for (int i = 2; i < rows; i++)
            {
                SynergyType type = SynergyType.None;
                if (indexes[0] != -1)
                {
                    string typeStr = this.GetString(i, indexes[0]).ToUpper();
                    type = (typeStr == "ACCOUNTING") ? SynergyType.Accounting :
                           (typeStr == "PHYSICAL"  ) ? SynergyType.Physical   : SynergyType.None;
                }
                string techA = this.GetString(i, indexes[1]);
                string techB = this.GetString(i, indexes[2]);
                if (type == SynergyType.None || techA == "" || techB == "") { continue; }
                EngineSize engSizeA = (techA.EndsWith("_SD")) ? EngineSize.Small  :
                                      (techA.EndsWith("_MD")) ? EngineSize.Medium :
                                      (techA.EndsWith("_LD")) ? EngineSize.Large  : EngineSize.None;
                if (engSizeA != EngineSize.None) { techA = techA.Substring(0, techA.Length - 3); }
                EngineSize engSizeB = (techB.EndsWith("_SD")) ? EngineSize.Small  :
                                      (techB.EndsWith("_MD")) ? EngineSize.Medium :
                                      (techB.EndsWith("_LD")) ? EngineSize.Large  : EngineSize.None;
                if (engSizeB != EngineSize.None) { techB = techB.Substring(0, techB.Length - 3); }
                if (engSizeA != EngineSize.None && engSizeB != EngineSize.None && engSizeA != engSizeB)
                {
                    this.LogError("On the " + this.SheetNames[wsIndex] +
                        " worksheet, the synergy for technologies with abbreviations \"" + techA + "\" and \"" + techB +
                        "\" is not valid.\r\nA synergy may not exist between a " + engSizeA +
                        " displacement engine technology and a " + engSizeB + " displacement engine technology.");
                }
                EngineSize engSize = (engSizeA == EngineSize.None) ? engSizeB : engSizeA;
                int techAIndex = TechnologyIndexes.GetIndex(techA);
                int techBIndex = TechnologyIndexes.GetIndex(techB);
                if (techAIndex == -1)
                {
                    this.LogError("On the " + this.SheetNames[wsIndex] + " worksheet, the technology with abbreviation \"" +
                        techA + "\" is undefined.");
                }
                if (techBIndex == -1)
                {
                    this.LogError("On the " + this.SheetNames[wsIndex] + " worksheet, the technology with abbreviation \"" +
                        techB + "\" is undefined.");
                }
                if (techAIndex != -1 && techBIndex != -1)
                {
                    for (int j = 3; j < indexes.Length; j++)
                    {
                        if (indexes[j] == -1) { continue; }
                        double synergy = this.GetDouble(i, indexes[j]);
                        TechnologyClass techClass = techClasses[j - 3];
                        if (fcSynergies)
                        {
                            this._techList[techAIndex].ImprvSynergies.AddSynergy(techClass, engSize, techBIndex, type, synergy);
                            this._techList[techBIndex].ImprvSynergies.AddSynergy(techClass, engSize, techAIndex, type, synergy);
                        }
                        else
                        {
                            this._techList[techAIndex].CostSynergies .AddSynergy(techClass, engSize, techBIndex, type, synergy);
                            this._techList[techBIndex].CostSynergies .AddSynergy(techClass, engSize, techAIndex, type, synergy);
                        }
                    } 
                }
            } 
        }
        FCModLevel GetFCModLevel(int row, int column)
        {
            string s = this.GetString(row, column);
            if      (s == string.Empty || s == "ALL" || s == "DEFAULT"  ) { return FCModLevel.Default  ; }
            else if (                                   s == "PRIMARY"  ) { return FCModLevel.Primary  ; }
            else if (                                   s == "SECONDARY") { return FCModLevel.Secondary; }
            return (FCModLevel)(-1);
        }
        #endregion
        #region 
        internal List<Technology> _techList;
        #endregion
    }
}

