using System;

using Volpe.Cafe.MarketData.BaselineInfo;
using Volpe.Cafe.MarketData.Design;
using Volpe.Cafe.ModelSettings;

namespace Volpe.Cafe.TechAnalysis
{
    /// <summary>
    /// Provides helper functions for evaluating amounts of various credits and adjustments resulting from off-cycle
    /// technologies that a manufacturer may claim toward compliance with either NHTSA's CAFE standards or EPA's CO-
    /// 2 standards, as well as the associated costs resulting from those technologies.
    /// </summary>
    public static class OffCycleTechEvaluator
    {
        /// <summary>
        /// Calculates the costs resulting from the various off-cycle technologies claimed by a manufacturer, as
        /// applicable to the specified vehicle during a given model year.
        /// </summary>
        /// <param name="veh">The vehicle for which to calculate the various off-cycle costs.</param>
        /// <param name="scen">The compliance scenario being analyzed.</param>
        /// <param name="year">The model year being analyzed.</param>
        /// <param name="settings">The modeling settings used for analysis.</param>
        /// <returns>
        /// The costs associated with the various off-cycle technologies claimed by a manufacturer, as applicable to
        /// the specified vehicle.
        /// </returns>
        /// <remarks>
        /// The costs for the various off-cycle technologies are calculated (and returned) as separate entities for air
        /// conditioning (AC) efficiency improvements, AC leakage improvements, and other "off-cycle" technologies. All
        /// of these costs are computed based on an "incremental" value of credits claimed by a manufacturer between
        /// the current analysis year and the first year available for modeling. Currently, the full-size pickup
        /// credits do not incur an additional cost and are, therefore, computed as 0 (zero).
        /// </remarks>
        public static OffCycleValues CalcOffCycleCosts(
            IVehicle veh, Scenario scen, int year, ModelingSettings settings)
        {
            var runtimeSettings = settings.RuntimeSettings;
            int minYear = runtimeSettings.AnalysisStartYear;
            if (year == minYear) {
                return OffCycleValues.Empty;
            }

            var refYearCredits = GetCappedCredits(minYear);
            var claimedCredits = GetCappedCredits(year);
            var offCycleCosts = scen[veh.GetRevision(year).RegClass, year].OffCycleCosts;
            var claimedCreditCosts = new OffCycleValues(
                (claimedCredits.AcEfficiency - refYearCredits.AcEfficiency) * offCycleCosts.AcEfficiency,
                (claimedCredits.AcLeakage - refYearCredits.AcLeakage) * offCycleCosts.AcLeakage,
                (claimedCredits.OffCycle - refYearCredits.OffCycle) * offCycleCosts.OffCycle,
                offCycleCosts.FullSizePickup);

            if (runtimeSettings.CapOffCycleCosts) {
                claimedCreditCosts.AcEfficiency = Math.Max(claimedCreditCosts.AcEfficiency, 0);
                claimedCreditCosts.AcLeakage = Math.Max(claimedCreditCosts.AcLeakage, 0);
                claimedCreditCosts.OffCycle = Math.Max(claimedCreditCosts.OffCycle, 0);
            }

            return claimedCreditCosts;

            // local helper function
            OffCycleValues GetCappedCredits(int claimYear)
            {
                var vehRevision = veh.Revisions[claimYear];
                var regClass = vehRevision.RegClass;
                var hevType = vehRevision.HevType;
                var program = runtimeSettings.ComplianceProgram;

                var caps = scen[regClass, claimYear].GetOffCycleCaps(program, hevType);
                var credits = veh.Manufacturer.CreditsAndAdjustments[regClass, claimYear];
                var cappedCredits = new OffCycleValues(
                    Math.Min(credits.ACEfficiency, caps.AcEfficiency),
                    Math.Min(credits.ACLeakage, caps.AcLeakage),
                    Math.Min(credits.OffCycleCredits, caps.OffCycle),
                    fullSizePickup: 0);

                return cappedCredits;
            }
        }

        /// <summary>
        /// Calculates the various off-cycle credits, subject to the specified non-battery-electric vehicle (non-BEV)
        /// credit caps and BEV credit caps, weighted based on the specified amounts of total and BEV-only vehicle
        /// sales of a manufacturer.
        /// </summary>
        /// <param name="offCycleCredits">
        ///     The various credits and adjustments that a manufacturer may use toward compliance with either NHTSA's
        ///     CAFE standards or EPA's CO-2 standards.
        /// </param>
        /// <param name="offCycleCaps">
        ///     The caps for various credits and adjustments that a manufacturer may claim toward compliance with
        ///     either NHTSA's CAFE standards or EPA's CO-2 standards for non-battery-electric vehicles (non-BEVs).
        /// </param>
        /// <param name="offCycleBevCaps">
        ///     The caps for various credits and adjustments that a manufacturer may claim toward compliance with
        ///     either NHTSA's CAFE standards or EPA's CO-2 standards for battery-electric vehicles (BEVs).
        /// </param>
        /// <param name="mfrSales">The total sales of a manufacturer.</param>
        /// <param name="mfrBevSales">The total BEV-only sales of a manufacturer.</param>
        /// <returns>
        /// The various weighted off-cycle credits, capped by the specified non-BEV credit caps and BEV credit caps.
        /// </returns>
        /// <remarks>
        /// Unlike other off-cycle values, the full-size pickup credits are not subject to any credit caps (non-BEV or
        /// BEV). Therefore, the <see cref="OffCycleValues.FullSizePickup"/> value will be computed as unweighted based
        /// on the <see cref="CreditsAndAdjustmentsInfo.FSPCredits"/> from the <paramref name="offCycleCredits"/>
        /// parameter.
        /// </remarks>
        public static OffCycleValues CalcWeightedOffCycleCredits(
            CreditsAndAdjustmentsInfo offCycleCredits, OffCycleValues offCycleCaps, OffCycleValues offCycleBevCaps,
            double mfrSales, double mfrBevSales)
        {
            var weightedCredits = new OffCycleValues(
                CalcWCredits(offCycleCredits.ACEfficiency, offCycleCaps.AcEfficiency, offCycleBevCaps.AcEfficiency),
                CalcWCredits(offCycleCredits.ACLeakage, offCycleCaps.AcLeakage, offCycleBevCaps.AcLeakage),
                CalcWCredits(offCycleCredits.OffCycleCredits, offCycleCaps.OffCycle, offCycleBevCaps.OffCycle),
                offCycleCredits.FSPCredits);

            return weightedCredits;

            // local helper function
            double CalcWCredits(double credits, double cap, double bevCap)
            {
                double mfrNonBevSales = mfrSales - mfrBevSales;
                return (Math.Min(credits, cap) * mfrNonBevSales
                      + Math.Min(credits, bevCap) * mfrBevSales) / mfrSales;
            }
        }

    }
}
