using System;
using TechKey = Volpe.Cafe.TechKeys.TechKey128;

namespace Volpe.Cafe.VehicleSimulation
{
    /// <summary>
    /// Provides vehicle simulation results for a single technology combination.
    /// </summary>
    [Serializable]
    internal sealed class SimulationResult : ISimulationResult
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="SimulationResult"/> class.
        /// </summary>
        public SimulationResult(TechKey key)
        {
            this.TechKey = key;
        }

        /// <summary>
        /// Defines an empty simulation result. This field is read-only.
        /// </summary>
        public static readonly ISimulationResult Empty = new SimulationResult(TechKey.Empty);

        public TechKey TechKey { get; private set; }

        public double[] FE1Adjustments { get; private set; }
        public double[] FE2Adjustments { get; private set; }
        public double[] BatteryCosts { get; private set; }
        public double[] BatteryEnergy { get; private set; }
        public double[] Motor1PeakPower { get; private set; }
        public double[] Motor1ContPower { get; private set; }
        public double[] Motor2PeakPower { get; private set; }
        public double[] Motor2ContPower { get; private set; }

        public bool IsEmpty
        {
            get {
                return this.TechKey.IsEmpty || (this.FE1Adjustments == null
                                             && this.FE2Adjustments == null
                                             && this.BatteryCosts == null);
            }
        }

        /// <summary>
        /// Gets or sets a property identified by the specified index, where index has the following meaning:
        /// 0 = <see cref="FE1Adjustments"/>, 1 = <see cref="FE2Adjustments"/>, 2 = <see cref="BatteryCosts"/>.
        /// </summary>
        internal double[] this[int index]
        {
            get {
                switch (index) {
                    case 0: return this.FE1Adjustments;
                    case 1: return this.FE2Adjustments;
                    case 2: return this.BatteryCosts;
                    case 3: return this.BatteryEnergy;
                    case 4: return this.Motor1PeakPower;
                    case 5: return this.Motor1ContPower;
                    case 6: return this.Motor2PeakPower;
                    case 7: return this.Motor2ContPower;

                    default:
                        throw new ArgumentOutOfRangeException("index");
                }
            }
            set {
                if (this.TechKey.IsEmpty) {
                    throw new InvalidOperationException("\"Empty\" SimulationResult instance may not be modified.");
                }

                switch (index) {
                    case 0: this.FE1Adjustments  = value; break;
                    case 1: this.FE2Adjustments  = value; break;
                    case 2: this.BatteryCosts    = value; break;
                    case 3: this.BatteryEnergy   = value; break;
                    case 4: this.Motor1PeakPower = value; break;
                    case 5: this.Motor1ContPower = value; break;
                    case 6: this.Motor2PeakPower = value; break;
                    case 7: this.Motor2ContPower = value; break;

                    default:
                        throw new ArgumentOutOfRangeException("index");
                }
            }
        }

        public double GetFE1Adjustment(TechnologyClass techClass)
        {
            return this.GetElement(this.FE1Adjustments, techClass);
        }
        public double GetFE2Adjustment(TechnologyClass techClass)
        {
            return this.GetElement(this.FE2Adjustments, techClass);
        }
        public double GetBatteryCost(TechnologyClass techClass)
        {
            return this.GetElement(this.BatteryCosts, techClass);
        }
        public double GetBatteryEnergy(TechnologyClass techClass)
        {
            return this.GetElement(this.BatteryEnergy, techClass);
        }
        public double GetMotor1PeakPower(TechnologyClass techClass)
        {
            return this.GetElement(this.Motor1PeakPower, techClass);
        }
        public double GetMotor1ContPower(TechnologyClass techClass)
        {
            return this.GetElement(this.Motor1ContPower, techClass);
        }
        public double GetMotor2PeakPower(TechnologyClass techClass)
        {
            return this.GetElement(this.Motor2PeakPower, techClass);
        }
        public double GetMotor2ContPower(TechnologyClass techClass)
        {
            return this.GetElement(this.Motor2ContPower, techClass);
        }

        private double GetElement(double[] arr, TechnologyClass techClass)
        {
            return (arr == null) ? 0 : arr[techClass.GetArrayIndex()];
        }

    }
}
