#region << Using Directives >>
using System;
using System.Collections.Generic;
using Volpe.Cafe.Settings;
using Volpe.Cafe.Utils;
using Volpe.Cafe.Generic;
using RC = Volpe.Cafe.RegulatoryClass;
#endregion
namespace Volpe.Cafe.IO.InputParsers
{
    sealed class XlScenariosParser : XlParser
    {
        #region 
        class SectionInfo
        {
            public RC  RegClass;
            public int SectionIndex;
            public int SectionStart;
            public int SectionEnd;
            public int[] VarIndexes;    
            public int MinMY, MaxMY;
        }
        #endregion
        #region 
        internal XlScenariosParser(string path, string password) : base(path, password, true) { }
        #endregion
        #region 
        protected override void ParseInternal()
        {
            this._scenList = new List<Scenario>();
            string[] sheets = this.SheetNames;
            for (int i = 0; i < sheets.Length; i++)
            {
                if (sheets[i].ToUpper().StartsWith("SCEN_"))
                {   
                    this._scenList.Add(this.ParseScenario(i, this._scenList.Count));
                }
            }
        }
        Scenario ParseScenario(int wsIndex, int scenIndex)
        {
            this.ActivateWorksheet(wsIndex);
            int rows = this.Rows;
            int cols = this.Columns;
            string[] sectionNames = new string[] { "Passenger Car", "Light Truck", "Light Truck 2b/3" };
            RC    [] sectionRCs   = new RC    [] { RC.PassengerCar, RC.LightTruck, RC.LightTruck2b3   };
            List<SectionInfo> sections = this.FindSections(sectionNames, sectionRCs);
            if (!this.VerifySections(wsIndex, sectionNames, sectionRCs, sections)) { return null; }
            string scenDescr = this.GetString(0, 0);
            RCObject<ScenarioInfo> scenInfo = new RCObject<ScenarioInfo>();
            foreach (SectionInfo sectInfo in sections)
            {
                scenInfo[sectInfo.RegClass] = this.ParseScenarioInfo(sectInfo);
            }
            return new Scenario(scenIndex, scenDescr, scenInfo);
        }
        List<SectionInfo> FindSections(string[] sectionNames, RC[] sectionRCs)
        {
            int rows = this.Rows;
            List<SectionInfo> sections = new List<SectionInfo>();
            SectionInfo sectInfo = null;
            for (int i = 1; i < rows; i++)
            {
                string cell = this.GetString(i, 0);
                int sectionIndex;
                if (Interaction.StringCompareAny(cell, sectionNames, true, out sectionIndex))
                {
                    if (sectInfo != null)
                    {   
                        sectInfo.SectionEnd = i - 1;
                        sections.Add(sectInfo);
                        sectInfo = null;
                    }
                    sectInfo = new SectionInfo();
                    sectInfo.RegClass     = sectionRCs[sectionIndex];
                    sectInfo.SectionIndex = sectionIndex;
                    sectInfo.SectionStart = i;
                }
            }
            if (sectInfo != null)
            {   
                sectInfo.SectionEnd = rows - 1;
                sections.Add(sectInfo);
                sectInfo = null;
            }
            return sections;
        }
        bool VerifySections(int wsIndex, string[] sectionNames, RC[] sectionRCs, List<SectionInfo> sections)
        {
            bool noErrors = true;
            if (sections.Count == 0)
            {
                this.LogError(this.SheetNames[wsIndex] + " worksheet: Missing regulatory class definitions " +
                    "(maybe the scenarios file is improperly aligned).");
                noErrors = false;
            }
            else
            {   
                bool[] sectionFound = new bool[sectionRCs.Length];
                foreach (SectionInfo sectInfo in sections)
                {
                    if (sectionFound[sectInfo.SectionIndex])
                    {
                        this.LogError(this.SheetNames[wsIndex] + " worksheet (R:" + (sectInfo.SectionStart + 1).ToString("0000") +
                            "):  \"" + sectionNames[sectInfo.SectionIndex] + "\" regulatory class has previously been defined " +
                            "(duplicate reg-class definitions are not allowed).");
                        noErrors = false;
                    }
                    else
                    {
                        sectionFound[sectInfo.SectionIndex] = true;
                        sectInfo.VarIndexes = new int[VarNames.Length];
                        for (int i = 0; i < VarNames.Length; i++) { sectInfo.VarIndexes[i] = -1; }
                        for (int i = sectInfo.SectionStart + 1; i <= sectInfo.SectionEnd; i++)
                        {
                            string variable = this.GetString(i, 0);
                            if (variable == string.Empty) { continue; } 
                            int varIndex;
                            if (Interaction.StringCompareAny(variable, VarNames, true, out varIndex))
                            {
                                sectInfo.VarIndexes[varIndex] = i;
                            }
                            else
                            {
                                this.LogError(this.SheetNames[wsIndex] + " worksheet (R:" + (i + 1).ToString("0000") + "):  \"" +
                                    variable + "\" is not a valid variable name.");
                                noErrors = false;
                            }
                        }
                        if (!this.VerifyIndexes(this.SheetNames[wsIndex] + " worksheet", sectInfo.VarIndexes, VarNames))
                        {
                            noErrors = false;
                        }
                        sectInfo.MinMY = 0;
                        sectInfo.MaxMY = 0;
                        int prevYear = -1;
                        for (int i = 1; i < this.Columns; i++)
                        {
                            string modelYear = this.GetString(sectInfo.SectionStart, i);
                            int year = Interaction.GetInt32(modelYear);
                            if (!ModelYear.IsValid(year))
                            {
                                this.LogError(this.SheetNames[wsIndex] + " worksheet (R:" + (sectInfo.SectionStart + 1).ToString("0000") +
                                    "):  \"" + modelYear + "\" is not a valid model year.");
                                noErrors = false;
                                continue;
                            }
                            if (prevYear != -1 && prevYear + 1 != year)
                            {
                                this.LogError(this.SheetNames[wsIndex] + " worksheet (R:" + (sectInfo.SectionStart + 1).ToString("0000") +
                                    "):  Model years " + prevYear + " -> " + year + " are not contiguous.");
                                noErrors = false;
                            }
                            prevYear = year;
                            if (sectInfo.MinMY == 0) { sectInfo.MinMY = year; }
                            else                     { sectInfo.MaxMY = year; }
                        }
                    }
                } 
            }
            return noErrors;
        }
        ScenarioInfo ParseScenarioInfo(SectionInfo info)
        {
            int years  = info.MaxMY - info.MinMY + 1;
            int offset = info.MinMY - ModelYear.MinYear;
            int arrLen = info.MaxMY - ModelYear.MinYear + 1;
            int   []   function           = new int   [arrLen];
            double[][] coefficients       = new double[arrLen][];
            double[]   minStndMpg         = new double[arrLen];
            double[]   minStndPct         = new double[arrLen];
            double[]   fineRate           = new double[arrLen];
            int   []   multiFuel          = new int   [arrLen];
            double[]   multiFuelFFVShare  = new double[arrLen];
            double[]   multiFuelPHEVShare = new double[arrLen];
            bool  []   includeAC          = new bool  [arrLen];
            double[]   acAdjustment       = new double[arrLen];
            double[]   acCost             = new double[arrLen];
            double[]   offCycleCap        = new double[arrLen];
            double[]   ffvCap             = new double[arrLen];
            double[]   shevTaxCredit      = new double[arrLen];
            double[]   phevTaxCredit      = new double[arrLen];
            double[]   evTaxCredit        = new double[arrLen];
            int   []   twFunction         = new int   [arrLen];
            double[]   payloadReturn      = new double[arrLen];
            double[]   towingReturn       = new double[arrLen];
            int   []   creditCarryFwd     = new int   [arrLen];
            for (int i = 0; i < arrLen; i++) { coefficients[i] = new double[CoefCount]; }
            for (int i = 0; i < years; i++)
            {
                int index = i + offset;
                function          [index] = this.GetInt32 (info.VarIndexes[ 0], i + 1);
                minStndMpg        [index] = this.GetDouble(info.VarIndexes[ 1], i + 1);
                minStndPct        [index] = this.GetDouble(info.VarIndexes[ 2], i + 1);
                fineRate          [index] = this.GetDouble(info.VarIndexes[ 3], i + 1);
                multiFuel         [index] = this.GetInt32 (info.VarIndexes[ 4], i + 1);
                multiFuelFFVShare [index] = this.GetDouble(info.VarIndexes[ 5], i + 1);
                multiFuelPHEVShare[index] = this.GetDouble(info.VarIndexes[ 6], i + 1);
                includeAC         [index] = this.GetBool  (info.VarIndexes[ 7], i + 1);
                acAdjustment      [index] = this.GetDouble(info.VarIndexes[ 8], i + 1);
                acCost            [index] = this.GetDouble(info.VarIndexes[ 9], i + 1);
                offCycleCap       [index] = this.GetDouble(info.VarIndexes[10], i + 1);
                ffvCap            [index] = this.GetDouble(info.VarIndexes[11], i + 1);
                shevTaxCredit     [index] = this.GetDouble(info.VarIndexes[12], i + 1);
                phevTaxCredit     [index] = this.GetDouble(info.VarIndexes[13], i + 1);
                evTaxCredit       [index] = this.GetDouble(info.VarIndexes[14], i + 1);
                twFunction        [index] = this.GetInt32 (info.VarIndexes[15], i + 1);
                payloadReturn     [index] = this.GetDouble(info.VarIndexes[16], i + 1);
                towingReturn      [index] = this.GetDouble(info.VarIndexes[17], i + 1);
                creditCarryFwd    [index] = this.GetInt32 (info.VarIndexes[18], i + 1);
                for (int j = 0; j < CoefCount; j++)
                {
                    coefficients[index][j] = this.GetDouble(info.VarIndexes[19 + j], i + 1);
                }
            }
            return new ScenarioInfo(info.RegClass, function, coefficients, minStndMpg, minStndPct,
                fineRate,
                multiFuel, multiFuelFFVShare, multiFuelPHEVShare,
                includeAC, acAdjustment, acCost, offCycleCap, ffvCap,
                shevTaxCredit, phevTaxCredit, evTaxCredit,
                twFunction, payloadReturn, towingReturn,
                creditCarryFwd);
        }
        #endregion
        #region 
        internal List<Scenario> _scenList;
        const int CoefCount = 10;   
        static readonly string[] VarNames = new string[] {
            "Function",             
            "Min (mpg)",            
            "Min (%)",              
            "Fine Rate",            
            "Multi-Fuel",           
            "FFV Share",            
            "PHEV Share",           
            "Include AC",           
            "AC Adjustment",        
            "AC Cost ($)",          
            "Off-Cycle Cap",        
            "FFV Cap",              
            "SHEV Tax Credit",      
            "PHEV Tax Credit",      
            "EV Tax Credit",        
            "TW Function",          
            "Payload Return",       
            "Towing Return",        
            "Credit Carry Fwd",     
            "A",                    
            "B",                    
            "C",                    
            "D",                    
            "E",                    
            "F",                    
            "G",                    
            "H",                    
            "I",                    
            "J" };                  
        #endregion
    }
}

