using System;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.SqlServer;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using System.Web;
using System.Web.Mvc;

using VRH.Common;
using VRH.Log4Pro.MultiLanguageManager;
using Vrh.Web.Common.Lib;
using Vrh.XmlProcessing;
using Vrh.iScheduler.Report;

namespace Vrh.Web.iScheduler.Report.Areas.iSchedulerReport.Controllers
{ 
    /// <summary>
    /// jquery.datatables plugin kiszolgáló akciói, függvényei.
    /// </summary>
    public class TableController : BaseController
    {
        #region JsonResult GetData action (JsonResult)
        /// <summary>
        /// A jquery.datatables plugin számára az adatok előállítása.
        /// </summary>
        /// <remarks>
        /// A <c>jquery.datatables</c> számára küldendő oszlopoknak legyen mindig
        /// konkrét típusa két okból is:
        /// - gyorsabb, ha konkrét típust kell szerializálnia a <c>Json</c>-nek.
        /// - a Lazy és Proxy sem okoznak semmilyen gondot.
        /// </remarks>
        /// <param name="dataTable">A jquery.datatables által küldött adatok, paraméterek.</param>
        /// <param name="groupId">
        /// A megadott vagy az xml-ben talált csoportazonosító. 
        /// "*" az értéke ha az összesre kíváncsi.
        /// </param>
        /// <param name="connectionString">Adatbázis eléréséhez szükséges kapcsolati sztring.</param>
        /// <returns></returns>
        public JsonResult GetData(DataTablesIn dataTable, string groupId, string connectionString)
        {
            try
            {
                #region DEBUG
#if DEBUG
                string thisfn = $"Areas.{SRConstants.AREA}.{nameof(TableController)}.{nameof(GetData)} action: ";
                Debug.WriteLine($"{thisfn}START: groupId={groupId}, connectionString={connectionString}");
#endif
                #endregion DEBUG
                using (var db = new SchedulerReportDbContext(connectionString))
                {
                    IQueryable<SchedulerReportPackage> all = db.SchedulerReportPackages
                                .Where(x => (groupId == SRConstants.Groups.All || x.GroupId == groupId));

                    #region Szűrés (filter)

                    IQueryable<SchedulerReportPackage> filtered = all;

                    #region Tábla szerinti szűrés

                    if (!string.IsNullOrEmpty(dataTable.Search.Value))
                    {
                        string srch = dataTable.Search.Value;
                        filtered = filtered.Where(x => SqlFunctions.PatIndex(srch, x.Name) > 0 ||
                                                       SqlFunctions.PatIndex(srch, x.GroupId) > 0 ||
                                                       SqlFunctions.PatIndex(srch, x.Description) > 0 ||
                                                       SqlFunctions.PatIndex(srch, x.RoleName) > 0 ||
                                                       SqlFunctions.PatIndex(srch, x.Mark) > 0  
                                                 );
                    }

                    #endregion Tábla szerinti szűrés

                    #region Oszlopok Oszlopok szerinti szűrés
                    foreach (DataTablesIn.DTColumn col in dataTable.Columns.Where(x => x.Searchable && !String.IsNullOrEmpty(x.Search?.Value)))
                    {
                        //IntegerFilter intfltr;  //egész értékek szűréséhez
                        //DateTimeFilter dtfltr;  //dátum értékek szűréséhez
                        bool isNull = (col.Search.Value == "%%");
                        switch (col.Data)
                        {
                            case nameof(PackageCols.Name):
                                filtered = isNull
                                         ? filtered.Where(x => string.IsNullOrEmpty(x.Name))
                                         : filtered.Where(x => SqlFunctions.PatIndex(col.Search.Value, x.Name) > 0);
                                break;
                            case nameof(PackageCols.GroupId):
                                filtered = isNull
                                         ? filtered.Where(x => string.IsNullOrEmpty(x.GroupId))
                                         : filtered.Where(x => SqlFunctions.PatIndex(col.Search.Value, x.GroupId) > 0);
                                break;
                            case nameof(PackageCols.Description):
                                filtered = isNull
                                         ? filtered.Where(x => string.IsNullOrEmpty(x.Description))
                                         : filtered.Where(x => SqlFunctions.PatIndex(col.Search.Value, x.Description) > 0);
                                break;
                            case nameof(PackageCols.RoleName):
                                filtered = isNull
                                         ? filtered.Where(x => string.IsNullOrEmpty(x.RoleName))
                                         : filtered.Where(x => SqlFunctions.PatIndex(col.Search.Value, x.RoleName) > 0);
                                break;
                            case nameof(PackageCols.Active):
                                if (bool.TryParse(col.Search.Value, out bool sv))
                                {
                                    filtered = filtered.Where(x => x.Active == sv);
                                }
                                break;
                            case nameof(PackageCols.Mark):
                                filtered = isNull
                                         ? filtered.Where(x => string.IsNullOrEmpty(x.Mark))
                                         : filtered.Where(x => SqlFunctions.PatIndex(col.Search.Value, x.Mark) > 0);
                                break;
                            default:
                                break;
                        }
                    }
                    #endregion Oszlopok szerinti szűrés

                    #endregion Szűrés (filter)

                    #region Rendezés
                    IOrderedQueryable<SchedulerReportPackage> ordered;
                    if (dataTable.Order != null && dataTable.Order.Any())
                    {
                        DataTablesIn.DTOrder ord = dataTable.Order[0];      // az első oszlop szerinti rendezés
                        bool bDesc = ord.Dir == DataTables.ORDER_DESCENDING;
                        if (dataTable.Columns[ord.Column].Data == nameof(PackageCols.Active))
                        {
                            ordered = bDesc ? filtered.OrderByDescending(o => o.Active) : filtered.OrderBy(o => o.Active);
                        }
                        else
                        {
                            Expression<Func<SchedulerReportPackage, object>> sortExpression = GetSortExpression(dataTable.Columns[ord.Column].Data);
                            ordered = bDesc ? filtered.OrderByDescending(sortExpression) : filtered.OrderBy(sortExpression);
                        }

                        for (int i = 1; i < dataTable.Order.Count(); i++)   // a többi oszlop szerinti rendezés
                        {
                            bDesc = ord.Dir == DataTables.ORDER_DESCENDING;
                            ord = dataTable.Order[i];
                            if (dataTable.Columns[ord.Column].Data == nameof(PackageCols.Active))
                            {
                                ordered = bDesc ? ordered.ThenByDescending(o => o.Active) : ordered.ThenBy(o => o.Active);
                            }
                            else
                            {
                                Expression<Func<SchedulerReportPackage, object>> sortExpression = GetSortExpression(dataTable.Columns[ord.Column].Data);
                                ordered = bDesc ? ordered.ThenByDescending(sortExpression) : ordered.ThenBy(sortExpression);
                            }
                        }
                    }
                    else
                    {   // a Take() miatt mindenképp kell rendezni
                        ordered = filtered.OrderBy(o => o.Id);
                    }
                    #endregion Rendezés

                    #region A kért lap (adatablak) és megjelenítés beállítása
                    // Amit lehet azt csak a kért lapon állítsuk be! Így gyorsabb.
                    var page = (dataTable.Length > 0
                        ? ordered.Skip(dataTable.Start).Take(dataTable.Length)
                        : ordered).Select(s => new PackageCols
                        {
                            Id = s.Id,
                            Name = s.Name,
                            GroupId = s.GroupId,
                            Description = s.Description,
                            RoleName = s.RoleName,
                            Active = s.Active,
                            Mark = s.Mark,
                        }).ToList();
                    #endregion A kért lap (adatablak) és megjelenítés beállítása

                    #region jquery.datatables számára küldendő adatok beállítása
                    // !!! a változók neve lényeges, mert ilyen neveken várja a jquery.daatables
                    int draw = dataTable.Draw;
                    int recordsTotal = all.Count();
                    int recordsFiltered = ordered.Count();
                    object data = page;
                    #endregion jquery.datatables számára küldendő adatok beállítása

                    return Json(new { draw, recordsTotal, recordsFiltered, data }, JsonRequestBehavior.AllowGet);
                }
            }
            catch (Exception ex)
            {
                return Json(new { error = $"\nError occured in GetData action!\n" + String.Join("\n", WebCommon.ErrorListBuilder(ex)) }, JsonRequestBehavior.AllowGet);
            }
        }
        #endregion GetData action (JsonResult)

        #region GetSortExpression private method a rendezéshez a GetData-ban
        
        /// <summary>
        /// Az oszlopnévnek megfelelő rendező kifejezéssel tér vissza.
        /// Itt kell megadni, hogy az oszlop melyik mező rendezettségéhez tartozik.
        /// </summary>
        /// <param name="colName">Az oszlop neve</param>
        /// <returns></returns>
        private Expression<Func<SchedulerReportPackage, object>> GetSortExpression(string colName)
        {
            switch (colName)
            {
                case nameof(PackageCols.Name):
                    return (x => x.Name);
                case nameof(PackageCols.GroupId):
                    return (x => x.GroupId);
                case nameof(PackageCols.Description):
                    return (x => x.Description);
                case nameof(PackageCols.RoleName):
                    return (x => x.RoleName);
                case nameof(PackageCols.Active):
                    return (x => x.Active);
                case nameof(PackageCols.Mark):
                    return (x => x.Mark);
                default:
                    throw new ApplicationException($"There are unknown column name in the ordering! Name={colName}");
            }
        }
        #endregion GetSortExpression private method a rendezéshez a GetData-ban
    }
}