using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml;
using AGO.Reporting.Common;

namespace AGO.Reporting.Service.ReportGenerators
{
    /// <summary>
    /// Ãåíåðàòîð îò÷åòîâ â ôîðìàòå Rtf.
    /// </summary>
    public class RtfReportGenerator : BaseReportGenerator, IReportGenerator
    {
        private static readonly Regex MarkerMatcher = new Regex(@"\</[\w,\.]+\>", RegexOptions.Compiled);

        private const string MARKER_START_SYMBOL = "</";
        private const string MARKER_END_SYMBOL = ">";

        #region IReportGenerator Members
        private string reportName;
        private string templateName;
        private StringBuilder reportBuilder;


        protected override string MarkerStartSymbol
        {
            get { return MARKER_START_SYMBOL; }
        }

        protected override string MarkerEndSymbol
        {
            get { return MARKER_END_SYMBOL; }
        }

        public void MakeReport(string pathToTemplate, XmlDocument data, CancellationToken token)
        {
        	CancellationToken = token;
            reportBuilder = new StringBuilder();
            using(StreamReader sr = File.OpenText(pathToTemplate))
            {
                reportBuilder.Append(sr.ReadToEnd());
                sr.Close();
            }

            //Çàïîìèíàåì èìÿ øàáëîíà, ÷òî áû èç íåãî ïîòîì ñäåëàòü èìÿ ôàéëà
            templateName = Path.GetFileName(pathToTemplate);
            //Ïûòàåìñÿ ïîëó÷èòü èìÿ ôàéëà èç äàííûõ ãåíåðàòîðà äàííûõ è èñïîëüçîâàòü åãî, åñëè
            //ñíàðóæè èìÿ ôàéëà îò÷åòà íå çàäàíî.
            string dataGenFileName = data.DocumentElement.GetAttribute("reportName");
            if (string.IsNullOrEmpty(reportName) && !string.IsNullOrEmpty(dataGenFileName))
                reportName = dataGenFileName;

            XmlNodeList ranges = data.DocumentElement.SelectNodes(XPATH_RANGES);
            InitTicker(ranges.Count);
            foreach (XmlNode range in ranges)
            {
                XmlNodeList rangeItems = range.SelectNodes(XPATH_ITEMS);
                Ticker.Ticks += rangeItems.Count;
                foreach (XmlNode rangeItem in rangeItems)
                {
                    XmlNodeList itemValues = rangeItem.SelectNodes(XPATH_VALUES);
                    Ticker.Ticks += itemValues.Count;
                    foreach (XmlNode itemValue in itemValues)
                    {
                        string markerName = itemValue.SelectSingleNode(XPATH_NAME).Value;
                        string value = itemValue.InnerText;
                        ReplaceMarker(markerName, value);
                        Ticker.AddTick();
                    }
                    Ticker.AddTick(); //Òèê äëÿ èòåìà
                }
                Ticker.AddTick(); //Òèê äëÿ äèàïàçîíà
            }

            ClearAllMarkers();
        }

        /// <summary>
        /// Çàìåíÿåò ìàðêåð â rtf-øàáëîíå äîêóìåíòà çíà÷åíèåì.
        /// </summary>
        /// <param name="markerName">Èìÿ ìàðêåðà (áåç ñïåö.ñèìâîëîâ ïî êðàÿì).</param>
        /// <param name="value">Çíà÷åíèå, íà êîòîðîå íàäî çàìåíèòü ìàðêåð.</param>
        private void ReplaceMarker(string markerName, string value)
        {
            string marker = CompleteMarker(markerName);
            while (reportBuilder.ToString().Contains(marker))
            {
                reportBuilder.Replace(marker, value);
            }
        }

        /// <summary>
        /// Ïðîâåðÿåò, ñîäåðæèò ëè rtf-øàáëîí äîêóìåíòà ìàðêåð äëÿ çàìåíû äàííûìè.
        /// </summary>
        /// <param name="markers">Ñïèñîê ìàðêåðîâ, îáíàðóæåííûõ â øàáëîíå îò÷åòà.</param>
        /// <returns>true, åñëè øàáëîí îò÷åòà ñîäåðæèò ìàðêåð(û).</returns>
        private bool HasMarker(out string[] markers)
        {
            markers = new string[0];
            string reportText = reportBuilder.ToString();
            if (string.IsNullOrEmpty(reportText)) return false;
            bool has = !string.IsNullOrEmpty(reportText) &&
                       reportText.Contains(MarkerStartSymbol) &&
                       reportText.Contains(MarkerEndSymbol);
            if (has)
            {
                MatchCollection matches = MarkerMatcher.Matches(reportText);
                markers = new string[matches.Count];
                for (int i = 0; i < matches.Count; i++)
                {
                    markers[i] = matches[i].Value;
                }
            }
            return has;
        }

        /// <summary>
        /// Èùåò ìàðêåðû â rtf-øàáëîíå äîêóìåíòà è çàìåíÿåò èõ íà ïóñòóþ ñòðîêó.
        /// </summary>
        private void ClearAllMarkers()
        {
            string[] markers;
            if (HasMarker(out markers))
            {
                foreach (string marker in markers)
                {
                    ReplaceMarker(marker, string.Empty);
                }
            }
        }
        #endregion

        #region IReportGeneratorResult Members

    	public Stream Result
        {
            get
            {
                var ms = new MemoryStream();
                byte[] reportContent = Encoding.UTF8.GetBytes(reportBuilder.ToString());
                ms.Write(reportContent, 0, reportContent.Length);
                ms.Position = 0;
                return ms;
            }
        }

        public string GetFileName(string proposed)
        {
			//priority: user proposed, data gen proposed, template name
			if (!string.IsNullOrWhiteSpace(proposed))
			{
				reportName = proposed;
			} 
			else if (string.IsNullOrEmpty(reportName))
            {
                reportName = templateName;
            }

			return Path.ChangeExtension(reportName, "rtf");
        }

    	public string ContentType
    	{
			get { return "text/rtf"; }
    	}

    	#endregion
    }
}