using NHibernate;
using System;
using System.Collections.Generic;
using LocationSpecies.Domain;
using NHibernate.Criterion;
using NHibernate.SqlCommand;

namespace WeatherPrep
{
    /// <summary>
    /// Location data for dry deposition calculations
    /// LocationData function called by CreateOutput.cs
    /// </summary>
    public class LocationData
    {
        #region "Class variables"
        // Variables for unused air pollution-related functions and old LocationSpecies.MDB connections
        //public string NationID;
        //public string Nation;
        //public string PriPartID;
        //public string PriPart;
        //public string SecPartID;
        //public string SecPart;
        //public string TerPartID;
        //public string TerPart;
        public double Latitude;
        public double Longitude;
        public int LeafOff_MidPoint_JD;
        public int LeafOn_MidPoint_JD;
        public double Elevation;
        public double GMTOffset;
        public double[] ozone;
        public int AlbedoStID;
        public int albedo;
        public double[] AlbedoVal;
        public double CoeffA;
        public double CoeffC;
        public double CoeffPHI;
        public double TerrainFactor;
        // Variables for unused air pollution-related functions and old LocationSpecies.MDB connections
        //public int[] MonStID;
        //public string[] COMonState;
        //public string[] COMonCounty;
        //public string[] COMonSite;
        //public string[] NO2MonState;
        //public string[] NO2MonCounty;
        //public string[] NO2MonSite;
        //public string[] O3MonState;
        //public string[] O3MonCounty;
        //public string[] O3MonSite;
        //public string[] PM10MonState;
        //public string[] PM10MonCounty;
        //public string[] PM10MonSite;
        //public double[] PM10MonLat;
        //public double[] PM10MonLong;
        //public string[] PM25MonState;
        //public string[] PM25MonCounty;
        //public string[] PM25MonSite;
        //public double[] PM25MonLat;
        //public double[] PM25MonLong;
        //public string[] SO2MonState;
        //public string[] SO2MonCounty;
        //public string[] SO2MonSite;
        //public double COBaseCost;
        //public int COBaseYear;
        //public double COBaseYearPPI;
        //public double NO2BaseCost;
        //public int NO2BaseYear;
        //public double NO2BaseYearPPI;
        //public double O3BaseCost;
        //public int O3BaseYear;
        //public double O3BaseYearPPI;
        //public double PM10BaseCost;
        //public int PM10BaseYear;
        //public double PM10BaseYearPPI;
        //public double PM25BaseCost;
        //public int PM25BaseYear;
        //public double PM25BaseYearPPI;
        //public double SO2BaseCost;
        //public int SO2BaseYear;
        //public double SO2BaseYearPPI;
        //public double ModelYearPPI;

        private ISession _s;
        #endregion
        //public static Location LookupLocation takes ISession NHibernate_Session, string place, string county, string state, string nation
        public static Location LookupLocation(ISession NHibernate_Session, string place, string county, string state, string nation)
        {
            using (ITransaction tx = NHibernate_Session.BeginTransaction())
            {
                string[] names = { place, county, state, nation };
                var excludedTypes = new[] { LocationType.CountySubdivision, LocationType.CensusSubdivision };

                bool isUSA = nation == "United States of America";
                var query = NHibernate_Session.QueryOver<Location>();

                if (isUSA)
                    query = query.Where(loc => !loc.Type.IsIn(excludedTypes));

                for (int i = 0; i < names.Length; i++)
                {
                    if (!string.IsNullOrEmpty(names[i]))
                    {
                        if (i > 0)
                        {
                            query = query
                                .JoinQueryOver<LocationRelation>(l => l.Relations, JoinType.InnerJoin)
                                .JoinQueryOver(r => r.Parent, JoinType.InnerJoin);
                        }

                        query = query.Where(loc => loc.Name.IsLike(names[i]));
                    }
                }

                return query
                    .Select(Projections.RootEntity())
                    .Cacheable()
                    .SingleOrDefault();
            }
        }

        // LocationData takes ISession NHibernate_Session to create constructors
        public LocationData(ISession NHibernate_Session)
        {
            ozone = new double[12];
            AlbedoVal = new double[12];

            _s = NHibernate_Session; 
        }

        //ReadLocationData function takes location of SQLite LocationSpecies Database, using NHibernate protocols
        public void ReadLocationData(Location location)
        {
            try
            {
                //NHibernate function .BeginTransaction with SQLite LocationSpecies Database
                using (ITransaction NHibernate_Transaction = _s.BeginTransaction())
                {
                    // Define Lat/Long and LeafOn/OffDOY using given location.
                    Latitude = location.Latitude;
                    Longitude = location.Longitude;
                    Elevation = location.Elevation;
                    LeafOff_MidPoint_JD = location.GrowthPeriod.LeafOffDays;
                    LeafOn_MidPoint_JD = location.GrowthPeriod.LeafOnDays;

                    //If LeafOff_MidPoint_JD and LeafOn_MidPoint_JD are both 0, then for North or South Hemisphere the LeafOff_MidPoint_JD = 365 and LeafOn_MidPoint_JD = 0
                    if (LeafOff_MidPoint_JD == 0 && LeafOn_MidPoint_JD == 0)
                    {
                        //LeafOff_MidPoint_JD set to 365
                        //Note: Will correct for leap year in function CreateLAI_AnnualDaily_NoFrostDays
                        LeafOff_MidPoint_JD = 365;
                    }

                    // Functions to walk-up location tree for given locaton if given location does not have needed data.
                    // Unique functions are provided because different parent locations may be needed to derive each data.
                    NHibernate_Transaction.Commit();
                }
                ReadTimezoneData(location);
                ReadOzoneData(location);
                ReadAlbedoData(location);
            }
            catch (Exception)
            {
                Console.Write("\n");
                Console.Write("Warning: Apparent error in WeatherPrepConfig.xml. ReadLocationData function cannot find that Place.\n");
                Console.Write("Suggestion: If the place does not exist in the i-Tree Location Database, then create it with Add to Database.\n");
                throw;
            }
        }

        //ReadTimezoneData takes location and gets timezone data
        void ReadTimezoneData(Location location)
        {
            using (ITransaction tx = _s.BeginTransaction())
            {
                var timeZone = location.TimeZone;
                while (location != null && timeZone == null)
                {
                    var lr = _s.QueryOver<LocationRelation>()
                        .Where(r => r.Location == location && r.Code != null)
                        .Cacheable()
                        .SingleOrDefault();

                    location = lr?.Parent;
                    timeZone = location?.TimeZone;
                }
                GMTOffset = timeZone.Offset;
                tx.Commit();
            }
        }

        //ReadOzoneData takes location and gets ozone data
        void ReadOzoneData(Location location)
        {
            IDictionary<Month, double> ozoneValues = null;

            using (ITransaction tx = _s.BeginTransaction())
            {
                var stateLat = location.StateLat;
                while (location != null && stateLat == null)
                {
                    var lr = _s.QueryOver<LocationRelation>()
                        .Where(r => r.Location == location && r.Code != null)
                        .Cacheable()
                        .SingleOrDefault();

                    location = lr?.Parent;
                    stateLat = location?.StateLat;
                }
                ozoneValues = stateLat?.OzoneValues ?? new Dictionary<Month, double>();
                tx.Commit();
            }

            foreach (KeyValuePair<Month, double> kvp in ozoneValues)
            {
                ozone[((int)kvp.Key) - 1] = kvp.Value;
            }
        }

        //ReadAlbedoData takes location and gets albedo data
        void ReadAlbedoData(Location location)
        {
            try
            {
                using (ITransaction NHibernate_Transaction = _s.BeginTransaction())
                {
                    AlbedoStID = location.AlbedoStation.Id;
                    CoeffA = location.AlbedoStation.CoefficientA;
                    CoeffC = location.AlbedoStation.CoefficientC;
                    CoeffPHI = location.AlbedoStation.CoefficientPhi;
                    foreach (KeyValuePair<Month, double> kvp in location.AlbedoStation.Albedo.Values)
                    {
                        AlbedoVal[((int)kvp.Key) - 1] = kvp.Value;
                    }
                    NHibernate_Transaction.Commit();
                }
            }
            catch (Exception)
            {
                throw;
            }
        }
    }
}


