using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Threading.Tasks;
using System.Xml;
using SqlWeb;
using SqlWeb.Data;
using NHibernate;
//Located at: https://data.itreetools.org/sql
using LocationSpecies.Domain;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using System.Reflection;
using LocationSpecies;
using NHibernate.Criterion;
using NHibernate.Linq;
using System.Linq;

//Note: LocationData_Object.LeafOn_MidPoint_JD is date when leaf has started to grow
//Note: LocationData_Object.LeafOff_MidPoint_JD is date when leaf has started to senescence

namespace WeatherPrep
{
    public class ForestData
    {
        public double DomainArea;
        public double EvGrnLAI;
        public double MaxLAI;
        public double PctEvGrnCov;
        public double PctTrCov;
        public double TrCovArea;

        public enum RURAL_URBAN { RURAL, URBAN };
        const double RURAL_LAI = 3.2;
        const double URBAN_LAI = 4.9;

        /// <summary>
        /// Set urban forest data to class variables.
        /// </summary>
        /// <param name="area">Area of analysis domain</param>
        /// <param name="lai">Maximam leaf area index in analysis domain</param>
        /// <param name="evergreen">Percent of evergreen vegetation in analysis domain</param>
        /// <param name="treeCover">Percent of vegetation cover in analysis domain</param>
        public ForestData(double area, double LeafAreaIndexMax, double Evergreen_pct, double TreeCover_pct)
        {
            DomainArea = area;
            MaxLAI = LeafAreaIndexMax;
            PctEvGrnCov = Evergreen_pct;
            PctTrCov = TreeCover_pct;
            EvGrnLAI = LeafAreaIndexMax * Evergreen_pct / 100;
            TrCovArea = area * TreeCover_pct / 100;
        }
    }

    public class LeafAreaIndex
    {
        //TimeStamp defined as DateTime variable
        DateTime TimeStamp;
        //Lai defined as double variable
        public double Lai;

        //LAI_TABLE defined as string LAI
        const string LAI_TABLE = "LAI";
        //Days_LeafTransition_Half_Duration defined as integer 14, which is half the typical full transition period of 28 days
        const int Days_LeafTransition_Half_Duration = 14;

        /// <summary>
        /// Process annual LAI calculations.
        /// </summary>
        /// <param name="LAI_max_m2pm2">Maximum LAI for the analysis domain</param>
        /// <param name="CanopyCover_Evergreen_Percent">Percent of evergreen vegetation for the analysis domain</param>
        /// <param name="LocationData_Object">Location data</param>
        /// <param name="DateYear_Start_int">Start year for the LAI process</param>
        /// <param name="DateYear_Stop_int">End year for the LAI process</param>
        /// <param name="LeafAreaIndex_dataBase">Path to LAI database</param>
        public static void ProcessLAI(double LAI_max_m2pm2, double CanopyCover_Evergreen_Percent, LocationData LocationData_Object,
                                    int DateYear_Start_int, int DateYear_Stop_int, string LeafAreaIndex_dataBase)
        {
            //LAI_TimeSeries_AnnualDaily_list defined to contain class LeafAreaIndex and its properties
            List<LeafAreaIndex> LAI_TimeSeries_AnnualDaily_list;
            //LAI_TimeSeries_AnnualHourly_list defined to contain class LeafAreaIndex and its properties
            List<LeafAreaIndex> LAI_TimeSeries_AnnualHourly_list;
            int Year_Simulated_YYYY;
            //LAI_min_m2pm2 initialized as double, later defined as LAI_max_m2pm2 * Evergreen_fraction
            double LAI_min_m2pm2;
            //NorthernHemisphere_Is_Location_boolean function returns True if LeafOff_MidPoint_JD > LeafOn_MidPoint_JD, otherwise false and in Southern Hemisphere
            bool NorthernHemisphere_Is_Location_boolean = LocationData_Object.LeafOff_MidPoint_JD > LocationData_Object.LeafOn_MidPoint_JD ? true : false;
            //FrostDays_Equal_Zero_boolean function returns true of LeafOn_MidPoint_JD is 0 or 1 and LeafOff_MidPoint_JD is 365 or 366
            bool FrostDays_Equal_Zero_boolean = (LocationData_Object.LeafOn_MidPoint_JD == 0 || LocationData_Object.LeafOn_MidPoint_JD == 1) &&
                (LocationData_Object.LeafOff_MidPoint_JD == 365 || LocationData_Object.LeafOff_MidPoint_JD == 366) ? true : false;
            //LeafOn_Start_JD (Julian Day) is LeafOn_MidPoint_JD minus Days_LeafTransition_Half_Duration, half of leaf transition days
            int LeafOn_Start_JD = LocationData_Object.LeafOn_MidPoint_JD - Days_LeafTransition_Half_Duration;
            //LeafOn_Stop_JD (Julian Day) is LeafOn_MidPoint_JD plus Days_LeafTransition_Half_Duration, half of leaf transition days
            int LeafOn_Stop_JD = LocationData_Object.LeafOn_MidPoint_JD + Days_LeafTransition_Half_Duration;
            //LeafOff_Start_JD (Julian Day) is LeafOff_MidPoint_JD minus Days_LeafTransition_Half_Duration, half of leaf transition days
            int LeafOff_Start_JD = LocationData_Object.LeafOff_MidPoint_JD - Days_LeafTransition_Half_Duration;
            //LeafOff_Stop_JD (Julian Day) is LeafOff_MidPoint_JD plus Days_LeafTransition_Half_Duration, half of leaf transition days
            int LeafOff_Stop_JD = LocationData_Object.LeafOff_MidPoint_JD + Days_LeafTransition_Half_Duration;

            try
            {
                //LAI_min_m2pm2 defined, defined as LAI_max_m2pm2 * CanopyCover_Evergreen_Percent/100, or Evergreen_fraction
                LAI_min_m2pm2 = LAI_max_m2pm2 * (CanopyCover_Evergreen_Percent / 100);

                //For loop from DateYear_Start_int to DateYear_Stop_int, typically the same date
                for (Year_Simulated_YYYY = DateYear_Start_int; Year_Simulated_YYYY <= DateYear_Stop_int; Year_Simulated_YYYY++)
                {
                    //flag_StartYear initialized to 0 or false
                    int flag_StartYear = 0;
                    //if Year_Simulated_YYYY equals the DateYear_Start_int then flag_StartYear set to 1 or true
                    if (Year_Simulated_YYYY == DateYear_Start_int)
                    {
                        //flag_StartYear set to 1 or true
                        flag_StartYear = 1;
                    }

                    //LAI_TimeSeries_AnnualDaily_list initialized as new LeafAraIndex class
                    LAI_TimeSeries_AnnualDaily_list = new List<LeafAreaIndex>();
                    //LAI_TimeSeries_AnnualHourly_list initialized as new LeafAraIndex class
                    LAI_TimeSeries_AnnualHourly_list = new List<LeafAreaIndex>();

                    //If FrostDays_Equal_Zero_boolean (boolean either true or false) then call CreateLAI_AnnualDaily_NoFrostDays
                    if (FrostDays_Equal_Zero_boolean)
                    {
                        CreateLAI_AnnualDaily_NoFrostDays(Year_Simulated_YYYY, LAI_max_m2pm2, ref LAI_TimeSeries_AnnualDaily_list, LocationData_Object);
                    }
                    //Else If NorthernHemisphere_Is_Location_boolean (boolean either true or false) then call CreateLAI_AnnualDaily_NorthernHemisphere
                    else if (NorthernHemisphere_Is_Location_boolean)
                    {
                        CreateLAI_AnnualDaily_NorthernHemisphere(Year_Simulated_YYYY, LAI_min_m2pm2, LAI_max_m2pm2,
                                LocationData_Object.LeafOn_MidPoint_JD, LocationData_Object.LeafOff_MidPoint_JD, ref LAI_TimeSeries_AnnualDaily_list,
                                LeafOn_Start_JD, LeafOn_Stop_JD, LeafOff_Start_JD, LeafOff_Stop_JD);
                    }
                    //Else If not NorthernHemisphere_Is_Location_boolean (boolean either true or false) then call CreateLAI_AnnualDaily_SouthernHemisphere
                    else
                    {
                        CreateLAI_AnnualDaily_SouthernHemisphere(Year_Simulated_YYYY, LAI_min_m2pm2, LAI_max_m2pm2,
                                LocationData_Object.LeafOn_MidPoint_JD, LocationData_Object.LeafOff_MidPoint_JD, ref LAI_TimeSeries_AnnualDaily_list,
                                LeafOn_Start_JD, LeafOn_Stop_JD, LeafOff_Start_JD, LeafOff_Stop_JD);
                    }

                    //call CreateLAI_AnnualHourly function
                    CreateLAI_AnnualHourly(ref LAI_TimeSeries_AnnualDaily_list, ref LAI_TimeSeries_AnnualHourly_list);
                    //csv defined as string using function String.Join with comma separating terms in LAI_TimeSeries_AnnualHourly_list
                    string csv = String.Join(",", LAI_TimeSeries_AnnualHourly_list);

                    //cte 2025 Consider removal, and convey leaf on and off dates differently
                    //call WriteLAIRecords function
                    WriteLAIRecords(LeafAreaIndex_dataBase, ref LAI_TimeSeries_AnnualHourly_list, LocationData_Object, flag_StartYear);
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        //CreateLAI_AnnualDaily_NoFrostDays function receives Year_Simulated_YYYY, LAI_max_m2pm2 as LAI maximum, and LAI_TimeSeries_SentBetweenFunctions_list instance of the LeafAreaIndex LAI_TimeSeries_SentBetweenFunctions_list class, and LocationData LocationData_Object
        static void CreateLAI_AnnualDaily_NoFrostDays(int Year_Simulated_YYYY, double LAI_max_m2pm2, ref List<LeafAreaIndex> LAI_TimeSeries_SentBetweenFunctions_list, LocationData LocationData_Object)
        {
            //JulianDay_Last_in_Year defined as int based on response of boolean IsLeapYear function, if true then 366, otherwise 365
            int JulianDay_Last_in_Year = DateTime.IsLeapYear(Year_Simulated_YYYY) ? 366 : 365;

            //If JulianDay_Last_in_Year = 366 and LeafOn_MidPoint_JD = 365, then set LeafOn_MidPoint_JD = 366
            if (JulianDay_Last_in_Year == 366 && LocationData_Object.LeafOff_MidPoint_JD == 365)
            {
                //LeafOff_MidPoint_JD set to 366
                LocationData_Object.LeafOff_MidPoint_JD = 366;
            }
            //If JulianDay_Last_in_Year = 366 and LeafOn_MidPoint_JD = 365, then set LeafOn_MidPoint_JD = 366
            if (JulianDay_Last_in_Year == 366 && LocationData_Object.LeafOn_MidPoint_JD == 365)
            {
                //LeafOn_MidPoint_JD set to 366
                LocationData_Object.LeafOn_MidPoint_JD = 366;
            }

            int i;
            //LAI_TimeSeries_list defined as LeafAreaIndex class
            LeafAreaIndex LAI_TimeSeries_list;
            //DateTime_JulianDay_or_Hour initialized as new DateTime variable for Year_Simulated_YYYY
            DateTime DateTime_JulianDay_or_Hour = new DateTime(Year_Simulated_YYYY, 1, 1);

            //For loop through JulianDay_Last_in_Year
            for (i = 0; i < JulianDay_Last_in_Year; i++)
            {
                //LAI_TimeSeries_list constructed with LeafAreaIndex function
                LAI_TimeSeries_list = new LeafAreaIndex();
                //LAI_TimeSeries_list.TimeStamp defined as date and hour for j
                LAI_TimeSeries_list.TimeStamp = DateTime_JulianDay_or_Hour;
                //LAI_TimeSeries_list.Lai variable equals LAI_max_m2pm2 for all time steps
                LAI_TimeSeries_list.Lai = LAI_max_m2pm2;
                //LAI_TimeSeries_SentBetweenFunctions_list updated with LAI_TimeSeries_list member
                LAI_TimeSeries_SentBetweenFunctions_list.Add(LAI_TimeSeries_list);
                //DateTime_JulianDay_or_Hour updated with 1 day, using function .AddDays 
                DateTime_JulianDay_or_Hour = DateTime_JulianDay_or_Hour.AddDays(1);
            }
        }
        /// <summary>
        /// Create an annual daily LAI values starting from LeafOn_MidPoint_JD-Days_LeafTransition_Half_Duration for Northern hemisphere.
        /// </summary>
        /// <param name="Year_Simulated_YYYY"></param>
        /// <param name="LAI_min_m2pm2"></param>
        /// <param name="LAI_max_m2pm2"></param>
        /// <param name="LeafOn_MidPoint_JD"></param>
        /// <param name="LeafOff_MidPoint_JD"></param>
        /// <param name="LAI_TimeSeries_SentBetweenFunctions_list"></param>
        /// <remarks>
        /// 
        /// 
        ///  OnStart         OnEnd                      OffStart        OffEnd
        ///   <------> <------>                          <------> <------> 
        ///              
        ///   +-------+-------+-------- -------//--------+-------+-------+---------+
        ///         leafOnDOY                                 leafOffDOY    
        ///            <----------------------------------------->
        ///                            Days_LeafOnDuration
        ///   <----------------------------------------->
        ///                   Days_LeafOnDuration
        /// 
        /// </remarks>
        /// 
        
        //CreateLAI_AnnualDaily_NorthernHemisphere function computes LAI for Northern Hemisphere
        static void CreateLAI_AnnualDaily_NorthernHemisphere(int Year_Simulated_YYYY, double LAI_min_m2pm2, double LAI_max_m2pm2,
                            int LeafOn_MidPoint_JD, int LeafOff_MidPoint_JD, ref List<LeafAreaIndex> LAI_TimeSeries_SentBetweenFunctions_list,
                            int LeafOn_Start_JD, int LeafOn_Stop_JD, int LeafOff_Start_JD, int LeafOff_Stop_JD)
        {
            //JulianDay_Last_in_Year defined as int based on response of boolean IsLeapYear function, if true then 366, otherwise 365
            int JulianDay_Last_in_Year = DateTime.IsLeapYear(Year_Simulated_YYYY) ? 366 : 365;
            int i, j, Days_LeafOnDuration;
            //LAI_TimeSeries_list defined as LeafAreaIndex class
            LeafAreaIndex LAI_TimeSeries_list;

            //Days_LeafOnDuration defined as period between LAI midpoint, during which LAI max is achieved
            Days_LeafOnDuration = LeafOff_MidPoint_JD - LeafOn_MidPoint_JD;
            //j defined as Days_LeafOnDuration, then increase below
            j = Days_LeafOnDuration;

            //For Loop through JulianDay_Last_in_Year to initialize vectors to set all values to LAI_min_m2pm2
            for (i = 0; i < JulianDay_Last_in_Year; i++)
            {
                LAI_TimeSeries_list = new LeafAreaIndex();
                LAI_TimeSeries_list.Lai = LAI_min_m2pm2;
                LAI_TimeSeries_SentBetweenFunctions_list.Add(LAI_TimeSeries_list);
            }

            //For Loop from (Days_LeafTransition_Half_Duration + 1) through Days_LeafOnDuration to set all transition and max values to LAI_max_m2pm2
            //Note: Appreciate the peculiar time period of this i range = last half of LAI ramping up period and LAI max period 
            //Note: Overwriting prior values
            for (i = (Days_LeafTransition_Half_Duration + 1); i < Days_LeafOnDuration; i++)
            {
                LAI_TimeSeries_SentBetweenFunctions_list[i].Lai = LAI_max_m2pm2;
            }
            //For Loop through all LAI increasing days, (Days_LeafTransition_Half_Duration * 2 + 1), to set LAI values based on exponential function
            //Note: Appreciate the peculiar time period of this i range = all of LAI ramping up period 
            //Note: Overwriting prior values
            for (i = 0; i < (Days_LeafTransition_Half_Duration * 2 + 1); i++)
            {
                LAI_TimeSeries_SentBetweenFunctions_list[i].Lai = LAI_min_m2pm2 + (LAI_max_m2pm2 - LAI_min_m2pm2) / 
                    (1 + Math.Exp(-0.37 * (-Days_LeafTransition_Half_Duration + i)));
            }
            //For Loop through all LAI decreasing days, (Days_LeafTransition_Half_Duration * 2 + 1), to set LAI values based on exponential function
            //Note: Appreciate the peculiar time period of this i range = all of LAI ramping down period, created by use of j counter in [j++]
            //Note: Overwriting prior values
            for (i = 0; i < (Days_LeafTransition_Half_Duration * 2 + 1); i++)
            {
                LAI_TimeSeries_SentBetweenFunctions_list[j++].Lai = LAI_min_m2pm2 + (LAI_max_m2pm2 - LAI_min_m2pm2) / 
                    (1 + Math.Exp(-0.37 * (Days_LeafTransition_Half_Duration - i)));
            }

            //DateTime_JulianDay_or_Hour initialized with DateTime function for (Year_Simulated_YYYY - 1) December 31, 1 day before start of year 
            DateTime DateTime_JulianDay_or_Hour = new DateTime(Year_Simulated_YYYY - 1, 12, 31);
            //LAI_TimeSeries_SentBetweenFunctions_list vector 1st item TimeStamp defined
            LAI_TimeSeries_SentBetweenFunctions_list[0].TimeStamp = DateTime_JulianDay_or_Hour.AddDays(LeafOn_MidPoint_JD - Days_LeafTransition_Half_Duration);

            //If LAI_TimeSeries_SentBetweenFunctions_list .TimeStamp.Year vector 1st item is not Year_Simulated_YYYY then 
            //Note: This should be true, and the TimeStamp.Year is (Year_Simulated_YYYY - 1)
            if (LAI_TimeSeries_SentBetweenFunctions_list[0].TimeStamp.Year != Year_Simulated_YYYY)
            {
                //DateTime_JulianDay_or_Hour initialized for TimeStamp .Month and .Day
                DateTime_JulianDay_or_Hour = new DateTime(Year_Simulated_YYYY, LAI_TimeSeries_SentBetweenFunctions_list[0].TimeStamp.Month,
                    LAI_TimeSeries_SentBetweenFunctions_list[0].TimeStamp.Day);
                //LAI_TimeSeries_SentBetweenFunctions_list .TimeStamp vector 1st item is defined as DateTime_JulianDay_or_Hour
                LAI_TimeSeries_SentBetweenFunctions_list[0].TimeStamp = DateTime_JulianDay_or_Hour;
            }
            //For loop through LAI_TimeSeries_SentBetweenFunctions_list based on .Count function, will increment by days
            for (i = 1; i < LAI_TimeSeries_SentBetweenFunctions_list.Count; i++)
            {
                //LAI_TimeSeries_SentBetweenFunctions_list .TimeStamp vector item defined as LAI_TimeSeries_SentBetweenFunctions_list prior item + 1
                //Note: Using .AddDays() function to increment days
                LAI_TimeSeries_SentBetweenFunctions_list[i].TimeStamp = LAI_TimeSeries_SentBetweenFunctions_list[i - 1].TimeStamp.AddDays(1);
                //If LAI_TimeSeries_SentBetweenFunctions_list .TimeStamp.Year exceeds Year_Simulated_YYYY then
                if (LAI_TimeSeries_SentBetweenFunctions_list[i].TimeStamp.Year > Year_Simulated_YYYY)
                {
                    //DateTime_JulianDay_or_Hour initialized for TimeStamp .Month and .Day of Year_Simulated_YYYY
                    DateTime_JulianDay_or_Hour = new DateTime(Year_Simulated_YYYY, LAI_TimeSeries_SentBetweenFunctions_list[i].TimeStamp.Month,
                        LAI_TimeSeries_SentBetweenFunctions_list[i].TimeStamp.Day);
                    //LAI_TimeSeries_SentBetweenFunctions_list .TimeStamp vector item is defined as DateTime_JulianDay_or_Hour
                    LAI_TimeSeries_SentBetweenFunctions_list[i].TimeStamp = DateTime_JulianDay_or_Hour;
                }
            }
        }
        /// <summary>
        /// Create an annual daily LAI values starting from LeafOff_MidPoint_JD-Days_LeafTransition_Half_Duration for Southern hemisphere.
        /// </summary>
        /// <param name="Year_Simulated_YYYY"></param>
        /// <param name="LAI_min_m2pm2"></param>
        /// <param name="LAI_max_m2pm2"></param>
        /// <param name="LeafOn_MidPoint_JD"></param>
        /// <param name="LeafOff_MidPoint_JD"></param>
        /// <param name="LAI_TimeSeries_SentBetweenFunctions_list"></param>
        /// <remarks>
        /// 
        /// 
        ///  OffStart       OffEnd                      OnStart         OnEnd
        ///   <------> <------>                          <------> <------> 
        ///              
        ///   +-------+-------+-------- -------//--------+-------+-------+---------+
        ///       leafOffDOY                                 leafOnDOY    
        ///            <----------------------------------------->
        ///                            lfOffDuration
        ///   <----------------------------------------->
        ///                   lfOffDuration
        /// 
        /// </remarks>

        //CreateLAI_AnnualDaily_SouthernHemisphere function computes LAI for Northern Hemisphere
        //Note: See Northern Hemisphere function for more comments in code
        static void CreateLAI_AnnualDaily_SouthernHemisphere(int Year_Simulated_YYYY, double LAI_min_m2pm2, double LAI_max_m2pm2,
                            int LeafOn_MidPoint_JD, int LeafOff_MidPoint_JD, ref List<LeafAreaIndex> LAI_TimeSeries_SentBetweenFunctions_list,
                            int LeafOn_Start_JD, int LeafOn_Stop_JD, int LeafOff_Start_JD, int LeafOff_Stop_JD)
        {
            int JulianDay_Last_in_Year = DateTime.IsLeapYear(Year_Simulated_YYYY) ? 366 : 365;
            int i, j, lfOffDuration;
            LeafAreaIndex LAI_TimeSeries_list;

            lfOffDuration = LeafOn_MidPoint_JD - LeafOff_MidPoint_JD;

            // set maximum value throughout a year
            for (i = 0; i < JulianDay_Last_in_Year; i++)
            {
                LAI_TimeSeries_list = new LeafAreaIndex();
                LAI_TimeSeries_list.Lai = LAI_max_m2pm2;
                LAI_TimeSeries_SentBetweenFunctions_list.Add(LAI_TimeSeries_list);
            }
            // overwrite minimum values from leaf off through on dates
            for (i = (Days_LeafTransition_Half_Duration + 1); i < lfOffDuration; i++)
            {
                LAI_TimeSeries_SentBetweenFunctions_list[i].Lai = LAI_min_m2pm2;
            }
            // overwrite maximum to minimum transition values in the first 29 days of the LAI_TimeSeries_SentBetweenFunctions_list
            for (i = 0; i < (Days_LeafTransition_Half_Duration * 2 + 1); i++)
            {
                LAI_TimeSeries_SentBetweenFunctions_list[i].Lai = LAI_min_m2pm2 + (LAI_max_m2pm2 - LAI_min_m2pm2) / 
                    (1 + Math.Exp(-0.37 * (Days_LeafTransition_Half_Duration - i)));
            }
            // overwrite minimum to maximum transition values 
            j = lfOffDuration;
            for (i = 0; i < (Days_LeafTransition_Half_Duration * 2 + 1); i++)
            {
                LAI_TimeSeries_SentBetweenFunctions_list[j++].Lai = LAI_min_m2pm2 + (LAI_max_m2pm2 - LAI_min_m2pm2) / 
                    (1 + Math.Exp(-0.37 * (-Days_LeafTransition_Half_Duration + i)));
            }

            // set timestamp starting from leaf off DOY - Days_LeafTransition_Half_Duration
            DateTime DateTime_JulianDay_or_Hour = new DateTime(Year_Simulated_YYYY - 1, 12, 31);
            LAI_TimeSeries_SentBetweenFunctions_list[0].TimeStamp = DateTime_JulianDay_or_Hour.AddDays(LeafOff_MidPoint_JD - Days_LeafTransition_Half_Duration);
            // The starting year calculated may result in the previous year.
            // This should be adjusted.
            if (LAI_TimeSeries_SentBetweenFunctions_list[0].TimeStamp.Year != Year_Simulated_YYYY)
            {
                DateTime_JulianDay_or_Hour = new DateTime(Year_Simulated_YYYY, LAI_TimeSeries_SentBetweenFunctions_list[0].TimeStamp.Month, LAI_TimeSeries_SentBetweenFunctions_list[0].TimeStamp.Day);
                LAI_TimeSeries_SentBetweenFunctions_list[0].TimeStamp = DateTime_JulianDay_or_Hour;
            }
            for (i = 1; i < LAI_TimeSeries_SentBetweenFunctions_list.Count; i++)
            {
                LAI_TimeSeries_SentBetweenFunctions_list[i].TimeStamp = LAI_TimeSeries_SentBetweenFunctions_list[i - 1].TimeStamp.AddDays(1);
                // adjust the year if exceeds the current year
                if (LAI_TimeSeries_SentBetweenFunctions_list[i].TimeStamp.Year > Year_Simulated_YYYY)
                {
                    DateTime_JulianDay_or_Hour = new DateTime(Year_Simulated_YYYY, LAI_TimeSeries_SentBetweenFunctions_list[i].TimeStamp.Month, LAI_TimeSeries_SentBetweenFunctions_list[i].TimeStamp.Day);
                    LAI_TimeSeries_SentBetweenFunctions_list[i].TimeStamp = DateTime_JulianDay_or_Hour;
                }
            }
        }
        static void CreateLAI_AnnualHourly(ref List<LeafAreaIndex> LAI_TimeSeries_AnnualDaily_list, ref List<LeafAreaIndex> LAI_TimeSeries_AnnualHourly_list)
        {
            int i, j;
            //LAI_TimeSeries_list defined as LeafAreaIndex class
            LeafAreaIndex LAI_TimeSeries_list;
            //DateTime_JulianDay_or_Hour defined as DateTime variable
            DateTime DateTime_JulianDay_or_Hour;

            //For loop through LAI_TimeSeries_AnnualDaily_list
            for (i = 0; i < LAI_TimeSeries_AnnualDaily_list.Count; i++)
            {
                DateTime_JulianDay_or_Hour = LAI_TimeSeries_AnnualDaily_list[i].TimeStamp;
                //For loop through hours of day
                for (j = 0; j < 24; j++)
                {
                    //LAI_TimeSeries_list constructed with LeafAreaIndex function
                    LAI_TimeSeries_list = new LeafAreaIndex();
                    //LAI_TimeSeries_list.Lai defined as LAI using LAI_TimeSeries_AnnualDaily_list on day i
                    LAI_TimeSeries_list.Lai = LAI_TimeSeries_AnnualDaily_list[i].Lai;
                    //LAI_TimeSeries_list.TimeStamp defined as date and hour for j
                    LAI_TimeSeries_list.TimeStamp = DateTime_JulianDay_or_Hour.AddHours(j);
                    //LAI_TimeSeries_AnnualHourly_list vector updated with LAI_TimeSeries_list variable
                    LAI_TimeSeries_AnnualHourly_list.Add(LAI_TimeSeries_list);
                }
            }
        }

        public static void WriteLAIRecords(
            string LeafAreaIndex_dataBase,
            ref List<LeafAreaIndex> LeafAreaIndex_list,
            LocationData LocationData_Object,
            int flag_StartYear)
        {
            try
            {
                string[] LeafAreaIndex_string = new string[LeafAreaIndex_list.Count + 1];

                if (flag_StartYear == 1)
                {
                    Console.WriteLine("StartYear has LeafOnDay = " +
                                      LocationData_Object.LeafOn_MidPoint_JD +
                                      " LeafOffDay = " +
                                      LocationData_Object.LeafOff_MidPoint_JD + "\n");

                    LeafAreaIndex_string[0] =
                        "TimeStamp,LAI,StartYear_LeafOnDay=" +
                        LocationData_Object.LeafOn_MidPoint_JD +
                        ",StartYear_LeafOffDay=" +         // (small typo fix: Off vs Of)
                        LocationData_Object.LeafOff_MidPoint_JD;
                }

                for (int i = 0; i < LeafAreaIndex_list.Count; i++)
                {
                    string[] rowString = new string[2];
                    rowString[0] = Convert.ToString(LeafAreaIndex_list[i].TimeStamp);
                    rowString[1] = Convert.ToString(LeafAreaIndex_list[i].Lai);
                    LeafAreaIndex_string[i + 1] = String.Join(",", rowString);
                }

                string csv = String.Join("\r\n", LeafAreaIndex_string);

                if (flag_StartYear == 1)
                {
                    File.WriteAllText(LeafAreaIndex_dataBase, csv);
                }
                else
                {
                    File.AppendAllText(LeafAreaIndex_dataBase, csv);
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// Read partial parts of hourly LAI values.
        /// </summary>
        /// <param name="LeafAreaIndex_dataBase">Path to LAI database</param>
        /// <param name="laiArr">Array to store LAI values</param>
        /// <param name="start">Start date and time to read</param>
        /// <param name="end">End date and time to read</param>
        /// <returns>Record count in the array</returns>
        public static int ReadLAIPartialRecords(string LeafAreaIndex_dataBase, ref List<LeafAreaIndex> LeafAreaIndex_list, DateTime start, DateTime end)
        {
            string line;
            LeafAreaIndex laiData;

            //LeafAreaIndex_dataBase is string that contains the LAI database file location and name as .csv
            //sr becomes instance of this LAI.csv file
            using (StreamReader sr = new StreamReader(LeafAreaIndex_dataBase))
            {
                try
                {
                    //read the header line
                    sr.ReadLine();

                    while ((line = sr.ReadLine()) != null)
                    {
                        //laiData is new instance of LeafAreaIndex constructor
                        laiData = new LeafAreaIndex();

                        //laiData is given the TimeStamp property by parsing the with DateTime function at the 1st item in line read from sr
                        laiData.TimeStamp = DateTime.Parse(line.Split(',')[0]);

                        //laiData is given the Lai property by parsing the with Double function at the 2nd item in the line read from sr
                        laiData.Lai = Double.Parse(line.Split(',')[1]);

                        //DateTime.Compare(DateTime t1, DateTime t2) method is used to compare two DateTime objects. It returns an integer that indicates the relationship between the two dates:
                        //If the return value is less than 0, it means that the first DateTime object(t1) comes before the second DateTime object(t2).
                        //If the return value is 0, it means both DateTime objects are equal, or represent the same time.
                        //If the return value is greater than 0, it means that the first DateTime object(t1) comes after the second DateTime object(t2).
                        int result1 = DateTime.Compare(laiData.TimeStamp, start);
                        int result2 = DateTime.Compare(laiData.TimeStamp, end);

                        //If result1 >= 0 then then it means laiData.TimeStamp is after or at the start
                        //If result2 <= 0 then then it means laiData.TimeStamp is before or at the end
                        if (result1 >= 0 && result2 <= 0)
                        {
                            //LeafAreaIndex_list is appended with new laiData
                            LeafAreaIndex_list.Add(laiData);
                        }
                    }

                    LeafAreaIndex_list.Sort((x, y) => DateTime.Compare(x.TimeStamp, y.TimeStamp)); //Sorting the LAI time-series by DateTime
                    return (LeafAreaIndex_list.Count);
                }
                catch (Exception)
                {
                    throw;
                }
            }
        }
    }
}
