using Microscopic_Traffic_Simulator___Model.CellularTopologyObjects.GeneralParameters;
using Microscopic_Traffic_Simulator___Model.Utilities;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;

namespace Microscopic_Traffic_Simulator___Model.TrafficObjects
{
    /// <summary>
    /// Class representing sensor.
    /// </summary>
    [Serializable]
    public partial class Sensor : Generator
    {        
        /// <summary>
        /// List of records from real environment.
        /// </summary>
        [NonSerialized]
        private List<Record> inputRecords;

        /// <summary>
        /// List of records obtained from a simulation.
        /// </summary>
        [NonSerialized]
        private List<Record> outputRecords;

        /// <summary>
        /// Path to file with the list of records from real environment.
        /// </summary>
        private string pathToInputRecords;

        /// <summary>
        /// Path to file with the list of records obtained from a simulation.
        /// </summary>
        private string pathToOutputRecords;

        /// <summary>
        /// Index of record which is needed to get time to.
        /// </summary>
        private int actionToGetTimeTo;

        /// <summary>
        /// Index of record which is needed to perform.
        /// </summary>
        private int actionToPerform;

        /// <summary>
        /// Reference to simulation parameters.
        /// </summary>
        protected CellularTopologyParameters cellularTopologyParameters;
        /// <summary>
        /// Setter of reference to simulation parameters.
        /// </summary>
        internal CellularTopologyParameters CellularTopologyParameters
        {
            set { cellularTopologyParameters = value; }
        }

        /// <summary>
        /// Initialize sensor with paths to files with input and output records.
        /// </summary>
        /// <param name="pathToInputRecords">Path to file with input records.</param>
        /// <param name="pathToOutputRecords">Path to file with output records.</param>
        public Sensor(string pathToInputRecords, string pathToOutputRecords)
        {
            this.pathToInputRecords = pathToInputRecords;
            this.pathToOutputRecords = pathToOutputRecords;
        }

        /// <summary>
        /// Loads records from file with path stored in pathToInputRecords to inputRecords.
        /// </summary>
        internal void LoadInputRecords()
        {
            inputRecords = new List<Record>();
            outputRecords = new List<Record>();
            actionToGetTimeTo = 0;
            actionToPerform = 0;
            foreach (string[] lineItems in new FileLineDataLoader(pathToInputRecords).GetLinesWithData())
            {
                inputRecords.Add(new Record()
                {
                    Time = TimeSpan.Parse(lineItems[0]),
                    Speed = double.Parse(lineItems[1])
                });
            }
        }

        /// <summary>
        /// Save records from outputRecords to file with path specified in pathToOutputRecords.
        /// </summary>
        internal void SaveInputRecords()
        {
            using (StreamWriter sw = new FileDataStore(pathToOutputRecords).GetStreamWriter())
            {
                foreach (Record record in outputRecords)
                {
                    sw.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0} {1}", record.Time, record.Speed));
                }
            }
        }

        /// <summary>
        /// Returns time to generation of new ticket by getting time to the next input record.
        /// </summary>
        /// <param name="random">Random instance which is not used.</param>
        /// <returns>Time to generation of new ticket.</returns>
        public override TimeSpan GetTimeToNextAction(Random random)
        {
            if (actionToGetTimeTo == inputRecords.Count)
            {
                return TimeSpan.MaxValue;
            }
            else if (actionToGetTimeTo == 0)
            {
                return inputRecords[actionToGetTimeTo++].Time;
            }
            else
            {
                return inputRecords[actionToGetTimeTo].Time - inputRecords[actionToGetTimeTo++ - 1].Time;
            }
        }

        /// <summary>
        /// Generate new ticket from input records.
        /// </summary>
        /// <param name="random">Random instance which is not used.</param>
        public override void PerformAction(Random random)
        {
            GenerateTicket(cellularTopologyParameters.FromKphSpeedToSimSpeed(inputRecords[actionToPerform++].Speed));
        }

        /// <summary>
        /// Adds new output record to sensor.
        /// </summary>
        /// <param name="time">Time of the record.</param>
        /// <param name="speed">Car speed during its recording by sensor.</param>
        internal void AddOutputRecord(TimeSpan time, int speed)
        {
            outputRecords.Add(new Record()
            {
                Time = time,
                Speed = cellularTopologyParameters.FromSimSpeedToKphSpeed(speed)
            });
        }
    }
}
