using System;
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.Web.Mvc;

//using VRH.Common;
using VRH.Log4Pro.MultiLanguageManager;
using Vrh.XmlProcessing;
using Vrh.Web.Common.Lib;

using Vrh.iScheduler;
using System.Text;
using Newtonsoft.Json;
using VRH.Common;

namespace Vrh.Web.iScheduler.Areas.iScheduler.Controllers
{
    /// <summary>
    /// Az iScheduler akcióinak elérése e copntrolleren keresztül.
    /// </summary>
    public class iSchedulerController : BaseController
    {
        #region Private constants and classes

        #region MandatoryUrlParams static class
        private class MandatoryUrlParams_Common
        {
            /// <summary>
            /// Ablakazonosító.
            /// </summary>
            public const string BOOTBOXID = SchConst.Params.BOOTBOXID;

            /// <summary>
            /// XmlParser kapcsolati sztringet tartalmazó paraméter neve.
            /// </summary>
            public const string XML = SchConst.Params.XML;
        }

        private class MandatoryUrlParams_ManagerAction: MandatoryUrlParams_Common
        {
            /// <summary>
            /// Megjelenítési mód, értékei "list", vagy "calendar".
            /// </summary>
            public const string DISPLAY = SchConst.Params.DISPLAY;

            /// <summary>
            /// Ütemezés csoport azonosítója
            /// </summary>
            public const string GROUPID = SchConst.Params.GROUPID;
        }

        private class MandatoryUrlParams_EditorAction: MandatoryUrlParams_Common
        {

            /// <summary>
            /// Megjelenítési mód, értékei "List", vagy "Calendar", vagy null
            /// </summary>
            public const string CALLINGVIEWMODE = SchConst.Params.CALLINGVIEWMODE;

            /// <summary>
            /// Ütemezés csoport azonosítója
            /// </summary>
            public const string GROUPID = SchConst.Params.GROUPID;

            /// <summary>
            /// Az ütemezés rekord azonosítója
            /// </summary>
            public const string ID = SchConst.Params.ID;
        }

        private class MandatoryUrlParams_CheckSchedule: MandatoryUrlParams_Common
        {
            /// <summary>
            /// Ütemezett objektum azonosító lista; opcionális paraméter
            /// </summary>
            public const string OBJECTIDLIST = SchConst.Params.OBJECTIDLIST;
        }

        #endregion MandatoryUrlParams static class

        #endregion Private constant and classes

        #region Private variables
        private readonly StateTrans m_States;
        #endregion Private variables

        #region Constructor
        /// <summary>
        /// Az időzítéskezelő kontrollerének alapértelmezett konstruktora.
        /// </summary>
        public iSchedulerController()
        {
            m_States = new StateTrans(base.ForcedLanguageCode);
            ViewBag.ValidationSummaryMessage = "";
        }
        #endregion Constructor

        #region Manager action
        /// <summary>
        /// Az ütemező WEB felület belépési pontja.
        /// </summary>
        public ActionResult Manager()
        {
            try
            {
                #region DEBUG
#if DEBUG
                string thisfn = $"{nameof(iSchedulerController)}.{nameof(Manager)}: ";  // debug prefix
                System.Diagnostics.Debug.WriteLine($"\n{thisfn}START");
#endif
                #endregion DEBUG
                base.ParameterSeparating(typeof(MandatoryUrlParams_ManagerAction));
                var xmlcs = base.RequestedParameters[MandatoryUrlParams_ManagerAction.XML];
                if (string.IsNullOrWhiteSpace(xmlcs)) { xmlcs = $"config={SchConst.DEFAULT_XMLCONFIGNAME};"; }
                var xmlc = new XmlConnection(xmlcs);
                var x = Server.MapPath("");
                var isxp = new SchedulerXmlParser(
                    xmlc,
                    base.BaseFolder,
                    base.ForcedLanguageCode,
                    base.OtherParameters,
                    base.RequestedParameters[MandatoryUrlParams_ManagerAction.GROUPID]);
                if (isxp.ErrorList.Any())
                {
                    SetError(typeof(SchedulerWordCodes.iScheduler.Error.ErrorOccuredWhileXMLParsing), nameof(Manager));
                    return View(SchConst.Views.ERRORLIST, isxp.ErrorList);
                }
                else
                {
                    DisplayMode dsplyMode = isxp.Manager.DefaultDisplayMode;
                    string dmStr = base.RequestedParameters[MandatoryUrlParams_ManagerAction.DISPLAY];
                    if (!string.IsNullOrWhiteSpace(dmStr))
                    {
                        if (!Enum.TryParse(dmStr, out dsplyMode))
                        {
                            throw new ApplicationException(base.Mlmgt(typeof(SchedulerWordCodes.iScheduler.Error.ThisViewIsNotExists), dmStr));
                        }
                    }
                    string viewName;
                    switch (dsplyMode)
                    {
                        case DisplayMode.Calendar:
                            viewName = SchConst.Views.MANAGERCALENDAR;
                            break;
                        default:
                            viewName = SchConst.Views.MANAGER;
                            break;

                    }
                    var ism = new SchedulerModel(isxp, dsplyMode.ToString()) {
                        StateFilterList = m_States.FilterList
                    };
                    return View(viewName, ism);
                }
            }
            catch (Exception ex)
            {
                SetError(typeof(GeneralWordCodes.MLM.General.Messages.ErrorHeadAction), nameof(Manager));
                return View(SchConst.Views.ERRORLIST, WebCommon.ErrorListBuilder(ex));
            }
        }
        #endregion Manager action

        #region Methods for jquery.datatables

        #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="objectType">Az ütemezendő objektum típusa. (xml-ból olvastuk ki)</param>
        /// <param name="groupId">
        /// A megadott vagy az xml-ben talált csoportazonosító. 
        /// Itt már a null érték jelenti, ha az összesre kíváncsi. A modellban lett átfordítva.
        /// </param>
        /// <param name="connectionString">Adatbázis eléréséhez szükséges kapcsolati sztring.</param>
        /// <param name="checkList">Konzisztencia ellenőrzés esetén, csak az ebben a listában szereplő ütemezések jellennek meg.</param>
        /// <param name="isConsistencyCheck">Annak jelzése, hogy jelenleg konzisztencia ellenőrzésben vagyunk.</param>
        /// <returns></returns>
        public JsonResult GetData(DataTablesIn dataTable, string objectType, string groupId, string connectionString, string checkList, bool isConsistencyCheck = false)
        {
            try
            {
#if DEBUG
                Debug.WriteLine($"iScheduler.iSchedulerController.GetData: objectType={objectType}, groupId={groupId}, checkList={checkList}");
#endif
                using (SchedulerDB db = new SchedulerDB(connectionString))
                {
                    IQueryable<Schedule> all;
                    if (isConsistencyCheck)
                    {   //
                        all = db.Schedules
                                .Where(x => (x.ScheduleObject.ObjectType == objectType)
                                        && (groupId == null || x.ScheduleObject.ObjectGroupId == groupId)
                                        && (SqlFunctions.CharIndex(x.ScheduleObject.ObjectId, checkList) > 0)
                                      );
                    }
                    else
                    {
                        all = db.Schedules
                                .Where(x => (x.ScheduleObject.ObjectType == objectType)
                                        && (groupId == null || x.ScheduleObject.ObjectGroupId == groupId)
                                      );
                    }

                    #region Szűrés (filter)

                    IQueryable<Schedule> 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.OperationTime.ToString()) > 0 ||
                                                       SqlFunctions.PatIndex(srch, x.ScheduleObject.ObjectName) > 0 ||
                                                       SqlFunctions.PatIndex(srch, x.ScheduleOperation.OperationName) > 0 ||
                                                       SqlFunctions.PatIndex(srch, x.State.ToString()) > 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(ManagerCols.OperationTime):
                                if (isNull)
                                {
                                    filtered = filtered.Where(x => x.OperationTime == null);
                                }
                                else
                                {
                                    var fr = new DateTimeFilter(col.Search.Value, "yyyy.MM.dd HH:mm:ss");
                                    if (fr.Mode == DateTimeFilterEnum.Before) filtered = filtered.Where(x => x.OperationTime < fr.DateTime_1);
                                    else if (fr.Mode == DateTimeFilterEnum.After) filtered = filtered.Where(x => x.OperationTime > fr.DateTime_1);
                                    else if (fr.Mode == DateTimeFilterEnum.Between) filtered = filtered.Where(x => x.OperationTime > fr.DateTime_1 && x.OperationTime < fr.DateTime_2);
                                    else if (fr.Mode == DateTimeFilterEnum.Equal)
                                    {
                                        filtered = filtered.Where(x => (!fr.Year || x.OperationTime.Year == fr.DateTime_1.Year) &&
                                                                       (!fr.Month || x.OperationTime.Month == fr.DateTime_1.Month) &&
                                                                       (!fr.Day || x.OperationTime.Day == fr.DateTime_1.Day) &&
                                                                       (!fr.Hour || x.OperationTime.Hour == fr.DateTime_1.Hour) &&
                                                                       (!fr.Minute || x.OperationTime.Minute == fr.DateTime_1.Minute) &&
                                                                       (!fr.Second || x.OperationTime.Second == fr.DateTime_1.Second) &&
                                                                       (!fr.Millisecond || x.OperationTime.Millisecond == fr.DateTime_1.Millisecond));
                                    }
                                    else
                                    {
                                        filtered = filtered.Where(x => SqlFunctions.PatIndex(col.Search.Value, x.OperationTime.ToString()) > 0);
                                    }
                                }
                                break;
                            case nameof(ManagerCols.ObjectName):
                                filtered = isNull
                                         ? filtered.Where(x => string.IsNullOrEmpty(x.ScheduleObject.ObjectName))
                                         : filtered.Where(x => SqlFunctions.PatIndex(col.Search.Value, x.ScheduleObject.ObjectName) > 0);
                                break;
                            case nameof(ManagerCols.OperationName):
                                filtered = isNull
                                         ? filtered.Where(x => string.IsNullOrEmpty(x.ScheduleOperation.OperationName))
                                         : filtered.Where(x => SqlFunctions.PatIndex(col.Search.Value, x.ScheduleOperation.OperationName) > 0);
                                break;
                            case nameof(ManagerCols.SeriesTypeAndName):
                                filtered = isNull
                                         ? filtered.Where(x => x.ScheduleSeriesId == null)
                                         : filtered.Where(x => SqlFunctions.PatIndex(col.Search.Value, x.ScheduleSeriesOne.SeriesType) > 0);
                                break;
                            case nameof(ManagerCols.State):
                                if (Enum.TryParse(col.Search.Value, out ScheduleStates ss))
                                {
                                    filtered = filtered.Where(x => x.State == ss);
                                }
                                break;
                            case nameof(ManagerCols.ObjectGroupId):
                                filtered = filtered.Where(x => SqlFunctions.PatIndex(col.Search.Value, x.ScheduleObject.ObjectGroupId) > 0);
                                break;
                            default:
                                break;
                        }
                    }
                    #endregion Oszlopok szerinti szűrés

                    #endregion Szűrés (filter)

                    #region Rendezés
                    IOrderedQueryable<Schedule> 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;
                        switch (dataTable.Columns[ord.Column].Data)
                        {
                            case nameof(ManagerCols.OperationTime):
                                ordered = bDesc ? filtered.OrderByDescending(o => o.OperationTime) : filtered.OrderBy(o => o.OperationTime);
                                break;
                            default:
                                Expression<Func<Schedule, object>> sortExpression = GetSortExpression(dataTable.Columns[ord.Column].Data);
                                ordered = bDesc ? filtered.OrderByDescending(sortExpression) : filtered.OrderBy(sortExpression);
                                break;
                        }

                        for (int i = 1; i < dataTable.Order.Count(); i++)   // a többi oszlop szerinti rendezés
                        {
                            ord = dataTable.Order[i];
                            switch (dataTable.Columns[ord.Column].Data)
                            {
                                case nameof(ManagerCols.OperationTime):
                                    ordered = bDesc ? ordered.ThenByDescending(o => o.OperationTime) : ordered.ThenBy(o => o.OperationTime);
                                    break;
                                default:
                                    Expression<Func<Schedule, object>> sortExpression = GetSortExpression(dataTable.Columns[ord.Column].Data);
                                    ordered = bDesc ? ordered.ThenByDescending(sortExpression) : ordered.ThenBy(sortExpression);
                                    break;
                            }
                        }
                    }
                    else
                    {   // a Take() miatt mindenképp kell rendezni
                        ordered = filtered.OrderBy(o => o.OperationTime);
                    }
					#endregion Rendezés

					#region A kért lap (adatablak) és megjelenítés beállítása
					var page = (dataTable.Length > 0
						? ordered.Skip(dataTable.Start).Take(dataTable.Length)
						: ordered).Select(s => new ManagerCols
						{
							Id = s.Id,
							OperationTime = s.OperationTime,
							ObjectName = s.ScheduleObject.ObjectName,
							OperationName = s.ScheduleOperation.OperationName,
							SeriesTypeAndName = s.ScheduleSeriesOne == null ? "" : s.ScheduleSeriesOne.SeriesType + "-" + s.ScheduleSeriesOne.SeriesName,
							State = s.State == ScheduleStates.Active ? m_States.Active : (s.State == ScheduleStates.Success ? m_States.Success : m_States.Failed),
							ObjectGroupId = s.ScheduleObject.ObjectGroupId,
#if DEBUG
							ReturnMessage = (s.ReturnMessage == null || s.ReturnMessage.Trim() == "") ? "" : "X"
#else
							ReturnMessage = (s.State != ScheduleStates.Failed || s.ReturnMessage == null || s.ReturnMessage.Trim() == "") ? "" : "X"
#endif
						}).ToList();
                    // Amit lehet azt csak a kért lapon állítsuk be! Így gyorsabb.
                    #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<Schedule, object>> GetSortExpression(string colName)
        {
            switch (colName)
            {
                case nameof(ManagerCols.ObjectName):
                    return (x => x.ScheduleObject.ObjectName);
                case nameof(ManagerCols.OperationName):
                    return (x => x.ScheduleOperation.OperationName);
                case nameof(ManagerCols.SeriesTypeAndName):
                    return (x => x.ScheduleSeriesOne.SeriesType);
                case nameof(ManagerCols.State):
                    return (x => x.State);
                case nameof(ManagerCols.ObjectGroupId):
                    return (x => x.ScheduleObject.ObjectGroupId);
                default:
                    throw new ApplicationException($"There are unknown column name in the ordering! Name={colName}");
            }
        }
        #endregion GetSortExpression private method a rendezéshez a GetData-ban

        #endregion Methods for jquery.datatables

        #region ShowStateMessage action
        /// <summary>
        /// A megadott ütemezés végrehajtása során keletkezett üzenet
        /// megkeresése és visszaadása.
        /// </summary>
        /// <param name="connectionString">Szabányos SQL kapcsolati sztring. Nem megnevezés.</param>
        /// <param name="id">Az ütemezés egyedi belső azonosítója.</param>
        /// <param name="bootboxid">Ha ablakba kérik az eredményt, annak azonosítója.</param>
        /// <returns></returns>
        [HttpGet]
        public JsonResult ShowStateMessage(string connectionString, int id, string bootboxid)
        {
            try
            {
                #region DEBUG
#if DEBUG
                string thisfn = "iScheduler.ShowStateMessage: ";  // debug prefix
                System.Diagnostics.Debug.WriteLine($"\n{thisfn}START connectionString={connectionString}, id={id}");
#endif
                #endregion DEBUG

                using (var db = new SchedulerDB(connectionString))
                {
                    Schedule sch = db.Schedules.FirstOrDefault(x => x.Id == id);
                    if (sch == null) ThrEx(typeof(SchedulerWordCodes.iScheduler.Error.NotFoundSchedule), id);
                    return Json(new ReturnInfoJSON
                    {
                        ReturnValue = sch.ReturnValue ?? 0,
                        ReturnMessage = sch.ReturnMessage.Trim().Replace("'", "").Replace(@"\", @"\\")
                    }, JsonRequestBehavior.AllowGet);
                }
            }
            catch (Exception ex)
            {
                return Json(new ReturnInfoJSON
                {
                    ReturnValue = -1,
                    ReturnMessage = string.Join(WebCommon.HTML_NEWLINE, WebCommon.ErrorListBuilder(ex))
                }, JsonRequestBehavior.AllowGet);
            }
        }
        #endregion ShowStateMessage action

        #region ScheduleExecute action

        //http://192.168.64.122/Log4ProIS-REPORTING/iScheduler/iScheduler/ScheduleExecute?xml=config%3dALMiSchedulerReport%3b&scheduleid=1
        [HttpGet]
        //[ValidateAntiForgeryToken]
        public ActionResult ScheduleExecuteGet(string xml, int scheduleid, bool formatreturnmessage = false) { return _ScheduleExecute(xml, scheduleid, formatreturnmessage); }
        [HttpPost]
        public ActionResult ScheduleExecute(string xml, int scheduleid, bool formatreturnmessage = false) { return _ScheduleExecute(xml, scheduleid, formatreturnmessage); }
        private ActionResult _ScheduleExecute(string xml, int scheduleid, bool formatreturnmessage = false)
        {
            string thisfn = "iScheduler.ScheduleExecute POST: ";
            System.Diagnostics.Debug.WriteLine($"{thisfn} PING");
            base.ParameterSeparating(typeof(MandatoryUrlParams_EditorAction));

            ReturnInfoJSON rijson = new ReturnInfoJSON();
            string message = "";
            try
            {
                using (var se = new ScheduleExecute(xml, base.BaseFolder, base.ForcedLanguageCode)) { se.Run(scheduleid, true); }
                rijson.ReturnValue = 0;
            }
            catch (Exception ex)
            {
                rijson.ReturnValue = -1;
                message = BuildResultMessage(String.Join(WebCommon.HTML_NEWLINE, WebCommon.ErrorListBuilder(ex)));
                //dialogSize = "xl";
            }
            SchedulerModel ism = SchedulerModel.LoadModel(LoadSQLConnectionString(xml), scheduleid);
            if (ism == null) { base.ThrEx(typeof(SchedulerWordCodes.iScheduler.Error.NotFoundSchedule), scheduleid); }

            string header = base.Mlmgt(typeof(SchedulerWordCodes.iScheduler.Execute.ActionIsCompleted));
            string headerformatted = $"{header}<br/><table>";
            string firstrowtemplate = $"<tr><th>{base.Mlmgt(typeof(SchedulerWordCodes.iScheduler.Execute.ColName))}</th>" +
                $"<th>{base.Mlmgt(typeof(SchedulerWordCodes.iScheduler.Execute.ColVal))}</th></tr>";
            string onerowtemplate = "<tr><td>{0}</td><td>{1}</td></tr>";
            string footer = "</table>";
            rijson.ReturnMessage = String.Join(
                    headerformatted, firstrowtemplate,
                    string.Format(onerowtemplate, "xml", xml),
                    string.Format(onerowtemplate, "GroupId", ism.ObjectGroupId),
                    string.Format(onerowtemplate, "ObjectId", $"{ism.ObjectIdText}(id={ism.ObjectIdValue})"),
                    string.Format(onerowtemplate, "TimeStamp", ism.OperationTime),
                    string.Format(onerowtemplate, "Message", ism.IsExistReturnMessage? ism.ReturnMessage: "-"),
                    (string.IsNullOrWhiteSpace(message)?"":string.Format(onerowtemplate, "E X C E P T I O N", message)),
                    footer
                    );
            if (!formatreturnmessage)
            {
                rijson.ReturnMessage = rijson.ReturnMessage
                .Replace("<br/>", ";").Replace("<br />", ";").Replace("<table>", "").Replace("</table>", "").Replace("<tr>", "")
                .Replace("</td><td>", "=").Replace("</th><th>", "=")
                .Replace("</td></tr>", ",").Replace("</th></tr>", ",")
                .Replace("<td>", "").Replace("<th>", "").Replace("<tr>", "");
            }
            return Json(rijson, JsonRequestBehavior.AllowGet);
        }
        private string LoadSQLConnectionString(string xmlcs=null)
        {
            if (string.IsNullOrWhiteSpace(xmlcs))
            {
                base.ParameterSeparating(typeof(MandatoryUrlParams_Common));
                xmlcs = base.RequestedParameters[MandatoryUrlParams_Common.XML];
                if (string.IsNullOrWhiteSpace(xmlcs)) { xmlcs = $"config={SchConst.DEFAULT_XMLCONFIGNAME};"; }
            }
            var xmlc = new XmlConnection(xmlcs);
            using (var isxp = new SchedulerXmlParser(
                xmlc, HttpContext.Server.MapPath(WebCommon.SIGN_RELATIVE),
                base.ForcedLanguageCode, base.OtherParameters))
            {
                var ism = new SchedulerModel(isxp)
                {   //ObjectType és ObjectGroupId a példányosításkor értéket kap. És az Id=0.
                    BootboxId = base.RequestedParameters[MandatoryUrlParams_EditorAction.BOOTBOXID],
                    StateFilterList = m_States.FilterList
                };
                return ism.ConnectionString;
            }
        }
        #endregion ScheduleExecute action

        #region Editor action 

        #region Editor GET
        /// <summary>
        /// iScheduler ütemezés szerkesztő felülete. GET.
        /// </summary>
        /// <returns>PartialView</returns>
        /// <remarks>
        /// "xml" paraméter: Fájl, amely a szerkesztő számára tartalmazza a paramétereket.
        /// "GroupId" paraméter>Csoport azonosító, amely felülírja az xml fájlban talált csoportazonosítót. 
        /// Módosításnál nem lehet *, új rekordnál a * jelentése az, hogy meg kell adnia egy csoportazonosítót a felületen.
        /// 
        /// "id" paraméter: Ha üres vagy 0, akkor új rekord felvitele következik. Egyébként módosítás.
        /// "bootboxid" paraméter: Ha ablakban nyitották meg az Editor-t, akkor a dialogus ablak HTML elemben (id-ban) szereplő érték.
        /// A bootboxActionHide paramétere lehet.
        /// 
        /// "callingViewMode" paraméter: Milyen módban hívták meg az Editor-t, illetve honnan: lista nézetből, naptár nézetből, vagy egyéb.
        /// </remarks>
        [HttpGet]
        public ActionResult Editor()
        {
            bool isDialog = false;
            try
            {
                base.ParameterSeparating(typeof(MandatoryUrlParams_EditorAction));
                isDialog = !string.IsNullOrWhiteSpace(base.RequestedParameters[MandatoryUrlParams_EditorAction.BOOTBOXID]);
                if (!isDialog)
                {
                    base.ThrEx(typeof(SchedulerWordCodes.iScheduler.Error.BootboxIdEmpty));
                }
                var xmlcs = base.RequestedParameters[MandatoryUrlParams_EditorAction.XML];
                if (string.IsNullOrWhiteSpace(xmlcs)) { xmlcs = $"config={SchConst.DEFAULT_XMLCONFIGNAME};"; }
                var xmlc = new XmlConnection(xmlcs);
                using (var isxp = new SchedulerXmlParser(
                    xmlc, HttpContext.Server.MapPath(WebCommon.SIGN_RELATIVE),
                    base.ForcedLanguageCode, base.OtherParameters,
                    base.RequestedParameters[MandatoryUrlParams_EditorAction.GROUPID]))
                {
                    if (isxp.ErrorList.Any())
                    {
                        SetError(typeof(GeneralWordCodes.MLM.General.Messages.ErrorHeadXML));
                        return PartialView(SchConst.Views.ERRORLIST, isxp.ErrorList);
                    }

                    string callingViewMode = base.RequestedParameters[MandatoryUrlParams_EditorAction.CALLINGVIEWMODE];
                    var ism = new SchedulerModel(isxp, callingViewMode)
                    {   //ObjectType és ObjectGroupId a példányosításkor értéket kap. És az Id=0.
                        BootboxId = base.RequestedParameters[MandatoryUrlParams_EditorAction.BOOTBOXID],
                        StateFilterList = m_States.FilterList
                    };

                    string id = base.RequestedParameters[MandatoryUrlParams_EditorAction.ID];
                    if (!string.IsNullOrWhiteSpace(id))
                    {
                        if (!int.TryParse(id, out int parseid))
                        {
                            base.ThrEx(typeof(SchedulerWordCodes.iScheduler.Error.TypeOfIdIsWrong), id);
                        }
                        ism.Id = parseid;
                    }

                    if (ism.Id == 0)    //felvitel
                    {
                        ism.OperationTime = DateTime.Now.AddHours(1);
                        ism.ScheduleType = SchConst.ScheduleTypes.Single;
                        ism.State = ScheduleStates.Active;
                    }
                    else { ism = ism.LoadModel(); }//módosítás
                    ism.StateTranslate = m_States.GetName(ism.State);
                    ism.PreviousScheduleType = ism.ScheduleType;
                    return PartialView(SchConst.Views.EDITOR, ism);
                } // using(var isxp = new SchedulerXmlParser()) END
            }
            catch (Exception ex)
            {
                SetError(typeof(GeneralWordCodes.MLM.General.Messages.ErrorHeadAction),nameof(Editor));
                if (isDialog)
                {
                    return PartialView(SchConst.Views.ERRORLIST, WebCommon.ErrorListBuilder(ex));
                }
                else
                {
                    return View(SchConst.Views.ERRORLIST, WebCommon.ErrorListBuilder(ex));

                }
            }
        }
        #endregion Editor GET

        #region Editor POST
        /// <summary>
        /// iScheduler ütemezés szerkesztő felület. POST.
        /// </summary>
        /// <param name="schedulerModel">iSchedulerModel típus</param>
        /// <returns>
        /// Ha nincs hiba, akkor egy javascript, melynek be kell zárnia a bootbox által létrehozott ablakot.
        /// Egyébként visszatér az Editor ablakhoz.
        /// </returns>
        /// <remarks>
        /// bootboxAction ablak bezárása: vrhct.bootbox.hide(bootboxid);
        /// </remarks>
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Editor(SchedulerModel schedulerModel)
        {
            string thisfn = "iScheduler.Editor POST: ";
            System.Diagnostics.Debug.WriteLine($"{thisfn} PING");
            if (ModelState.IsValid)
            {
                try
                {
                    //if (!model.IsScheduleExecute) throw new ApplicationException("Próba exception!");
                    if (schedulerModel.ScheduleType != schedulerModel.PreviousScheduleType)
                    {   //akkor átváltott sorozatra vagy vissza
                        //schedulerModel.PreviousScheduleType = schedulerModel.ScheduleType;
                        return PartialView(SchConst.Views.EDITOR, schedulerModel);
                    }
                    //minél hamarabb megjegyezzük, hogy korábban-van-e a beállított dátum, mint a pontos idő!
                    //ha korábban, akkor egyből passzív
                    ScheduleStates sstate = schedulerModel.OperationTime < DateTime.Now ? ScheduleStates.Success : ScheduleStates.Active;

                    int scheduleId = schedulerModel.Id;
                    using (SchedulerDB db = new SchedulerDB(schedulerModel.ConnectionString))
                    {
                        #region Adatok mentése
                        Schedule row;
                        if (scheduleId == 0) row = new Schedule();
                        else
                        {
                            row = db.Schedules.Find(scheduleId);
                            if (row == null)
                            {
                                base.ThrEx(typeof(SchedulerWordCodes.iScheduler.Error.NotFoundSchedule), scheduleId);
                            }

                            if (schedulerModel.SeriesId == null && row.ScheduleSeriesId != null)
                            {   //Ez itt tkp. nem hiba, de így közöljük, hogy kikerült a sorozatból!
                                row.ScheduleSeriesId = schedulerModel.SeriesId;
                                db.SaveChanges();
                                AddModelError(typeof(SchedulerWordCodes.iScheduler.Editor.ScheduleIsNotAMemberOfASeries));
                                TempData[MODELSTATE] = ModelState;
                                return PartialView(SchConst.Views.EDITOR, schedulerModel);
                            }
                        }

                        if (row.ScheduleSeriesId == null)
                        {   //jelenleg nincs sorozatban
                            if (sstate == ScheduleStates.Success)
                            {
                                base.ThrEx(typeof(SchedulerWordCodes.iScheduler.Error.TimeOfTheScheduledTaskCannotBeErlier));
                            }

                            ScheduleObject sob = db.ScheduleObjects
                                                  .FirstOrDefault(x => x.ObjectType == schedulerModel.ObjectType && x.ObjectId == schedulerModel.ObjectIdValue);
                            if (sob == null) sob = new ScheduleObject();
                            sob.ObjectType = schedulerModel.ObjectType;
                            sob.ObjectId = schedulerModel.ObjectIdValue;
                            sob.ObjectName = schedulerModel.ObjectIdText;
                            sob.ObjectGroupId = schedulerModel.ObjectGroupId;

                            ScheduleOperation sop = db.ScheduleOperations
                                                      .FirstOrDefault(x => x.ObjectType == schedulerModel.ObjectType && x.OperationId == schedulerModel.OperationIdValue);
                            if (sop == null) sop = new ScheduleOperation();
                            sop.ObjectType = schedulerModel.ObjectType;
                            sop.OperationId = schedulerModel.OperationIdValue;
                            sop.OperationName = schedulerModel.OperationIdText;

                            row.ScheduleObject = sob;
                            row.ScheduleOperation = sop;
                            row.OperationTime = schedulerModel.OperationTime;
                            row.State = sstate;
                            if (schedulerModel.ScheduleType == SchConst.ScheduleTypes.Single)
                            {
                                row.ScheduleSeriesId = null;
                                if (scheduleId == 0) db.Schedules.Add(row);
                            }
                            else
                            {
                                var sso = new ScheduleSeriesOne
                                {
                                    SeriesType = schedulerModel.ScheduleType,
                                    SeriesName = schedulerModel.SeriesName,
                                    SeriesColor = schedulerModel.SeriesColor
                                };
                                db.ScheduleSeries.Add(sso);

                                row.ScheduleSeriesOne = sso;
                                if (scheduleId == 0) db.Schedules.Add(row);

                                #region Weekly series    
                                if (schedulerModel.ScheduleType == SchConst.ScheduleTypes.Weekly)
                                {
                                    DateTime opdate = schedulerModel.OperationTime.AddDays(1);
                                    DateTime endDate = schedulerModel.OperationTime.AddDays(schedulerModel.NumberOf * 7).AddDays(-1);
                                    while (opdate <= endDate)
                                    {
                                        if (schedulerModel.SelectedWeekDays.Contains((int)opdate.DayOfWeek))
                                        {   //ha benne van a kiválasztott napokban
                                            Schedule sch = new Schedule()
                                            {
                                                ScheduleObject = sob,
                                                ScheduleOperation = sop,
                                                OperationTime = opdate,
                                                State = sstate,
                                                ScheduleSeriesOne = sso
                                            };
                                            db.Schedules.Add(sch);
                                        }
                                        opdate = opdate.AddDays(1);
                                    }
                                }
                                #endregion Weekly series    
                            }
                            db.SaveChanges();
                        }
                        else
                        {
                            row.ScheduleSeriesOne.SeriesName = schedulerModel.SeriesName;
                            row.ScheduleSeriesOne.SeriesColor = schedulerModel.SeriesColor;
                            db.SaveChanges();
                        }
                        scheduleId = row.Id;
                        System.Diagnostics.Debug.WriteLine($"{thisfn} Sikeres mentés.");
                        #endregion Adatok mentése
                    }

                    string returnScript = "";

                    #region Execute test
                    if (schedulerModel.IsScheduleExecute)
                    {   //ha itt baj van, azt már csak kiüzenjük
                        System.Diagnostics.Debug.WriteLine($"{thisfn} Execute test START {DateTime.Now:hh:mm:ss.fff}");

                        string title = base.Mlmgt(typeof(SchedulerWordCodes.iScheduler.Editor.ResultOfTheOperation));
                        string message = "";
                        try
                        {
                            using (var se = new ScheduleExecute(schedulerModel.XmlParserConnectionString, base.BaseFolder, base.ForcedLanguageCode))
                            {
                                se.Run(scheduleId, true);
                            }
                        }
                        catch (Exception ex)
                        {
                            message = BuildResultMessage(String.Join(WebCommon.HTML_NEWLINE, WebCommon.ErrorListBuilder(ex)));
                            //dialogSize = "xl";
                        }
                        returnScript += string.Format("vrhschedit.ShowStateMessage({0});", scheduleId);

                        System.Diagnostics.Debug.WriteLine($"{thisfn} Execute test END {DateTime.Now:hh:mm:ss.fff}");
                    }
                    #endregion Execute test

                    returnScript += $"vrhct.bootbox.hide('{schedulerModel.BootboxId}');";

                    if (!string.IsNullOrWhiteSpace(schedulerModel.CallingViewMode))
                    {
                        if (Enum.TryParse(schedulerModel.CallingViewMode, true, out DisplayMode dm))
                        {
                            switch (dm)
                            {
                                case DisplayMode.List:
                                    returnScript += "vrhsms.ScheduleTable.Table.draw('page');";
                                    break;
                                case DisplayMode.Calendar:
                                    returnScript += "$calendar.fullCalendar('refetchEvents');";
                                    break;
                                default:
                                    break;
                            }
                        }
                    }

                    //System.Diagnostics.Debug.WriteLine($"{thisfn}script={script}");
                    return JavaScript(returnScript);
                }
                catch (Exception ex)
                {
                    AddModelError(ex);
                }
            }
            TempData[MODELSTATE] = ModelState;
            return PartialView(SchConst.Views.EDITOR, schedulerModel);
        }
        #endregion Editor POST

        #endregion Editor

        #region Delete action
        /// <summary>
        /// Egy időzítés vagy sorozat időzítés törlése.
        /// </summary>
        /// <param name="id">Az időzítés azonosítója.</param>
        /// <param name="connectionString">
        /// SQL szerver adatbázisához kapcsolati sztring. Nem megnevezés, hanem
        /// szabványos és érvényes SQL sztring.</param>
        /// <param name="isDeleteSeries">
        /// Ha az időzítés egy sorozat része, akkor az <paramref name="isDeleteSeries"/>
        /// igaz értéke esetén a sorozat is törlődik.
        /// </param>
        /// <returns></returns>
        [HttpPost]
        public JsonResult Delete(int id, string connectionString, bool isDeleteSeries)
        {
            try
            {
                using (var db = new SchedulerDB(connectionString))
                {
                    Schedule row = db.Schedules.Find(id);
                    if (row == null) ThrEx(typeof(SchedulerWordCodes.iScheduler.Error.NotFoundSchedule), id);

                    if (isDeleteSeries)
                    {   //a teljes sorozat törlése
                        if (row.ScheduleSeriesId == null) ThrEx(typeof(SchedulerWordCodes.iScheduler.Error.ScheduleIsNotAMemberOfTheSeries));

                        ScheduleSeriesOne schser = row.ScheduleSeriesOne;

                        foreach (Schedule sch in db.Schedules.Where(x => x.ScheduleSeriesId == schser.Id))
                        {
                            db.Schedules.Remove(sch);
                        }
                        db.ScheduleSeries.Remove(schser);
                    }
                    else
                    {
                        db.Schedules.Remove(row);
                    }
                    db.SaveChanges();
                }
                return Json(new ReturnInfoJSON(), JsonRequestBehavior.AllowGet);
            }
            catch (Exception ex)
            {
                return Json(new ReturnInfoJSON
                {
                    ReturnValue = -88,
                    ReturnMessage = string.Join(WebCommon.HTML_NEWLINE, WebCommon.ErrorListBuilder(ex))
                }, JsonRequestBehavior.AllowGet);
            }
        }
        #endregion

        #region UpdateData (egyelőre nincs megvalósítva a Manager.cshtml-ben)
        //public string UpdateData(int id, string value, int? rowId, int? columnPosition, int? columnId, string columnName, string connectionString)
        //{
        //    try
        //    {
        //        using (iSchedulerDB db = new iSchedulerDB())
        //        {
        //            Schedule row = db.Schedules.Find(id);
        //            if (row == null)
        //            {
        //                string frm = MultiLanguageManager.GetTranslation(typeof(WordCodes.iScheduler.Error.NotFoundSchedule));
        //                throw new ApplicationException(String.Format(frm, id));
        //            }

        //            switch (columnName)
        //            {
        //                case "Object":
        //                    CheckRequired(value, MultiLanguageManager.GetTranslation(typeof(WordCodes.iScheduler.Manager.Label.ObjectId)));
        //                    row.ScheduleObject.ObjectName = value;
        //                    db.SaveChanges();
        //                    break;

        //                case "Operation":
        //                    CheckRequired(value, MultiLanguageManager.GetTranslation(typeof(WordCodes.iScheduler.Manager.Label.OperationId)));
        //                    row.ScheduleOperation.OperationName = value;
        //                    db.SaveChanges();
        //                    break;

        //                case "OperationTime":
        //                    CheckRequired(value, MultiLanguageManager.GetTranslation(typeof(WordCodes.iScheduler.Manager.Label.OperationTime)));
        //                    row.OperationTime = DateTime.Today;
        //                    db.SaveChanges();
        //                    break;

        //                default:
        //                    break;
        //            }

        //        }
        //        return value;
        //    }
        //    catch (System.Data.Entity.Validation.DbEntityValidationException evex)
        //    {
        //        string msg = ErrorMessageBuilder(evex);
        //        return msg;
        //    }
        //    catch (Exception ex)
        //    {
        //        string msg = ErrorMessageBuilder(ex);
        //        return msg;
        //    }
        //}
        #endregion UpdateData

        #region CreateSystemViews
        [HttpPost]
        public JsonResult CreateSystemViews()
        {
            try
            {
                SchedulerDB.SelectLists.CreateSystemViews(LoadSQLConnectionString());
                return Json(new VRH.Common.ReturnInfoJSON
                {
                    ReturnValue = ModelState.IsValid ? 0 : 1,
                    ReturnMessage = "Scripts are created!",
                }, JsonRequestBehavior.AllowGet);
            }
            catch (Exception ex)
            {
                return Json(new VRH.Common.ReturnInfoJSON
                {
                    ReturnValue = 1,
                    ReturnMessage = string.Join("\n", Vrh.Web.Common.Lib.WebCommon.ErrorListBuilder(ex)),
                }, JsonRequestBehavior.AllowGet);
            }
        }
        [HttpGet]
        public JsonResult CreateSystemViewsGet() { return CreateSystemViews(); }
        #endregion CreateSystemViews

        #region CheckSchedule action
        /// <summary>
        /// Ütemezések létezésének ellenőrzése.
        /// </summary>
        /// <returns></returns>
        public JsonResult CheckSchedule()
        {
            List<CheckListJSON> resultlist = new List<CheckListJSON>();
            try
            {
                System.Diagnostics.Debug.WriteLine("Vrh.iScheduler.iSchedulerController.CheckSchedule: START");
                base.ParameterSeparating(typeof(MandatoryUrlParams_CheckSchedule));
                var xmlcs = base.RequestedParameters[MandatoryUrlParams_CheckSchedule.XML];
                if (string.IsNullOrWhiteSpace(xmlcs)) { xmlcs = $"config={SchConst.DEFAULT_XMLCONFIGNAME};"; }
                var xmlc = new XmlConnection(xmlcs);
                using (var isxp = new SchedulerXmlParser(
                    xmlc, HttpContext.Server.MapPath(WebCommon.SIGN_RELATIVE),
                    base.ForcedLanguageCode, base.OtherParameters))
                {
                    if (isxp.ErrorList.Any())
                    {
                        for (int ix = 0; ix < isxp.ErrorList.Count; ix++)
                        {
                            resultlist.Add(new CheckListJSON() { Value = "Error" + ix.ToString("00"), Text = isxp.ErrorList[ix] });
                        }
                    }
                    else
                    {
                        using (SchedulerDB db = new SchedulerDB(isxp.ConnectionString))
                        {
                            string objectidlist = base.RequestedParameters[MandatoryUrlParams_CheckSchedule.OBJECTIDLIST];
                            if (String.IsNullOrEmpty(objectidlist))
                            {   //ha üres, akkor az összes aktív ütemezéssel kell visszatérnie
                                var distinctlst = db.Schedules
                                                    .Where(x => x.State == ScheduleStates.Active)
                                                    .GroupBy(g => new { Id = g.ScheduleObject.ObjectId, Name = g.ScheduleObject.ObjectName })
                                                    .Select(g => new
                                                    {
                                                        g.Key.Id,
                                                        g.Key.Name
                                                    });
                                foreach (var item in distinctlst)
                                {
                                    resultlist.Add(new CheckListJSON() { Value = item.Id, Text = item.Name, Checked = true });
                                }
                            }
                            else
                            {
                                string[] olist = objectidlist.Split(',');
                                foreach (string objectid in olist)
                                {
                                    ScheduleObject so = db.ScheduleObjects.FirstOrDefault(x => x.ObjectId == objectid);
                                    if (so == null)
                                    {
                                        resultlist.Add(new CheckListJSON() { Value = objectid, Text = "", Checked = false });
                                    }
                                    else
                                    {
                                        bool isexists = db.Schedules.Any(x => x.State == ScheduleStates.Active && x.ScheduleObjectId == so.Id);
                                        resultlist.Add(new CheckListJSON() { Value = objectid, Text = so.ObjectName, Checked = isexists });
                                    }
                                }
                            }
                        }//using(iSchedulerDB) END
                    }
                }//using (var isxp = new SchedulerXmlParser()) END
            }
            catch (Exception ex)
            {
                int ix = 0;
                foreach (string mess in WebCommon.ErrorListBuilder(ex))
                {
                    resultlist.Add(new CheckListJSON() { Value = "Error" + ix.ToString("00"), Text = mess });
                    ix++;
                }
            }
            return Json(resultlist, JsonRequestBehavior.AllowGet);
        }
        #endregion CheckSchedule action

        #region ConsistencyCheck action
        private class CheckGetResponse
        {
            public string ErrorMessage { get; set; }
            public string ObjectIdList { get; set; }
            public CheckGetResponse()
            {
                ErrorMessage = "";
                ObjectIdList = "";
            }
        }
		/// <summary>
		/// Megnézi, hogy a megadott objectType és groupId adatokkal rendelkező objektumokra vannak-e ütemezések,
		/// amelyekre nincsenek, azokat törli, amelyekre vannak, azoknak a listájával visszatér.
		/// </summary>
		/// <param name="objectType"></param>
		/// <param name="groupId"></param>
		/// <param name="connectionString"></param>
		/// <returns></returns>
		[HttpGet]
        public JsonResult ConsistencyCheck(string objectType, string groupId, string connectionString)
        {
#if DEBUG
            System.Diagnostics.Debug.WriteLine("Vrh.iShceduler.iSchedulerController.ConsistencyCheck.GET: objectType={0}, groupId={1}, connectionString={2}"
                                             , objectType, groupId, connectionString);
#endif
            var cgr = new CheckGetResponse();
            try
            {
                using (var db = new SchedulerDB(connectionString))
                {
                    if (!db.Schedules.Any())
                    {
                        string mess = MultiLanguageManager.GetTranslation(typeof(SchedulerWordCodes.iScheduler.Error.ThereAreNoSchedules));
                        throw new ApplicationException(mess);
                    }

                    var objectsToRemove = new List<ScheduleObject>();
                    string prefix = "";
                    IQueryable<ScheduleObject> lst = db.ScheduleObjects
                                                       .Where(x => x.ObjectType == objectType && (x.ObjectGroupId == groupId || groupId == SchConst.Groups.All));
                    foreach (ScheduleObject schobj in lst)
                    {
                        if (db.Schedules.Any(x => x.ScheduleObjectId == schobj.Id))
                        {
                            cgr.ObjectIdList += prefix + schobj.ObjectId;
                            prefix = ",";
                        }
                        else { objectsToRemove.Add(schobj); }
                    }

                    if (objectsToRemove.Any())
                    {
                        foreach (ScheduleObject schobj in objectsToRemove) { db.ScheduleObjects.Remove(schobj); }
                        db.SaveChanges();
                    }
                }
            }
            catch (Exception ex) { cgr.ErrorMessage = String.Join(WebCommon.HTML_NEWLINE, WebCommon.ErrorListBuilder(ex)); }
            return Json(cgr, JsonRequestBehavior.AllowGet);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="objectType"></param>
        /// <param name="groupId"></param>
        /// <param name="connectionString"></param>
        /// <param name="checkList"></param>
        /// <returns></returns>
        [HttpPost]
        public JsonResult ConsistencyCheck(string objectType, string groupId, string connectionString, List<CheckListJSON> checkList)
        {
#if DEBUG
            System.Diagnostics.Debug.WriteLine("Vrh.iShceduler.iSchedulerController.ConsistencyCheck.POST: objectType={0}, groupId={1}, connectionString={2}"
                                             , objectType, groupId, connectionString);
            foreach (CheckListJSON item in checkList)
            {
                System.Diagnostics.Debug.WriteLine("Vrh.iShceduler.iSchedulerController.ConsistencyCheck.POST: item={0}", item.ToString());
            }
#endif
            string ErrorMessage = "";
            try
            {
                using (SchedulerDB db = new SchedulerDB(connectionString))
                {
                    if (checkList != null && checkList.Count > 0)
                    {
                        foreach (CheckListJSON chk in checkList.Where(x => x.Checked == false))
                        {
                            ScheduleObject schobj = db.ScheduleObjects.FirstOrDefault(x => x.ObjectType == objectType && x.ObjectId == chk.Value);
                            if (schobj != null)
                            {
                            }
                        }
                        db.SaveChanges();
                    }
                }
            }
            catch (Exception ex)
            {
                ErrorMessage = String.Join(WebCommon.HTML_NEWLINE, WebCommon.ErrorListBuilder(ex));
            }
            return Json(ErrorMessage, JsonRequestBehavior.AllowGet);
        }
		#endregion ConsistencyCheck action

		#region GetCalendarEvents action

		private class CalendarResponse
        {
            public string ErrorMessage { get; set; } = "";
            public List<FullCalendarEvent> Events { get; set; } = new List<FullCalendarEvent>();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="objectType"></param>
        /// <param name="groupId"></param>
        /// <param name="connectionString"></param>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        [HttpPost]
        public JsonResult GetCalendarEvents(string objectType, string groupId, string connectionString, string start, string end)
        {
            #region DEBUG
#if DEBUG
            string dpx = "iScheduler.GetCalendarEvents: ";  // debug prefix
            System.Diagnostics.Debug.WriteLine($"\n{dpx}START objectType={objectType}, groupId={groupId}, connectionString={connectionString}, start={start}, end={end}");
#endif
            #endregion DEBUG
            var calres = new CalendarResponse();
            try
            {
                using (var db = new SchedulerDB(connectionString))
                {
                    DateTime startdt = UnixToDateTime(start);
                    DateTime enddt = UnixToDateTime(end);

                    IQueryable<Schedule> schs = db.Schedules
                                                  .Where(x => x.State == ScheduleStates.Active
                                                           && x.OperationTime >= startdt
                                                           && x.OperationTime < enddt
                                                           && x.ScheduleObject.ObjectType == objectType
                                                           && (groupId == "*" || x.ScheduleObject.ObjectGroupId == groupId)
                                                        );

                    #region editorUrl bázis összeállítása
                    UrlHelper uh = new UrlHelper(this.ControllerContext.RequestContext);
                    string editorUrl = uh.Action(nameof(iSchedulerController.Editor), SchConst.CTRL, new
                    {
                        area = SchConst.AREA,
                        callingViewMode = DisplayMode.Calendar.ToString(),
                    });
                    #endregion editorUrl bázis összeállítása

                    foreach (Schedule sch in schs)
                    {
                        FullCalendarEvent fce = new FullCalendarEvent()
                        {
                            id = sch.Id,
                            title = sch.ScheduleObject.ObjectName,
                            start = sch.OperationTime.ToString("yyyy-MM-ddTHH:mm"),
                            color = sch.ScheduleSeriesOne == null ? "black" : sch.ScheduleSeriesOne.SeriesColor,
                            url = $"{editorUrl}&id={sch.Id}&GroupId={sch.ScheduleObject.ObjectGroupId}",
                        };
                        Color c = Color.FromName(fce.color);
                        fce.textColor = (((c.R + c.B + c.G) / 3) > 128) ? Color.Black.ToString() : Color.White.ToString();
                        calres.Events.Add(fce);
                    }
                }
            }
            catch (Exception ex)
            {
                calres.ErrorMessage = String.Join(WebCommon.HTML_NEWLINE, WebCommon.ErrorListBuilder(ex));
            }

            return Json(calres, JsonRequestBehavior.AllowGet);
        }
        #endregion GetCalendarEvents action

        #region Actions for iScheduler plugin methods

        #region CheckObjects action
        /// <summary>
        /// Az ütemezendő objektumok létezésének ellenőrzése.
        /// Az ütemező a <paramref name="list"/>-ben felsorolja, mely objektumokat kell ellenőrizni
        /// a plugin interface által elvárt CheckObjects metódus meghívásával.
        /// </summary>
        /// <param name="xml">iScheduler xml kapcsolati sztringje.</param>
        /// <param name="groupId">Csoportazonosító, amelyre szűrni kell. Ha üres, akkor az XML fájlban lévő csoportazonosítóra szűr.</param>
        /// <param name="list">Az ellenőrzendő objektumok listája, vesszővel elválasztva.</param>
        /// <returns></returns>
        /// <remarks>Ha megváltoztatod a nevét, akkor az SchConst.cs osztályban is írd át az Actions alosztályban!</remarks>
        public JsonResult CheckObjects(string xml, string groupId, string list)
        {
            try
            {
                #region DEBUG
#if DEBUG
                string thisfn = $"{nameof(iSchedulerController)}.{nameof(CheckObjects)}: ";
                System.Diagnostics.Debug.WriteLine($"{thisfn}PING GroupId={groupId}, xml={xml}, list={list}");
#endif
                #endregion DEBUG

                var resultlist = new List<CheckListJSON>();

                using (var se = new ScheduleExecute(xml, base.BaseFolder, base.ForcedLanguageCode))
                {
                    resultlist = se.Check(list, groupId);
                }
                return Json(resultlist, JsonRequestBehavior.AllowGet);
            }
            catch (Exception ex)
            {
                var result = new List<CheckListJSON>();
                int i = 0;
                foreach (var item in WebCommon.ErrorListBuilder(ex))
                {
                    result.Add(new CheckListJSON { Value = (i++).ToString(), Text = item });
                }
                return Json(result, JsonRequestBehavior.AllowGet);
            }
        }
        #endregion CheckObjects action

        #region ListObjects action
        /// <summary>
        /// Az ütemezendő objektumok listáját adja vissza plugin mód esetén,
        /// a plugin interface által elvárt ListObjects metódus meghívásával.
        /// </summary>
        /// <param name="xml">iScheduler xml kapcsolati sztringje.</param>
        /// <param name="groupId">Csoportazonosító, amelyre szűrni kell. Ha üres, akkor az XML fájlban lévő csoportazonosítóra szűr.</param>
        /// <param name="activeOnly">Ha csak az aktívnak jelölt csomagok kellenek.</param>
        /// <param name="query">Az "autocomplete" szűrés kereső karaktere(i).</param>
        /// <returns></returns>
        /// <remarks>Ha megváltoztatod a nevét, akkor az SchConst.cs osztályban is írd át az Actions alosztályban!</remarks>
        public JsonResult ListObjects(string xml, string groupId, string activeOnly, string query)
        {
            try
            {
                #region DEBUG
#if DEBUG
                string thisfn = $"{nameof(iSchedulerController)}.{nameof(ListObjects)}: ";
                System.Diagnostics.Debug.WriteLine($"{thisfn}query={query}, GroupId={groupId}, xml={xml}, ActiveOnly={activeOnly}");
#endif
                #endregion DEBUG

                var resultlist = new List<SelectListJSON>();

                bool isActiveOnly = true;
                if (!string.IsNullOrWhiteSpace(activeOnly))
                {
                    if (bool.TryParse(activeOnly, out bool aOnly))
                    {
                        isActiveOnly = aOnly;
                    }
                }

                using (var se = new ScheduleExecute(xml, base.BaseFolder, base.ForcedLanguageCode))
                {
                    resultlist = se.List(isActiveOnly, groupId);
                }
                query = query ?? "";
                resultlist = resultlist.Where(x => x.Text.Contains(query ?? "")).ToList();
                return Json(resultlist, JsonRequestBehavior.AllowGet);
            }
            catch (Exception ex)
            {
                var result = new List<SelectListJSON>();
                int i = 0;
                foreach (var item in WebCommon.ErrorListBuilder(ex))
                {
                    result.Add(new SelectListJSON { Value = (i++).ToString(), Text = item });
                }
                return Json(result, JsonRequestBehavior.AllowGet);
            }
        }
        #endregion ListObjects action

        #region ListOperations action
        /// <summary>
        /// Az ütemezendő objektumok listáját adja vissza plugin mód esetén,
        /// a plugin interface által elvárt ListOperations metódus meghívásával.
        /// </summary>
        /// <param name="xml">iScheduler xml kapcsolati sztringje.</param>
        /// <param name="query">Az "autocomplete" szűrés kereső karaktere(i).</param>
        /// <returns></returns>
        /// <remarks>Ha megváltoztatod a nevét, akkor az SchConst.cs osztályban is írd át az Actions alosztályban!</remarks>
        public JsonResult ListOperations(string xml, string query)
        {
            try
            {
                #region DEBUG
#if DEBUG
                string thisfn = $"{nameof(iSchedulerController)}.{nameof(ListOperations)}: ";
                System.Diagnostics.Debug.WriteLine($"{thisfn}query={query}, xml={xml}");
#endif
                #endregion DEBUG

                var resultlist = new List<SelectListJSON>();

                using (var se = new ScheduleExecute(xml, base.BaseFolder, base.ForcedLanguageCode))
                {
                    resultlist = se.ListOperations();
                }
                query = query ?? "";
                resultlist = resultlist.Where(x => x.Text.Contains(query ?? "")).ToList();
                return Json(resultlist, JsonRequestBehavior.AllowGet);
            }
            catch (Exception ex)
            {
                var result = new List<SelectListJSON>();
                int i = 0;
                foreach (var item in WebCommon.ErrorListBuilder(ex))
                {
                    result.Add(new SelectListJSON { Value = (i++).ToString(), Text = item });
                }
                return Json(result, JsonRequestBehavior.AllowGet);
            }
        }
        #endregion ListOperations action

        #endregion Actions for iScheduler plugin methods

        #region Private methods (UnixToDateTime)

        #region BuildResultMessage private method
        private string BuildResultMessage(string retMessage)
        {
            string message = retMessage.Trim().Replace("'", "").Replace(@"\", @"\\");
            return message;
        }
        #endregion BuildResultMessage private method

        #region UnixToDateTime private method
        /// <summary>
        /// Unix időpecsét átalakítása DateTime értékké.
        /// Unix időpecsét = az eltelt idő másodpercben 1970.1.1 óta
        /// </summary>
        /// <param name="datestring"></param>
        /// <returns></returns>
        private DateTime UnixToDateTime(string datestring)
        {
            double ddate = Convert.ToDouble(datestring);
            System.DateTime result = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc);
            result = result.AddSeconds(ddate).ToLocalTime();
            return result;
        }
        #endregion UnixToDateTime private method

        private void SetError(Type headWordCode, params object[] args)
        {
            ViewBag.Title = this.Mlmgt(typeof(GeneralWordCodes.MLM.General.Words.Error));
            ViewBag.Header = this.Mlmgt(headWordCode, args);
        }

        #endregion Private methods

    }
}