#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member

using System;
using System.Collections.Generic;

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

namespace Volpe.Cafe.MarketData
{
    /// <summary>
    /// Represents any vehicle component, produced by an original equipment manufacturer (OEM), that encapsulates a
    /// description of its engineering characteristics as well as provides an annual historic view of its revisions.
    /// </summary>
    /// <typeparam name="T">
    ///     The type of the <see cref="IOEMComponentFamily"/> associated with this <see cref="OEMComponent{T}"/>.
    /// </typeparam>
    /// <remarks>
    /// OEM components provide extensions to the <see cref="OEMProduct{TB,TR}"/>s, allowing for multiple vehicles to be
    /// aggregated, or "grouped", for technology application. Components include vehicle platforms, engines, and
    /// transmissions, and may be used directly by a vehicle.
    /// </remarks>
    /// <seealso cref="BaselineComponent"/>
    /// <seealso cref="ComponentRevision"/>
    /// <seealso cref="OEMComponentFamily{T}"/>
    [Serializable]
    public abstract class OEMComponent<T> : OEMProduct<BaselineComponent, IOEMComponentRevision>, IOEMComponent
        where T : IOEMComponentFamily
    {
        #region <<< Constructors >>>

        /// <summary>
        /// Initializes a new instance of the <see cref="OEMComponent{T}"/> class, using the specified baseline info.
        /// </summary>
        /// <param name="baselineInfo">
        ///     A description of baseline (or initial) configuration for this <see cref="OEMComponent{T}"/>.
        /// </param>
        protected OEMComponent(BaselineComponent baselineInfo) : base(baselineInfo)
        {
            this.TechUtilization = new TechnologyUtilization[TechnologyIndexes.TechnologyCount];
        }

        #endregion

        #region <<< Variables >>>

        /// <summary>
        /// Specifies an <see cref="IOEMComponentFamily"/> that this component belongs to.
        /// </summary>
        protected T _family;

        /// <summary>
        /// Specifies a list of <see cref="IVehicle"/>s that use this component.
        /// </summary>
        protected List<IVehicle> _vehicles;

        #endregion

        #region <<< Properties >>>

        /// <inheritdoc cref="IOEMComponent.Family"/>
        public T Family => this._family;

        public IReadOnlyList<IVehicle> Vehicles { get; private set; }

        public string ComponentName => this.RevisionInfo.ComponentName;
        public string FamilyName => this.RevisionInfo.FamilyName;

        //----- explicit interface implementation -----
        IOEMComponentFamily IOEMComponent.Family => this.Family;
        IAnnualValue<IOEMComponentRevision> IOEMComponent.Revisions => this.Revisions;

        #endregion

        #region <<< Methods >>>

        public override void LinkToIndustry(IIndustry industry)
        {
            base.LinkToIndustry(industry);

            this._family = this.FindComponentFamily();
            this._vehicles = this.FindComponentVehicles();

            this.ValidateComponent();

            // create read-only wrappers
            this.Vehicles = this._vehicles.AsReadOnly();
        }

        public void CondenseRevisions()
        {
            for (int year = this.Revisions.MinYear + 1; year <= this.Revisions.MaxYear; year++) {
                var prev = this.Revisions[year - 1];
                var curr = this.Revisions[year];
                if (prev.TechKey == curr.TechKey) {
                    this.Revisions[year] = prev;
                }
            }
        }

        public void RemoveVehicle(IVehicle veh, int analysisYear, int applicationYear)
        {
            this._vehicles.Remove(veh);
        }

        /// <summary>
        /// Creates and returns a new component revision for the specified <paramref name="newYear"/>.
        /// <br/>
        /// If the component does not have any revisions, the new revision will be derived from the component's
        /// baseline information. Otherwise, the new revision will be derived from the component's most recent
        /// revision.
        /// <br/>
        /// This method returns a new revision object of type <see cref="ComponentRevision"/>.
        /// </summary>
        protected override IOEMComponentRevision CreateNewRevision(int newYear)
        {
            var currentRevision = this.RevisionInfo;
            var newRevision = (this.HasRevisions)
                ? new ComponentRevision(newYear, currentRevision)
                : new ComponentRevision(newYear, this);

            return newRevision;
        }

        /// <summary>
        /// Searches the list of available component families within the industry for a component family that matches
        /// this component's <see cref="FamilyName"/>.
        /// </summary>
        /// <returns>
        /// The component family that matches this component's <see cref="FamilyName"/>, or null, if the component
        /// family could not be found.
        /// </returns>
        protected abstract T FindComponentFamily();

        /// <summary>
        /// Searches the list of available vehicles within the industry for all elements that are identified as using
        /// this component's <see cref="IOEMProductInfo.Code"/>.
        /// </summary>
        /// <returns>
        /// A list of vehicles that are identified as using this component's <see cref="IOEMProductInfo.Code"/>, or an
        /// empty list, if no vehicles could be found.
        /// </returns>
        protected abstract List<IVehicle> FindComponentVehicles();

        /// <summary>
        /// Performs some basic validation on a component.
        /// </summary>
        /// <remarks>
        /// Subclasses that override the <see cref="LinkToIndustry"/> method should also call the
        /// <see cref="ValidateComponent"/> method after setting all required references, or implement their own
        /// validation.
        /// </remarks>
        protected virtual void ValidateComponent()
        {
            // perform some validation
            string productText = this.GetType().Name;
            string errors = string.Empty;
            string errHeader = $"{productText} (Code:{this.Code},Name:{this.ManufacturerName}): ";

            if (this.Manufacturer == null) {
                errors += $"{errHeader}Manufacturer Name is not valid.\n";
            }
            if (this._family == null) {
                errors += $"{errHeader}Family Name is not valid.\n";
            }
            //if (component._vehicles == null || component._vehicles.Count == 0) {
            //    errors += $"{errHeader}does not contain any valid vehicles.\n";
            //}

            if (errors.Length != 0) {
                throw new Exception(errors);
            }
        }

        #endregion
    }
}
