using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using System.Web;

using VRH.Common;
using Vrh.XmlProcessing;

namespace Vrh.OneReport
{
    /// <summary>
    /// A OneReport modul XML paraméterező fájljának feldolgozása.
    /// </summary>
    public class OneReportXmlParser : XmlParser
    {
        #region Constants
        #endregion Constants

        #region ElementNames private classes
        private new class ElementNames : XmlLinqBase.ElementNames
        {
            /// <summary>
            /// 'Dataset' elem név a "Report/RdlDatasets" elemben.
            /// </summary>
            public const string DATASET = "Dataset";

            /// <summary>
            /// 'Parameter' elem név a "Report/RdlParameters" elemben.
            /// </summary>
            public const string PARAMETER = "Parameter";

            /// <summary>
            /// 'RdlDatasets' elem név a "Report" elemben.
            /// </summary>
            public const string DATASETS = "Datasets";

            /// <summary>
            /// 'RdlParameters' elem név a "Report" elemben.
            /// </summary>
            public const string PARAMETERS = "Parameters";
		}
		#endregion ElementNames private classes

		#region AttributeNames static class
		/// <summary>
		/// Az XML fájlban ilyen attribútum nevek találhatóak egy "Report" elemben.
		/// </summary>
		public static class AttributeNames
        {
            /// <summary>
            /// 'ConnectionString' attribútum név a 'Report' és 'Dataset' elemben.
            /// </summary>
            public const string CONNECTIONSTRING = "ConnectionString";

            /// <summary>
            /// 'Id' attribútum név a 'Report' elemben. 
            /// </summary>
            public const string ID = "Id";

            /// <summary>
            /// 'Name' attribútum név a 'Parameter' elemben. 
            /// </summary>
            public const string NAME = "Name";

            /// <summary>
            /// 'ReportFile' attribútum név a 'Report' elemben.
            /// </summary>
            public const string REPORTFILE = "ReportFile";
        }
        #endregion AttributeNames static class

        #region PROPERTIES

        /// <summary>
        /// A riport azonosítója
        /// </summary>
        public string Id { get; private set; }

        /// <summary>
        /// A riport megnevezése.
        /// </summary>
        public string DisplayName { get; private set; }

        /// <summary>
        /// Az aktulisan érvényes mappák.
        /// </summary>
        public FolderElement Folders { get; private set; }

        /// <summary>
        /// Az riportban beállított formátumok.
        /// </summary>
        public FormatElement Formats { get; private set; }

        /// <summary>
        /// A riportra érvényes timout beállítások.
        /// </summary>
        public TimeoutElement Timeout { get; private set; }

		/// <summary>
		/// A riportra érvényes AutoPurgebeállítások.
		/// </summary>
		public AutoPurgeElement AutoPurge { get; private set; }

		/// <summary>
		/// A riport (RDL) fájl elnevezése a teljes eléréssel.
		/// </summary>
		public string ReportFile { get; private set; }

        /// <summary>
        /// Ha file-ba készül a riport, akkor ide lehet tenni, hogy
        /// hova készült, hogy akárhol fel lehessen használni. 
        /// Fájl elnevezése a teljes eléréssel.
        /// </summary>
        public string TargetFile { get; set; }

        /// <summary>
        /// A riport bemenő paramétereinek gyűjteménye.
        /// </summary>
        public NameValueCollection  Parameters { get; private set; }

        /// <summary>
        /// A riporthoz felhasznált Dataset-ek listája.
        /// </summary>
        public List<Dataset> Datasets { get; private set; }

        /// <summary>
        /// Feldolgozás közben keletkezett hibaüzenetek gyűjteménye.
        /// </summary>
        public List<String> ErrorMessages { get; set; } = new List<string>();

        #endregion PROPERTIES

        #region Constructor
        /// <summary>
        /// XML paraméterező fájl feldolgozásának konstruktora.
        /// </summary>
        /// <param name="xmlc">XmlParser kapcsolati sztring (connection string), amely egy Report elemre mutat.</param>
        /// <param name="appPath">Az alkalmazás alap mappája. Ez alatt van az App_Path.</param>
        /// <param name="lcid">A környezetben érvényes nyelvi kód. Ha üres, akkor az XmlParser-ben beállított lesz érvényes.</param>
        /// <param name="otherVars">A környezetben az XmlParser részére létrehozott változók.</param>
        public OneReportXmlParser(
            XmlConnection xmlc, 
            string appPath, 
            string lcid,
            Dictionary<string,string> otherVars)
            : base(xmlc, appPath, lcid, otherVars)
        {
            try
            {
				var xmlcGeneral = NextToMe(this, Constants.GENERALELEMENTNAME);
				var xmlcDirectory = NextToMe(this, Constants.REPORTDIRECTORYELEMENTNAME);
				var general = new GeneralXmlParser(xmlcGeneral, appPath, lcid, otherVars);
				var directory = new DirectoryXmlParser(xmlcDirectory, appPath, lcid, otherVars);

                this.Id = GetValue(AttributeNames.ID, this.RootElement, "", true, true);

                #region DisplayName beállítása
                this.DisplayName = directory.GetDisplayName(this.Id);
                if (string.IsNullOrWhiteSpace(this.DisplayName)) this.DisplayName = this.Id;
                #endregion DisplayName beállítása

                #region Folders beállítása
                this.Folders = new FolderElement(
                    this.RootElement.Element(GeneralXmlParser.ElementNames.DEFAULTFOLDER),
                    general.DefaultFolder
                );
                this.Folders.Export = VrhCommon.RealPath(appPath, this.Folders.Export);
                this.Folders.File = VrhCommon.RealPath(appPath, this.Folders.File);
                this.Folders.Rdl = VrhCommon.RealPath(appPath, this.Folders.Rdl);
                #endregion Folders beállítása

                #region Formats beállítása
                this.Formats = new FormatElement(this.RootElement.Element(GeneralXmlParser.ElementNames.DEFAULTFORMAT),general.DefaultFormat);
                #endregion Formats beállítása

                #region Timeout beálítása
                this.Timeout = new TimeoutElement(this.RootElement.Element(GeneralXmlParser.ElementNames.TIMEOUT),general.Timeout);
				#endregion Timeout beálítása

				#region AutoPurge beálítása (nem adható meg riportonként, hanem csak a Generalban)
				this.AutoPurge = new AutoPurgeElement(defaultAutoPurge: general.AutoPurge);
				#endregion AutoPurge beálítása (nem adható meg riportonként, hanem csak a Generalban)

				#region ReportFile beállítása és létezésének ellenőrzése
				this.ReportFile = Path.Combine(this.Folders.Rdl,GetValue(this.RootElement.Attribute(AttributeNames.REPORTFILE), "", true, true));
                if (!File.Exists(this.ReportFile))
                {
                    throw new ApplicationException($"The report input (.rdl) file does not exist! ReportId = {this.Id}; File = \"{this.ReportFile}\"");
                }
                #endregion ReportFile beállítása és létezésének ellenőrzése

                #region Parameters beállítása 
                XElement rdlParamXE = this.RootElement.Element(ElementNames.PARAMETERS);
                if (rdlParamXE != null)
                {
                    IEnumerable<XElement> paramsXE = rdlParamXE.Elements(ElementNames.PARAMETER);
                    if (paramsXE != null && paramsXE.Any())
                    {
                        this.Parameters = new NameValueCollection();
                        foreach (XElement parXE in paramsXE)
                        {
                            this.Parameters.Add(
                                GetValue(AttributeNames.NAME, parXE, "", true, true),
                                GetValue(parXE, "")
                            );
                        }
                    }
                }
                #endregion Parameters beállítása 

                #region Datasets beállítása
                string defaultConnectionStringStoreName = GetValue(this.RootElement.Attribute(AttributeNames.CONNECTIONSTRING), general.ConnectionString);
                XElement rdlDatasetsXE = this.RootElement.Element(ElementNames.DATASETS); 
                if (rdlDatasetsXE != null)
                {
                    IEnumerable<XElement> datasetsXE = rdlDatasetsXE.Elements(ElementNames.DATASET);
                    if (datasetsXE != null && datasetsXE.Any())
                    {
                        this.Datasets = new List<Dataset>();
                        foreach (XElement dsetXE in datasetsXE)
                        {
                            this.Datasets.Add(new Dataset(this.Id,dsetXE, general.PluginList, defaultConnectionStringStoreName));
                        }
                    }
                }
                #endregion Datasets beállítása
            }
            catch (Exception ex)
            {
                this.ErrorMessages.Add($"Current xml file: {base.CurrentFileName}");
                this.ErrorMessages.AddRange(VrhCommon.ErrorListBuilder(ex));
            }
        }
		XmlConnection NextToMe(XmlParser xmlp, string elementpath, string preset=null, string id=null)
		{
			var xmlcecs = new XmlConnection(xmlp.EffectiveConnectionString);
			var ep = elementpath.Replace(@"\", @"/");  ep = ep[0] == '/' ? ep.Substring(1) : ep;
			var e = xmlcecs.Element.Replace(@"\", @"/"); e = e[0] == '/' ? e : '/' + e;
			var ae = e.Split('/'); ae[ae.Length - 1] = ep; e = String.Join("/", ae);
			xmlcecs.Element = e;
			xmlcecs.Preset = preset;
			xmlcecs.Id = id;
			return xmlcecs;
		}
		#endregion Constructor

		#region Public methods


		#endregion Public methods
	}
}
