using System;
using System.IO;
using Volpe.Cafe.Logging;
using Volpe.Cafe.Reporting;
using Volpe.Cafe.Utils;

namespace Volpe.Cafe.ModelSettings
{
    /// <summary>
    /// Provides locations of the input data files for use with the modeling system as well as the directory where the
    /// modeling results will be saved.
    /// </summary>
    [Serializable]
    public class IOSettings
    {
        #region <<< Constructors >>>

        /// <summary>
        /// Initializes a new instance of the <see cref="IOSettings"/> class, optionally setting the initial state to
        /// the default values.
        /// </summary>
        /// <param name="setDefaultSettings">
        ///     true, to initialize the new instance with default values; false, otherwise.
        /// </param>
        public IOSettings(bool setDefaultSettings)
        {
            if (setDefaultSettings) {
                this.SetDefaultSettings();
            }
        }

        #endregion

        #region <<< Variables >>>

        string _marketDataFile;
        string _technologiesFile;
        string _parametersFile;
        string _scenariosFile;

        #endregion

        #region <<< Properties >>>

        //----- input settings -----
        /// <summary>Gets or sets the location of the market data file.</summary>
        public string MarketDataFile
        {
            get => this._marketDataFile;
            set {
                this._marketDataFile = value;
                if (File.Exists(value)) {
                    this.MarketDataTimestamp = File.GetLastWriteTime(value);
                }
            }
        }
        /// <summary>Gets or sets the location of the technologies file.</summary>
        public string TechnologiesFile
        {
            get => this._technologiesFile;
            set {
                this._technologiesFile = value;
                if (File.Exists(value)) {
                    this.TechnologiesTimestamp = File.GetLastWriteTime(value);
                }
            }
        }
        /// <summary>Gets or sets the location of the parameters file.</summary>
        public string ParametersFile
        {
            get => this._parametersFile;
            set {
                this._parametersFile = value;
                if (File.Exists(value)) {
                    this.ParametersTimestamp = File.GetLastWriteTime(value);
                }
            }
        }
        /// <summary>Gets or sets the location of the scenarios file.</summary>
        public string ScenariosFile
        {
            get => this._scenariosFile;
            set {
                this._scenariosFile = value;
                if (File.Exists(value)) {
                    this.ScenariosTimestamp = File.GetLastWriteTime(value);
                }
            }
        }

        /// <summary>
        /// Gets when the market data file was last modified, at the time it was loaded by the model.
        /// </summary>
        public DateTime MarketDataTimestamp { get; private set; }
        /// <summary>
        /// Gets when the technologies file was last modified, at the time it was loaded by the model.
        /// </summary>
        public DateTime TechnologiesTimestamp { get; private set; }
        /// <summary>
        /// Gets when the parameters file was last modified, at the time it was loaded by the model.
        /// </summary>
        public DateTime ParametersTimestamp { get; private set; }
        /// <summary>
        /// Gets when the scenarios file was last modified, at the time it was loaded by the model.
        /// </summary>
        public DateTime ScenariosTimestamp { get; private set; }

        //----- output settings -----
        /// <summary>Gets or sets the name of the session associated with the model run.</summary>
        public string SessionName { get; set; }

        /// <summary>
        /// Gets or sets the default output path, where the diagnostic files and reports will be written to.
        /// </summary>
        public string BaseOutputPath { get; set; }
        /// <summary>Gets the output path, where the diagnostic files and reports will be written to.</summary>
        public string OutputPath => Path.Combine(this.BaseOutputPath, this.SessionName);

        /// <summary>Gets or sets whether to compress all model outputs into a single zip file.</summary>
        public bool CompressModelOutputs { get; set; }
        /// <summary>
        /// Gets the file path where all model outputs will be written to as a single compressed zip file.
        /// </summary>
        public string OutputZipFile =>
            Path.Combine($"{this.BaseOutputPath.TrimEnd('\\')}_zip", $"{this.SessionName}.zip");

        /// <summary>
        /// Gets or sets the level of reporting to use during analysis.
        /// </summary>
        public ReportingLevel ReportingLevel { get; set; }
        /// <summary>
        /// Gets or sets the level of logging to use during analysis.
        /// </summary>
        public LogLevel LogLevel { get; set; }

        /// <summary>Gets the path where model log files are written.</summary>
        public string LogPath
        {
            get {
                string logPath = Path.Combine(this.OutputPath, "logs");
                Directory.CreateDirectory(logPath);
                return logPath;
            }
        }
        /// <summary>Gets the path where debug log files are written.</summary>
        public string DebugLogPath
        {
            get {
                string debugLogPath = Path.Combine(this.OutputPath, "debug-logs");
                Directory.CreateDirectory(debugLogPath);
                return debugLogPath;
            }
        }

        #endregion

        #region <<< Methods >>>

        /// <summary>
        /// Creates a new object that is a copy of the current <see cref="IOSettings"/> instance.
        /// </summary>
        /// <returns>A new object that is a copy of this <see cref="IOSettings"/>.</returns>
        public IOSettings Clone()
        {
            return new IOSettings(false)
            {   // input settings
                MarketDataFile        = this.MarketDataFile,
                TechnologiesFile      = this.TechnologiesFile,
                ParametersFile        = this.ParametersFile,
                ScenariosFile         = this.ScenariosFile,
                MarketDataTimestamp   = this.MarketDataTimestamp,
                TechnologiesTimestamp = this.TechnologiesTimestamp,
                ParametersTimestamp   = this.ParametersTimestamp,
                ScenariosTimestamp    = this.ScenariosTimestamp,

                // output settings
                SessionName           = this.SessionName,
                BaseOutputPath        = this.BaseOutputPath,
                CompressModelOutputs  = this.CompressModelOutputs,
                ReportingLevel        = this.ReportingLevel,
                LogLevel              = this.LogLevel
            };
        }

        /// <summary>
        /// Sets the initial state of the current <see cref="IOSettings"/> object to the default values.
        /// </summary>
        public void SetDefaultSettings()
        {
            // input settings
            this.MarketDataFile   = string.Empty;
            this.TechnologiesFile = string.Empty;
            this.ParametersFile   = string.Empty;
            this.ScenariosFile    = string.Empty;

            // output settings
            this.SessionName          = "New Session";
            this.BaseOutputPath       = ApplicationInfo.DefaultOutputPath;
            this.CompressModelOutputs = false;
            this.ReportingLevel       = ReportingLevel.Default;
            this.LogLevel             = LogLevel.Default;
        }

        /// <summary>
        /// Summarizes the information about this <see cref="IOSettings"/> instance to the specified
        /// <see cref="TextWriter"/>.
        /// </summary>
        public void WriteSummaryInfo(TextWriter writer)
        {
            if (writer == null) {
                return;
            }

            writer.WriteLine("--- IO Settings ---");
            WriteFileInfo("MarketDataFile", this.MarketDataFile, this.MarketDataTimestamp);
            WriteFileInfo("TechnologiesFile", this.TechnologiesFile, this.TechnologiesTimestamp);
            WriteFileInfo("ParametersFile", this.ParametersFile, this.ParametersTimestamp);
            WriteFileInfo("ScenariosFile", this.ScenariosFile, this.ScenariosTimestamp);

            writer.WriteLine($"  OutputPath={this.OutputPath}");
            writer.WriteLine($"  CompressModelOutputs={(this.CompressModelOutputs ? $"True ({this.OutputZipFile})" : "False")}");
            writer.WriteLine($"  ReportingLevel={this.ReportingLevel}");
            writer.WriteLine($"  LogLevel={this.LogLevel}");
            writer.WriteLine();

            // local helper function
            void WriteFileInfo(string name, string file, DateTime timestamp)
            {
                const string format = "M/dd/yy H:mm:ss";
                writer.WriteLine($"  {name}={file} ({timestamp.ToString(format)})");
            }
        }

        #endregion
    }
}
