using System;
using Volpe.Cafe.MarketData;

namespace Volpe.Cafe.EffectsData
{
    /// <summary>
    /// Provides consumer costs arising from the various modeling effects.
    /// </summary>
    [Serializable]
    public sealed class ConsumerCosts : IAppendable
    {
        /// <summary>Gets or sets the total vehicle sales.</summary>
        public double Sales { get; set; }

        /// <summary>
        /// Gets or sets the payback period, which is the number of years before increases in cumulative vehicle costs
        /// are repaid.
        /// </summary>
        public double Payback { get; set; }
        /// <summary>
        /// Gets or sets the total cost of ownership (TCO) payback period, which is the number of years before
        /// increases in cumulative vehicle total costs of ownership are repaid.
        /// </summary>
        public double PaybackTCO { get; set; }

        /// <summary>Gets or sets the new vehicle sales value.</summary>
        public double NewSalesSurplus { get; set; }

        //----- tech costs & value losses -----
        /// <summary>Gets or sets the total technology costs.</summary>
        public double TechCosts { get; set; }
        /// <summary>Gets or sets the total regulatory costs.</summary>
        public double RegCosts { get; set; }
        /// <summary>Gets or sets the additional maintenance and repair costs.</summary>
        public double MaintRepairCosts { get; set; }
        /// <summary>Gets or sets the implicit opportunity cost.</summary>
        public double OpportunityCost { get; set; }

        /// <summary>
        /// Gets or sets the total vehicle tax credits, before any new vehicle technology application.
        /// </summary>
        public double InitTaxCredits { get; set; }
        /// <summary>Gets or sets the total vehicle tax credits.</summary>
        public double TaxCredits { get; set; }
        /// <summary>
        /// Gets or sets the total vehicle battery tax credits, before any new vehicle technology application.
        /// </summary>
        public double InitBatteryTaxCredits { get; set; }
        /// <summary>Gets or sets the total battery tax credits.</summary>
        public double BatteryTaxCredits { get; set; }

        //----- ownership and operating costs -----
        /// <summary>
        /// Gets or sets the ownership costs associated with new vehicle sales, before any new vehicle technology
        /// application.
        /// </summary>
        public OwnershipCosts InitOwnershipCosts { get; private set; } = new OwnershipCosts();
        /// <summary>
        /// Gets or sets the ownership costs associated with new vehicle sales.
        /// </summary>
        public OwnershipCosts OwnershipCosts { get; private set; } = new OwnershipCosts();

        //----- energy costs -----
        /// <summary>
        /// Gets or sets the retail fuel expenditures, before any new vehicle technology application.
        /// </summary>
        public double InitialRetailFuelCosts { get; set; }
        /// <summary>Gets or sets the retail fuel expenditures.</summary>
        public double RetailFuelCosts { get; set; }
        /// <summary>Gets or sets the rebound fuel expenditures.</summary>
        public double ReboundFuelCosts { get; set; }
        /// <summary>Gets or sets the vehicle travel value.</summary>
        public double DriveSurplus { get; set; }
        /// <summary>Gets or sets the value of reallocated miles.</summary>
        public double ReallocatedVMTSurplus { get; set; }
        /// <summary>Gets or sets the refueling time value.</summary>
        public double RefuelSurplus { get; set; }

        /// <summary>
        /// Calculates and returns the average consumer costs with reference to the specified baseline consumer costs
        /// object.
        /// </summary>
        /// <param name="baseCosts">
        ///     The baseline consumer costs object, that is the reference for the average consumer costs.
        /// </param>
        public double GetAverageConsumerCosts(ConsumerCosts baseCosts)
        {
            // calc "cost changes" as alternative - baseline
            return SumCostChangesComponents(this) - SumCostChangesComponents(baseCosts);

            // local helper function
            double SumCostChangesComponents(ConsumerCosts value)
            {
                return (value.NewSalesSurplus +
                        value.RegCosts +
                        value.MaintRepairCosts +
                        value.OpportunityCost +
                        value.OwnershipCosts.TaxesAndFees +
                        value.OwnershipCosts.InsuranceCosts) / value.Sales;
            }
        }

        /// <summary>
        /// Calculates and returns the average consumer benefits with reference to the specified baseline consumer
        /// costs object.
        /// </summary>
        /// <param name="baseCosts">
        ///     The baseline consumer costs object, that is the reference for the average consumer benefits.
        /// </param>
        /// <returns></returns>
        public double GetAverageConsumerBenefits(ConsumerCosts baseCosts)
        {
            return
                // calc "cost changes" as alternative - baseline
                (this.DriveSurplus          / this.Sales - baseCosts.DriveSurplus          / baseCosts.Sales) +
                (this.ReallocatedVMTSurplus / this.Sales - baseCosts.ReallocatedVMTSurplus / baseCosts.Sales) +
                (this.TaxCredits            / this.Sales - baseCosts.TaxCredits            / baseCosts.Sales) +
                (this.BatteryTaxCredits     / this.Sales - baseCosts.BatteryTaxCredits     / baseCosts.Sales) +

                // calc "savings" as baseline - alternative
                (baseCosts.RetailFuelCosts / baseCosts.Sales - this.RetailFuelCosts / this.Sales) +
                (baseCosts.RefuelSurplus   / baseCosts.Sales - this.RefuelSurplus   / this.Sales);
        }

        /// <summary>
        /// Resets all consumer costs to zero.
        /// </summary>
        public void Clear()
        {
            // general
            this.Sales = 0;
            this.Payback = 0;
            this.PaybackTCO = 0;
            this.NewSalesSurplus = 0;

            // tech costs & value losses
            this.TechCosts = 0;
            this.RegCosts = 0;
            this.MaintRepairCosts = 0;
            this.OpportunityCost = 0;

            this.InitTaxCredits = 0;
            this.TaxCredits = 0;
            this.InitBatteryTaxCredits = 0;
            this.BatteryTaxCredits = 0;

            // ownership and operating costs
            this.InitOwnershipCosts.Clear();
            this.OwnershipCosts.Clear();

            // energy costs
            this.InitialRetailFuelCosts = 0;
            this.RetailFuelCosts = 0;
            this.ReboundFuelCosts = 0;
            this.DriveSurplus = 0;
            this.ReallocatedVMTSurplus = 0;
            this.RefuelSurplus = 0;
        }

        /// <summary>
        /// Appends all values from the specified <see cref="ConsumerCosts"/> object to this instance.<br/>
        /// <b>NOTE</b>: The payback periods are not appended.
        /// </summary>
        public void Append(ConsumerCosts value)
        {
            // general
            this.Sales           += value.Sales;
            this.NewSalesSurplus += value.NewSalesSurplus;

            // tech costs & value losses
            this.TechCosts        += value.TechCosts;
            this.RegCosts         += value.RegCosts;
            this.MaintRepairCosts += value.MaintRepairCosts;
            this.OpportunityCost  += value.OpportunityCost;

            this.InitTaxCredits        += value.InitTaxCredits;
            this.TaxCredits            += value.TaxCredits;
            this.InitBatteryTaxCredits += value.InitBatteryTaxCredits;
            this.BatteryTaxCredits     += value.BatteryTaxCredits;

            // ownership and operating costs
            this.InitOwnershipCosts.Append(value.InitOwnershipCosts);
            this.OwnershipCosts.Append(value.OwnershipCosts);

            // energy costs
            this.InitialRetailFuelCosts += value.InitialRetailFuelCosts;
            this.RetailFuelCosts        += value.RetailFuelCosts;
            this.ReboundFuelCosts       += value.ReboundFuelCosts;
            this.DriveSurplus           += value.DriveSurplus;
            this.ReallocatedVMTSurplus  += value.ReallocatedVMTSurplus;
            this.RefuelSurplus          += value.RefuelSurplus;
        }

        //----- explicit interface implementation -----
        void IAppendable.Append(IAppendable value)
        {
            this.Append((ConsumerCosts)value);
        }

    }
}
