using MeteoWebApp.Data;
using MeteoWebApp.Helpers;
using MeteoWebApp.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace MeteoWebApp.Controllers
{
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;
        private readonly MeteoDbContext _db;

        public HomeController(ILogger<HomeController> logger, MeteoDbContext db)
        {
            _logger = logger;
            _db = db;
        }
 /* FirstRun section */
        /* Create username and password */
        public IActionResult FirstRun()
        {
            // First run is available only when no login exists in database
            if (!_db.Logins.Any())
            {
                return View();
            }
            return RedirectToAction("Index");
        }
/* End of FirstRun section */


/* Login section */

        /* Process login from forms */

        public IActionResult Login(string name = null, string pass = null)
        {
            if (name == null || pass == null)
            {
                // Return default login page
                return View();
            }
            else
            {
                if (_db.Logins.Any())
                {
                    List<Login> logins = _db.Logins.ToList();
                    Login result = null;
                    SHA256 hash = SHA256.Create();

                    foreach (Login login in logins)
                    {
                        // Match username and password
                        if (name == login.Name && Encoding.UTF8.GetString(hash.ComputeHash(Encoding.UTF8.GetBytes(pass))) == login.PasswordHash)
                        {
                            result = login;
                        }
                    }
                    // Username and password was found in database 
                    if (result != null)
                    {
                        // Set session keys
                        HttpContext.Session.Set("Authorized", Encoding.UTF8.GetBytes("true"));
                        HttpContext.Session.Set("User", Encoding.UTF8.GetBytes(name));
                        // Redirect to settings after login
                        return RedirectToAction("Settings");
                    }
                }
                return RedirectToAction("Index");
            }
        }

        /* Create new entry in database for user */
        public IActionResult SetLogin(string name, string pass)
        {
            if (!_db.Logins.Any())
            {
                Login result = new Login();
                SHA256 hash = SHA256.Create();

                byte[] hashed = hash.ComputeHash(Encoding.UTF8.GetBytes(pass));
                string stringHashed = Encoding.UTF8.GetString(hashed);

                result.Name = name;
                result.PasswordHash = stringHashed;

                _db.Logins.Add(result);
                _db.SaveChanges();

                HttpContext.Session.Set("Authorized", Encoding.UTF8.GetBytes("true"));
                HttpContext.Session.Set("User", Encoding.UTF8.GetBytes(name));
            }
            return RedirectToAction("Settings");
        }

        /*  Clear session to disable login */
        [HttpPost]
        public IActionResult Logout()
        {
            HttpContext.Session.Clear();

            return RedirectToAction("Index");
        }
/* End of Login section */


/* Index section */
        /* Index - show station which is default or by selected id */
        public IActionResult Index(int id = 0)
        {
            // Redirect to first run when there is no user in database
            if (!_db.Logins.Any())
            {
                return RedirectToAction("FirstRun");
            }
            else
            {
                // Show selected station
                if (id != 0)
                {
                    if (_db.Stations.Where(m => m.StationId == id).Any() && _db.Measurements.Where(m => m.StationId == id).Any())
                    {

                        var model = new Tuple<Station, Measurement>(_db.Stations.Where(s => s.StationId == id).First(), _db.Measurements.Where(m => m.StationId == id).OrderByDescending(o => o.MeasurementId).First());
                        return View(model);
                    }

                }
                // Show default station
                else
                {
                    if (_db.Stations.Where(s => s.Default == true).Any())
                    {
                        if (_db.Measurements.Where(m => m.StationId == _db.Stations.Where(m => m.Default).Select(m => m.StationId).Single()).Any())
                        {
                            var model = new Tuple<Station, Measurement>(_db.Stations.Where(m => m.Default == true).First(), _db.Measurements.Where(m => m.StationId == _db.Stations.Where(m => m.Default).Select(m => m.StationId).Single()).OrderByDescending(o => o.MeasurementId).First());
                            return View(model);
                        }
                    }
                }
                // Return index view with null and show user that there is no data to present
                return View(null);
            }
        }
/* End of Index section */


/* Map section */
        public IActionResult Map()
        {
            if (!_db.Stations.Any())
            {
                return RedirectToAction("Index");
            }
            else
            { 
                List<Station> stations = _db.Stations.ToList();
                List<Measurement> measurements = new List<Measurement>();

                foreach (Station station in _db.Stations.ToList())
                {
                    if (_db.Measurements.Where(m => m.StationId == station.StationId).Any())
                    {
                        // Select last measurement from each station to show values on map
                        // otherwise do nothing
                        measurements.Add(_db.Measurements.Where(m => m.StationId == station.StationId).OrderByDescending(o => o.MeasurementId).First());
                    }
                }
                var model = new Tuple<List<Station>, List<Measurement>>(stations, measurements);
                return View(model);
            }
        }
/* End of Map section */


/* History section */
        /* History data */
        public IActionResult History(int stationId = 0, int reportType = 1, DateTime firstDate = new DateTime(), List<string> graphs = null)
        {
            // There is no station in database -> redirect to index which show info to user
            if (!_db.Stations.Any())
            {
                return RedirectToAction("Index");
            }

            // Station id was not defined -> Show data for default station
            if (stationId == 0)
            {
                stationId = _db.Stations.Where(m => m.Default).Select(m => m.StationId).Single();
            }

            // There is no measurements in database for selected station -> redirect to index which show info to user
            if (!_db.Measurements.Where(m => m.StationId == stationId).Any())
            {
                return RedirectToAction("Index", new { id = stationId });
            }
            // There is data for selected station in database go and select them

            List<DateTime> calendarDates = new List<DateTime>();
            List<Station> stations = _db.Stations.ToList();
            List<Measurement> measurement1 = new List<Measurement>();
            List<Measurement> measurement2 = new List<Measurement>();
            List<Measurement> measurementAverage = new List<Measurement>();
            List<Measurement> measurementMin = new List<Measurement>();
            List<Measurement> measurementMax = new List<Measurement>();

            // Date was not defined -> Show data for today
            if (firstDate == new DateTime())
            {
                firstDate = DateTime.Today;
            }

            // Prepare limit dates for calender
            calendarDates.Add(_db.Measurements.Select(m => m.Timestamp).Min().Date);
            calendarDates.Add(firstDate);
            calendarDates.Add(_db.Measurements.Select(m => m.Timestamp).Max().Date);

            // Select data from database according to selection -> default is one day history
            switch (reportType) 
            {
                // Day
                case 1:
                    {
                        DateTime nextDate = firstDate.AddDays(1);

                        // This is not average values it is onlystored to this variable to simplify return parameters
                        measurementAverage = _db.Measurements.Where(m => m.Timestamp > firstDate && m.Timestamp < nextDate && m.StationId == stationId).ToList();
                        break;
                    }
                // Week
                case 2:
                    {
                        DateTime nextDate = firstDate.AddDays(7);

                        // Select coresponding data from database according to date interval
                        measurement1 = _db.Measurements.Where(m => m.Timestamp > firstDate && m.Timestamp < nextDate && m.StationId == stationId).ToList();

                        // Go thru selected interval and count avg, min and max values
                        for (DateTime date = firstDate; date <= nextDate.AddHours(-1); date = date.AddHours(1))
                        {
                            // Select 1 hour interval
                            measurement2 = measurement1.Where(m => m.Timestamp > date && m.Timestamp < date.AddHours(1)).ToList();
                            // Calculate average values for
                            if (measurement2.Count() != 0)
                            {
                                measurementAverage.Add(measurement2.GroupBy(m => m.StationId).Select(g => new Measurement
                                {
                                    MeasurementId = 1,
                                    StationId = g.Key,
                                    Timestamp = date,
                                    Temperature = (float)Math.Round(g.Average(m => m.Temperature), 2),
                                    DewPoint = (float)Math.Round(g.Average(m => m.DewPoint), 2),
                                    TemperatureFeelsLike = (float)Math.Round(g.Average(m => m.TemperatureFeelsLike), 2),
                                    Humidity = (float)Math.Round(g.Average(m => m.Humidity), 2),
                                    Pressure = (float)Math.Round(g.Average(m => m.Pressure), 2),
                                    SeaLevelPressure = (float)Math.Round(g.Average(m => m.SeaLevelPressure), 2),
                                    WindSpeed = (float)Math.Round(g.Average(m => m.WindSpeed), 2),
                                    WindAngle = (float)Math.Round(g.Average(m => m.WindAngle), 2),
                                    WindAngleStr = "",
                                    RainAmount = (float)Math.Round(g.Sum(m => m.RainAmount), 2),
                                    UvIndex = (float)Math.Round(g.Average(m => m.UvIndex), 2),
                                    SoilTempDepth1 = measurement2.Select(m => m.SoilTempDepth1).FirstOrDefault(),
                                    SoilTemp1 = (float)Math.Round(g.Average(m => m.SoilTemp1), 2),
                                    SoilTempDepth2 = measurement2.Select(m => m.SoilTempDepth2).FirstOrDefault(),
                                    SoilTemp2 = (float)Math.Round(g.Average(m => m.SoilTemp2), 2),
                                    SoilTempDepth3 = measurement2.Select(m => m.SoilTempDepth3).FirstOrDefault(),
                                    SoilTemp3 = (float)Math.Round(g.Average(m => m.SoilTemp3), 2),
                                    SoilMoistureDepth1 = measurement2.Select(m => m.SoilMoistureDepth1).FirstOrDefault(),
                                    SoilMoisture1 = (float)Math.Round(g.Average(m => m.SoilMoisture1), 2),
                                    SoilMoistureDepth2 = measurement2.Select(m => m.SoilMoistureDepth2).FirstOrDefault(),
                                    SoilMoisture2 = (float)Math.Round(g.Average(m => m.SoilMoisture2), 2),
                                    SoilMoistureDepth3 = measurement2.Select(m => m.SoilMoistureDepth3).FirstOrDefault(),
                                    SoilMoisture3 = (float)Math.Round(g.Average(m => m.SoilMoisture3), 2),
                                    BatteryVoltage = (float)Math.Round(g.Average(m => m.BatteryVoltage), 2)
                                }).First());
                                // calculate Min values
                                measurementMin.Add(measurement2.GroupBy(m => m.StationId).Select(g => new Measurement
                                {
                                    MeasurementId = 1,
                                    StationId = g.Key,
                                    Timestamp = date,
                                    Temperature = (float)Math.Round(g.Min(m => m.Temperature), 2),
                                    DewPoint = (float)Math.Round(g.Min(m => m.DewPoint), 2),
                                    TemperatureFeelsLike = (float)Math.Round(g.Min(m => m.TemperatureFeelsLike), 2),
                                    Humidity = (float)Math.Round(g.Min(m => m.Humidity), 2),
                                    Pressure = (float)Math.Round(g.Min(m => m.Pressure), 2),
                                    SeaLevelPressure = (float)Math.Round(g.Min(m => m.SeaLevelPressure), 2),
                                    WindSpeed = (float)Math.Round(g.Min(m => m.WindSpeed), 2),
                                    WindAngle = (float)Math.Round(g.Min(m => m.WindAngle), 2),
                                    WindAngleStr = "",
                                    RainAmount = (float)Math.Round(g.Min(m => m.RainAmount), 2),
                                    UvIndex = (float)Math.Round(g.Min(m => m.UvIndex), 2),
                                    SoilTempDepth1 = "",
                                    SoilTemp1 = (float)Math.Round(g.Min(m => m.SoilTemp1), 2),
                                    SoilTempDepth2 = "",
                                    SoilTemp2 = (float)Math.Round(g.Min(m => m.SoilTemp2), 2),
                                    SoilTempDepth3 = "",
                                    SoilTemp3 = (float)Math.Round(g.Min(m => m.SoilTemp3), 2),
                                    SoilMoistureDepth1 = "",
                                    SoilMoisture1 = (float)Math.Round(g.Min(m => m.SoilMoisture1), 2),
                                    SoilMoistureDepth2 = "",
                                    SoilMoisture2 = (float)Math.Round(g.Min(m => m.SoilMoisture2), 2),
                                    SoilMoistureDepth3 = "",
                                    SoilMoisture3 = (float)Math.Round(g.Min(m => m.SoilMoisture3), 2),
                                    BatteryVoltage = (float)Math.Round(g.Min(m => m.BatteryVoltage), 2)
                                }).First());
                                // calculate max values
                                measurementMax.Add(measurement2.GroupBy(m => m.StationId).Select(g => new Measurement
                                {
                                    MeasurementId = 1,
                                    StationId = g.Key,
                                    Timestamp = date,
                                    Temperature = (float)Math.Round(g.Max(m => m.Temperature), 2),
                                    DewPoint = (float)Math.Round(g.Max(m => m.DewPoint), 2),
                                    TemperatureFeelsLike = (float)Math.Round(g.Max(m => m.TemperatureFeelsLike), 2),
                                    Humidity = (float)Math.Round(g.Max(m => m.Humidity), 2),
                                    Pressure = (float)Math.Round(g.Max(m => m.Pressure), 2),
                                    SeaLevelPressure = (float)Math.Round(g.Max(m => m.SeaLevelPressure), 2),
                                    WindSpeed = (float)Math.Round(g.Max(m => m.WindSpeed), 2),
                                    WindAngle = (float)Math.Round(g.Max(m => m.WindAngle), 2),
                                    WindAngleStr = "",
                                    RainAmount = (float)Math.Round(g.Max(m => m.RainAmount), 2),
                                    UvIndex = (float)Math.Round(g.Max(m => m.UvIndex), 2),
                                    SoilTempDepth1 = "",
                                    SoilTemp1 = (float)Math.Round(g.Max(m => m.SoilTemp1), 2),
                                    SoilTempDepth2 = "",
                                    SoilTemp2 = (float)Math.Round(g.Max(m => m.SoilTemp2), 2),
                                    SoilTempDepth3 = "",
                                    SoilTemp3 = (float)Math.Round(g.Max(m => m.SoilTemp3), 2),
                                    SoilMoistureDepth1 = "",
                                    SoilMoisture1 = (float)Math.Round(g.Max(m => m.SoilMoisture1), 2),
                                    SoilMoistureDepth2 = "",
                                    SoilMoisture2 = (float)Math.Round(g.Max(m => m.SoilMoisture2), 2),
                                    SoilMoistureDepth3 = "",
                                    SoilMoisture3 = (float)Math.Round(g.Max(m => m.SoilMoisture3), 2),
                                    BatteryVoltage = (float)Math.Round(g.Max(m => m.BatteryVoltage), 2)
                                }).First());
                            }
                        }
                        break;
                    }
                // Month
                case 3:
                    {
                        DateTime nextDate = firstDate.AddMonths(1);
                        // Select coresponding data from database according to date interval
                        measurement1 = _db.Measurements.Where(m => m.Timestamp > firstDate && m.Timestamp < nextDate && m.StationId == stationId).ToList();

                        // Go thru selected interval and count avg, min and max values
                        for (DateTime date = firstDate; date <= nextDate.AddDays(-1); date = date.AddDays(1))
                        {
                            // Select 1 day interval
                            measurement2 = measurement1.Where(m => m.Timestamp > date && m.Timestamp < date.AddDays(1)).ToList();
                            // Calculate average values for interval
                            if (measurement2.Count() != 0)
                            {
                                measurementAverage.Add(measurement2.GroupBy(m => m.StationId).Select(g => new Measurement
                                {
                                    MeasurementId = 1,
                                    StationId = g.Key,
                                    Timestamp = date,
                                    Temperature = (float)Math.Round(g.Average(m => m.Temperature), 2),
                                    DewPoint = (float)Math.Round(g.Average(m => m.DewPoint), 2),
                                    TemperatureFeelsLike = (float)Math.Round(g.Average(m => m.TemperatureFeelsLike), 2),
                                    Humidity = (float)Math.Round(g.Average(m => m.Humidity), 2),
                                    Pressure = (float)Math.Round(g.Average(m => m.Pressure), 2),
                                    SeaLevelPressure = (float)Math.Round(g.Average(m => m.SeaLevelPressure), 2),
                                    WindSpeed = (float)Math.Round(g.Average(m => m.WindSpeed), 2),
                                    WindAngle = (float)Math.Round(g.Average(m => m.WindAngle), 2),
                                    WindAngleStr = "",
                                    RainAmount = (float)Math.Round(g.Sum(m => m.RainAmount), 2),
                                    UvIndex = (float)Math.Round(g.Average(m => m.UvIndex), 2),
                                    SoilTempDepth1 = measurement2.Select(m => m.SoilTempDepth1).FirstOrDefault(),
                                    SoilTemp1 = (float)Math.Round(g.Average(m => m.SoilTemp1), 2),
                                    SoilTempDepth2 = measurement2.Select(m => m.SoilTempDepth2).FirstOrDefault(),
                                    SoilTemp2 = (float)Math.Round(g.Average(m => m.SoilTemp2), 2),
                                    SoilTempDepth3 = measurement2.Select(m => m.SoilTempDepth3).FirstOrDefault(),
                                    SoilTemp3 = (float)Math.Round(g.Average(m => m.SoilTemp3), 2),
                                    SoilMoistureDepth1 = measurement2.Select(m => m.SoilMoistureDepth1).FirstOrDefault(),
                                    SoilMoisture1 = (float)Math.Round(g.Average(m => m.SoilMoisture1), 2),
                                    SoilMoistureDepth2 = measurement2.Select(m => m.SoilMoistureDepth2).FirstOrDefault(),
                                    SoilMoisture2 = (float)Math.Round(g.Average(m => m.SoilMoisture2), 2),
                                    SoilMoistureDepth3 = measurement2.Select(m => m.SoilMoistureDepth3).FirstOrDefault(),
                                    SoilMoisture3 = (float)Math.Round(g.Average(m => m.SoilMoisture3), 2),
                                    BatteryVoltage = (float)Math.Round(g.Average(m => m.BatteryVoltage), 2)
                                }).First());
                                // Calculate Min values
                                measurementMin.Add(measurement2.GroupBy(m => m.StationId).Select(g => new Measurement
                                {
                                    MeasurementId = 1,
                                    StationId = g.Key,
                                    Timestamp = date,
                                    Temperature = (float)Math.Round(g.Min(m => m.Temperature), 2),
                                    DewPoint = (float)Math.Round(g.Min(m => m.DewPoint), 2),
                                    TemperatureFeelsLike = (float)Math.Round(g.Min(m => m.TemperatureFeelsLike), 2),
                                    Humidity = (float)Math.Round(g.Min(m => m.Humidity), 2),
                                    Pressure = (float)Math.Round(g.Min(m => m.Pressure), 2),
                                    SeaLevelPressure = (float)Math.Round(g.Min(m => m.SeaLevelPressure), 2),
                                    WindSpeed = (float)Math.Round(g.Min(m => m.WindSpeed), 2),
                                    WindAngle = (float)Math.Round(g.Min(m => m.WindAngle), 2),
                                    WindAngleStr = "",
                                    RainAmount = (float)Math.Round(g.Min(m => m.RainAmount), 2),
                                    UvIndex = (float)Math.Round(g.Min(m => m.UvIndex), 2),
                                    SoilTempDepth1 = "",
                                    SoilTemp1 = (float)Math.Round(g.Min(m => m.SoilTemp1), 2),
                                    SoilTempDepth2 = "",
                                    SoilTemp2 = (float)Math.Round(g.Min(m => m.SoilTemp2), 2),
                                    SoilTempDepth3 = "",
                                    SoilTemp3 = (float)Math.Round(g.Min(m => m.SoilTemp3), 2),
                                    SoilMoistureDepth1 = "",
                                    SoilMoisture1 = (float)Math.Round(g.Min(m => m.SoilMoisture1), 2),
                                    SoilMoistureDepth2 = "",
                                    SoilMoisture2 = (float)Math.Round(g.Min(m => m.SoilMoisture2), 2),
                                    SoilMoistureDepth3 = "",
                                    SoilMoisture3 = (float)Math.Round(g.Min(m => m.SoilMoisture3), 2),
                                    BatteryVoltage = (float)Math.Round(g.Min(m => m.BatteryVoltage), 2)
                                }).First());
                                // Calculate max values
                                measurementMax.Add(measurement2.GroupBy(m => m.StationId).Select(g => new Measurement
                                {
                                    MeasurementId = 1,
                                    StationId = g.Key,
                                    Timestamp = date,
                                    Temperature = (float)Math.Round(g.Max(m => m.Temperature), 2),
                                    DewPoint = (float)Math.Round(g.Max(m => m.DewPoint), 2),
                                    TemperatureFeelsLike = (float)Math.Round(g.Max(m => m.TemperatureFeelsLike), 2),
                                    Humidity = (float)Math.Round(g.Max(m => m.Humidity), 2),
                                    Pressure = (float)Math.Round(g.Max(m => m.Pressure), 2),
                                    SeaLevelPressure = (float)Math.Round(g.Max(m => m.SeaLevelPressure), 2),
                                    WindSpeed = (float)Math.Round(g.Max(m => m.WindSpeed), 2),
                                    WindAngle = (float)Math.Round(g.Max(m => m.WindAngle), 2),
                                    WindAngleStr = "",
                                    RainAmount = (float)Math.Round(g.Max(m => m.RainAmount), 2),
                                    UvIndex = (float)Math.Round(g.Max(m => m.UvIndex), 2),
                                    SoilTempDepth1 = "",
                                    SoilTemp1 = (float)Math.Round(g.Max(m => m.SoilTemp1), 2),
                                    SoilTempDepth2 = "",
                                    SoilTemp2 = (float)Math.Round(g.Max(m => m.SoilTemp2), 2),
                                    SoilTempDepth3 = "",
                                    SoilTemp3 = (float)Math.Round(g.Max(m => m.SoilTemp3), 2),
                                    SoilMoistureDepth1 = "",
                                    SoilMoisture1 = (float)Math.Round(g.Max(m => m.SoilMoisture1), 2),
                                    SoilMoistureDepth2 = "",
                                    SoilMoisture2 = (float)Math.Round(g.Max(m => m.SoilMoisture2), 2),
                                    SoilMoistureDepth3 = "",
                                    SoilMoisture3 = (float)Math.Round(g.Max(m => m.SoilMoisture3), 2),
                                    BatteryVoltage = (float)Math.Round(g.Max(m => m.BatteryVoltage), 2)
                                }).First());
                            }
                        }
                        break;
                    }
                // Year
                case 4:
                    {
                        DateTime nextDate = firstDate.AddYears(1);
                        // Select coresponding data from database according to date interval
                        measurement1 = _db.Measurements.Where(m => m.Timestamp > firstDate && m.Timestamp < nextDate && m.StationId == stationId).ToList();

                        // Go thru selected interval and count avg, min and max values
                        for (DateTime date = firstDate; date <= nextDate.AddDays(-7); date = date.AddDays(7))
                        {
                            // Select 1 week interval
                            measurement2 = measurement1.Where(m => m.Timestamp > date && m.Timestamp < date.AddDays(7)).ToList();
                            // Calculate average values for interval
                            if (measurement2.Count() != 0)
                            {
                                measurementAverage.Add(measurement2.GroupBy(m => m.StationId).Select(g => new Measurement
                                {
                                    MeasurementId = 1,
                                    StationId = g.Key,
                                    Timestamp = date,
                                    Temperature = (float)Math.Round(g.Average(m => m.Temperature), 2),
                                    DewPoint = (float)Math.Round(g.Average(m => m.DewPoint), 2),
                                    TemperatureFeelsLike = (float)Math.Round(g.Average(m => m.TemperatureFeelsLike), 2),
                                    Humidity = (float)Math.Round(g.Average(m => m.Humidity), 2),
                                    Pressure = (float)Math.Round(g.Average(m => m.Pressure), 2),
                                    SeaLevelPressure = (float)Math.Round(g.Average(m => m.SeaLevelPressure), 2),
                                    WindSpeed = (float)Math.Round(g.Average(m => m.WindSpeed), 2),
                                    WindAngle = (float)Math.Round(g.Average(m => m.WindAngle), 2),
                                    WindAngleStr = "",
                                    RainAmount = (float)Math.Round(g.Sum(m => m.RainAmount), 2),
                                    UvIndex = (float)Math.Round(g.Average(m => m.UvIndex), 2),
                                    SoilTempDepth1 = measurement2.Select(m => m.SoilTempDepth1).FirstOrDefault(),
                                    SoilTemp1 = (float)Math.Round(g.Average(m => m.SoilTemp1), 2),
                                    SoilTempDepth2 = measurement2.Select(m => m.SoilTempDepth2).FirstOrDefault(),
                                    SoilTemp2 = (float)Math.Round(g.Average(m => m.SoilTemp2), 2),
                                    SoilTempDepth3 = measurement2.Select(m => m.SoilTempDepth3).FirstOrDefault(),
                                    SoilTemp3 = (float)Math.Round(g.Average(m => m.SoilTemp3), 2),
                                    SoilMoistureDepth1 = measurement2.Select(m => m.SoilMoistureDepth1).FirstOrDefault(),
                                    SoilMoisture1 = (float)Math.Round(g.Average(m => m.SoilMoisture1), 2),
                                    SoilMoistureDepth2 = measurement2.Select(m => m.SoilMoistureDepth2).FirstOrDefault(),
                                    SoilMoisture2 = (float)Math.Round(g.Average(m => m.SoilMoisture2), 2),
                                    SoilMoistureDepth3 = measurement2.Select(m => m.SoilMoistureDepth3).FirstOrDefault(),
                                    SoilMoisture3 = (float)Math.Round(g.Average(m => m.SoilMoisture3), 2),
                                    BatteryVoltage = (float)Math.Round(g.Average(m => m.BatteryVoltage), 2)
                                }).First());
                                // Calculate Min values
                                measurementMin.Add(measurement2.GroupBy(m => m.StationId).Select(g => new Measurement
                                {
                                    MeasurementId = 1,
                                    StationId = g.Key,
                                    Timestamp = date,
                                    Temperature = (float)Math.Round(g.Min(m => m.Temperature), 2),
                                    DewPoint = (float)Math.Round(g.Min(m => m.DewPoint), 2),
                                    TemperatureFeelsLike = (float)Math.Round(g.Min(m => m.TemperatureFeelsLike), 2),
                                    Humidity = (float)Math.Round(g.Min(m => m.Humidity), 2),
                                    Pressure = (float)Math.Round(g.Min(m => m.Pressure), 2),
                                    SeaLevelPressure = (float)Math.Round(g.Min(m => m.SeaLevelPressure), 2),
                                    WindSpeed = (float)Math.Round(g.Min(m => m.WindSpeed), 2),
                                    WindAngle = (float)Math.Round(g.Min(m => m.WindAngle), 2),
                                    WindAngleStr = "",
                                    RainAmount = (float)Math.Round(g.Min(m => m.RainAmount), 2),
                                    UvIndex = (float)Math.Round(g.Min(m => m.UvIndex), 2),
                                    SoilTempDepth1 = "",
                                    SoilTemp1 = (float)Math.Round(g.Min(m => m.SoilTemp1), 2),
                                    SoilTempDepth2 = "",
                                    SoilTemp2 = (float)Math.Round(g.Min(m => m.SoilTemp2), 2),
                                    SoilTempDepth3 = "",
                                    SoilTemp3 = (float)Math.Round(g.Min(m => m.SoilTemp3), 2),
                                    SoilMoistureDepth1 = "",
                                    SoilMoisture1 = (float)Math.Round(g.Min(m => m.SoilMoisture1), 2),
                                    SoilMoistureDepth2 = "",
                                    SoilMoisture2 = (float)Math.Round(g.Min(m => m.SoilMoisture2), 2),
                                    SoilMoistureDepth3 = "",
                                    SoilMoisture3 = (float)Math.Round(g.Min(m => m.SoilMoisture3), 2),
                                    BatteryVoltage = (float)Math.Round(g.Min(m => m.BatteryVoltage), 2)
                                }).First());
                                // Calculate max values
                                measurementMax.Add(measurement2.GroupBy(m => m.StationId).Select(g => new Measurement
                                {
                                    MeasurementId = 1,
                                    StationId = g.Key,
                                    Timestamp = date,
                                    Temperature = (float)Math.Round(g.Max(m => m.Temperature), 2),
                                    DewPoint = (float)Math.Round(g.Max(m => m.DewPoint), 2),
                                    TemperatureFeelsLike = (float)Math.Round(g.Max(m => m.TemperatureFeelsLike), 2),
                                    Humidity = (float)Math.Round(g.Max(m => m.Humidity), 2),
                                    Pressure = (float)Math.Round(g.Max(m => m.Pressure), 2),
                                    SeaLevelPressure = (float)Math.Round(g.Max(m => m.SeaLevelPressure), 2),
                                    WindSpeed = (float)Math.Round(g.Max(m => m.WindSpeed), 2),
                                    WindAngle = (float)Math.Round(g.Max(m => m.WindAngle), 2),
                                    WindAngleStr = "",
                                    RainAmount = (float)Math.Round(g.Max(m => m.RainAmount), 2),
                                    UvIndex = (float)Math.Round(g.Max(m => m.UvIndex), 2),
                                    SoilTempDepth1 = "",
                                    SoilTemp1 = (float)Math.Round(g.Max(m => m.SoilTemp1), 2),
                                    SoilTempDepth2 = "",
                                    SoilTemp2 = (float)Math.Round(g.Max(m => m.SoilTemp2), 2),
                                    SoilTempDepth3 = "",
                                    SoilTemp3 = (float)Math.Round(g.Max(m => m.SoilTemp3), 2),
                                    SoilMoistureDepth1 = "",
                                    SoilMoisture1 = (float)Math.Round(g.Max(m => m.SoilMoisture1), 2),
                                    SoilMoistureDepth2 = "",
                                    SoilMoisture2 = (float)Math.Round(g.Max(m => m.SoilMoisture2), 2),
                                    SoilMoistureDepth3 = "",
                                    SoilMoisture3 = (float)Math.Round(g.Max(m => m.SoilMoisture3), 2),
                                    BatteryVoltage = (float)Math.Round(g.Max(m => m.BatteryVoltage), 2)
                                }).First());
                            }
                        }
                        break;
                    }
                default:
                    {
                        DateTime nextDate = firstDate.AddDays(1);

                        // This is not average values it is only to simplyfy return parameters
                        measurementAverage = _db.Measurements.Where(m => m.Timestamp > firstDate && m.Timestamp < nextDate && m.StationId == stationId).ToList();

                        break;
                    }
            }

            var model = new Tuple<List<Station>, List<DateTime>, List<string>, int, List<Measurement>, List<Measurement>, List<Measurement>>(stations, calendarDates, graphs, reportType, measurementAverage, measurementMin, measurementMax);
            return View(model);
        }

/* End of History section */


/* Settings section */
        /* Settings, only for authorized user */
        [Login]
        public IActionResult Settings()
        {
            List<Station> stations = _db.Stations.ToList();
            List<Measurement> measurements = new List<Measurement>();

            foreach (Station station in stations)
            {
                if (_db.Measurements.Where(m => m.StationId == station.StationId).Any())
                {    
                    // Select last measurement for each station
                    measurements.Add(_db.Measurements.Where(m => m.StationId == station.StationId).OrderByDescending(m => m.MeasurementId).First());
                }
            }

            // When lists are empty only card for add new station is shown 
            var model = new Tuple<List<Station>, List<Measurement>>(stations, measurements);
            return View(model);
        }

        /* Create new station, only for authorized user */
        [Login]
        [HttpPost]
        public IActionResult CreateStation(string name, float gpsLatitude, float gpsLongitude, bool defaultStation = false)
        {
            if (_db.Stations.Any())
            {   
                // New station was selected as default -> set others Default value to false
                if (defaultStation)
                {
                    List<Station> stations = _db.Stations.ToList();
                    _db.Stations.AttachRange(stations);
                    foreach (Station stationFalse in stations)
                    {
                        stationFalse.Default = false;
                    }
                }
            }
            else// Set first station to default
            { 
                defaultStation = true;
            }

            Station station = new Station();
            // Id is auto increment db value
            station.Name = name;
            station.GpsLatitude = gpsLatitude;
            station.GpsLongitude = gpsLongitude;
            station.Default = defaultStation;
            station.ApiKey = Guid.NewGuid();
            //station.ApiKey = Guid.Parse("a139c273-e27d-4e69-97d0-7fdc6b46e96f"); // Brno station for testing
            _db.Stations.Add(station);
            _db.SaveChanges();
            return RedirectToAction("Settings");
        }

        /* Modify station information from settings, only for authorized user */
        [Login]
        [HttpPost]
        public IActionResult ModifyStation(int stationId, string name, float gpsLatitude, float gpsLongitude, bool defaultStation = false)
        {

            // Station was selected as default -> set others Default value to false
            if (defaultStation)
            {
                List<Station> stations = _db.Stations.ToList();
                _db.Stations.AttachRange(stations);
                foreach (Station stationFalse in stations)
                {
                    stationFalse.Default = false;
                }
            }

            // Now make changes on selected station
            Station station = _db.Stations.Where(s => s.StationId == stationId).First();

            _db.Stations.Attach(station);

            station.Name = name;
            station.GpsLatitude = gpsLatitude;
            station.GpsLongitude = gpsLongitude;
            station.Default = defaultStation;

            _db.SaveChanges();

            return RedirectToAction("Settings");
        }

        /* Delete station, only for authorized user */
        [Login]
        [HttpPost]
        public IActionResult DeleteStation(int stationId, bool checkDelete = false)
        {
            // Delete station button was pressed -> Get last measurement for station 
            if (checkDelete) 
            {
                Measurement measurement = null;
                             
                if (_db.Measurements.Where(m => m.StationId == stationId).Any())
                {
                        measurement =_db.Measurements.Where(m => m.StationId == stationId).OrderByDescending(m => m.MeasurementId).First();
                }

                var model = new Tuple<Station, Measurement>(_db.Stations.Where(s => s.StationId == stationId).First(), measurement);

                return View(model);
            }

            // Save for future
            bool deleteDefault = _db.Stations.Where(s => s.StationId == stationId).Select(s => s.Default).First();

            // Select what should be removed and apply changes to database
            _db.Measurements.RemoveRange(_db.Measurements.Where(m => m.StationId == stationId));
            _db.Stations.Remove(_db.Stations.Where(s => s.StationId == stationId).First());
            _db.SaveChanges();

            // User deleted default station, set first in database as default when there is more stations 
            if (_db.Stations.Count() >= 1 && deleteDefault)
            {
                Station station = _db.Stations.First();
                _db.Stations.Attach(station);
                station.Default = true;
                _db.SaveChanges();
            }

            return RedirectToAction("Settings");
        }
/* End of Settings section */


/* Error page */
        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }
    }
}
