using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using Volpe.Cafe.IO;
using Volpe.Cafe.Utils;
namespace Volpe.Cafe.Settings
{
    [Serializable]
    public sealed class ModelingSettings : ICloneable
    {
        #region 
        ModelingSettings(ModelingSettings prototype)
        {
            this._runtimeSettings = new List<ISettings>();
            for (int i = 0; i < prototype._runtimeSettings.Count; i++)
            {
                this._runtimeSettings.Add(prototype._runtimeSettings[i].Clone());
            }
            this._technologies = (prototype._technologies == null) ? null : prototype.CloneTechnologies(prototype._technologies);
            this._parameters   = (prototype._parameters   == null) ? null : prototype._parameters.Clone();
            this._scenarios    = (prototype._scenarios    == null) ? null : prototype.CloneScenarios(prototype._scenarios);
        }
        public ModelingSettings()
        {
            this._runtimeSettings = new List<ISettings>();
            this._runtimeSettings.Add(new InputSettings());
            this._runtimeSettings.Add(new OutputSettings());
            this._runtimeSettings.Add(new OperatingModes());
            for (int i = 0; i < this._runtimeSettings.Count; i++)
            {
                this._runtimeSettings[i].SetDefaultSettings();
            }
        }
        #endregion
        #region 
        #region 
        object ICloneable.Clone()
        {
            return this.Clone();
        }
        public ModelingSettings Clone()
        {
            return new ModelingSettings(this);
        }
        List<Technology> CloneTechnologies(List<Technology> list)
        {
            List<Technology> newList = new List<Technology>(list.Count);
            for (int i = 0, count = list.Count; i < count; i++)
            {
                newList.Add((Technology)(list[i].Clone()));
            }
            return newList;
        }
        List<Scenario> CloneScenarios(List<Scenario> list)
        {
            List<Scenario> newList = new List<Scenario>(list.Count);
            for (int i = 0, count = list.Count; i < count; i++)
            {
                newList.Add((Scenario)(list[i].Clone()));
            }
            return newList;
        }
        #endregion
        public void WriteSummaryLog(LogWriter writer)
        {
            if (writer.Summary == null) { return; }
            writer.Summary.WriteLine("---------- Modeling Settings ----------");
            for (int i = 0; i < this._runtimeSettings.Count; i++)
            {
                this._runtimeSettings[i].WriteSummaryLog(writer);
            }
            if (this._technologies != null && this._technologies.Count > 0)
            {
                string techStr = "--- Technologies Analyzed ---";
                for (int i = 0, techCount = this._technologies.Count; i < techCount; i++)
                {
                    Technology tech = this._technologies[i];
                    techStr += "\n  " + tech.Index + ". " + tech.Abbr + " - " + tech.Name;
                }
                writer.Summary.WriteLine(techStr + "\n");
            }
            if (this._scenarios != null && this._scenarios.Count > 0)
            {
                string scenStr = "--- Scenarios Analyzed ---";
                for (int i = 0, scenCount = this._scenarios.Count; i < scenCount; i++)
                {
                    Scenario scen = this._scenarios[i];
                    scenStr += "\n  " + scen.Index + ". " + scen.Description + ((scen.IsBaseline) ? "  (Baseline)" : "");
                }
                writer.Summary.WriteLine(scenStr + "\n");
            }
        }
        public ISettings GetRuntimeSettings(Type type)
        {
            int index = this.FindRuntimeSettings(type);
            return (index == -1) ? null : this._runtimeSettings[index];
        }
        public bool HasCustomRuntimeSettings(Type type)
        {
            return (this.FindCustomRuntimeSettings(type) != -1);
        }
        int FindRuntimeSettings(Type type)
        {
            for (int i = 0; i < this._runtimeSettings.Count; i++)
            {
                if (this._runtimeSettings[i].GetType() == type) { return i; }
            }
            return -1;
        }
        int FindCustomRuntimeSettings(Type type)
        {
            for (int i = 5; i < this._runtimeSettings.Count; i++)
            {
                if (this._runtimeSettings[i].GetType() == type) { return i; }
            }
            return -1;
        }
        public void AddCustomRuntimeSettings(Type type, bool overwritePrevious)
        {
            int index = this.FindRuntimeSettings(type);
            if (index != -1)
            {   
                if (!overwritePrevious) { return; }
                this._runtimeSettings.RemoveAt(index);
            }
            System.Reflection.ConstructorInfo ctor = type.GetConstructor(Type.EmptyTypes);
            ISettings settings = (ISettings)ctor.Invoke(null);
            settings.SetDefaultSettings();
            this._runtimeSettings.Add(settings);
        }
        public void RemoveCustomRuntimeSettings(Type type)
        {
            int index = this.FindRuntimeSettings(type);
            if (index != -1) { this._runtimeSettings.RemoveAt(index); }
        }
        public void RemoveCustomRuntimeSettings()
        {
            for (int i = this._runtimeSettings.Count - 1; i > 4; i--)
            {
                this._runtimeSettings.RemoveAt(i);
            }
        }
        #endregion
        #region 
        public List<Technology> Technologies
        {
            get { return this._technologies; }
            set
            {
                if (value == null) { throw new ArgumentNullException("Technologies", "The specified value cannot be null."); }
                this._technologies = value;
            }
        }
        public Parameters Parameters
        {
            get { return this._parameters; }
            set
            {
                if (value == null) { throw new ArgumentNullException("Parameters", "The specified value cannot be null."); }
                this._parameters = value;
            }
        }
        public List<Scenario> Scenarios
        {
            get { return this._scenarios; }
            set
            {
                if (value == null) { throw new ArgumentNullException("Scenarios", "The specified value cannot be null."); }
                this._scenarios = value;
            }
        }
        public InputSettings InputSettings { get { return (InputSettings)this._runtimeSettings[0]; } }
        public OutputSettings OutputSettings { get { return (OutputSettings)this._runtimeSettings[1]; } }
        public OperatingModes OperatingModes { get { return (OperatingModes)this._runtimeSettings[2]; } }
        #endregion
        #region 
        List<ISettings> _runtimeSettings;
        List<Technology> _technologies;
        Parameters _parameters;
        List<Scenario> _scenarios;
        #endregion
    }
}

