using System;
using System.Collections.Generic;
using FastReport.Code;
using FastReport.Utils;

namespace FastReport.AdvMatrix
{
    /// <summary>
    /// Represents the storage of header descriptors and its values.
    /// </summary>
    public class MatrixHeader : IFRSerializable
    {
        private AdvMatrixObject matrix;
        private string name;
        private int runningIndex;

        /// <summary>
        /// Gets the root header descriptor.
        /// </summary>
        public HeaderDescriptor Descriptor { get; private set; }
        
        /// <summary>
        /// Gets the size of this header.
        /// </summary>
        public int Size {  get { return GetSize(Descriptor); } }

        internal HeaderData Data { get; private set; }

        internal int NextIndex
        {
            get
            {
                runningIndex++;
                return runningIndex;
            }
        }

        private void FillData(HeaderDescriptor descr, HeaderData data, List<HeaderData> activeItems)
        {
            foreach (HeaderDescriptor d in descr.Items)
            {
                if (!String.IsNullOrEmpty(d.Filter))
                {
                    if ((dynamic)matrix.Report.Calc(d.Filter) == false)
                        continue;
                }

                if (!String.IsNullOrEmpty(d.VisibleExpression))
                {
                    d.Visible = (dynamic)matrix.Report.Calc(d.VisibleExpression);
                }

                HeaderData dataItem = data.Add(d);
                activeItems.Add(dataItem);

                if (matrix.DataSource != null)
                    dataItem.DataRowNo = matrix.DataSource.CurrentRowNo;

                // set index for the newly added items
                if (dataItem.Index == 0)
                    dataItem.Index = NextIndex;

                if (d.SortAggregate != null)
                    d.SortAggregate.ProcessDataRow(dataItem.Index, 0, matrix.Report.Calc(d.SortAggregate.Expression));

                FillData(d, dataItem, activeItems);
            }
        }

        private int GetSize(HeaderDescriptor root)
        {
            int maxSize = -1;
            foreach (HeaderDescriptor d in root.Items)
            {
                int size = GetSize(d);
                if (size > maxSize)
                    maxSize = size;
            }
            return maxSize + (root.Stepped && root.Items.Count > 0 ? 0 : 1);
        }

        private void GetExpressions(HeaderDescriptor root, List<string> expressions)
        {
            foreach (HeaderDescriptor d in root.Items)
            {
                if (!String.IsNullOrEmpty(d.Expression))
                    expressions.Add(d.Expression);
                if (!String.IsNullOrEmpty(d.DisplayText))
                    expressions.AddRange(CodeUtils.GetExpressions(d.DisplayText, "[", "]"));
                if (!String.IsNullOrEmpty(d.Filter))
                    expressions.Add(d.Filter);
                if (!String.IsNullOrEmpty(d.VisibleExpression))
                    expressions.Add(d.VisibleExpression);
                if (d.SortAggregate != null)
                    expressions.Add(d.SortAggregate.Expression);
                // we don't need to add InteractiveSortAggregate expression because it's a reference to existing cell aggregate

                GetExpressions(d, expressions);
            }
        }

        private void InitAggregates(HeaderDescriptor root)
        {
            root.InitAggregates();

            foreach (HeaderDescriptor d in root.Items)
            {
                InitAggregates(d);
            }
        }

        internal void GetExpressions(List<string> expressions)
        {
            InitAggregates(Descriptor);
            GetExpressions(Descriptor, expressions);
        }

        internal void Init()
        {
            runningIndex = 0;
            Data.Clear();
        }

        internal List<HeaderData> ProcessDataRow()
        {
            List<HeaderData> activeItems = new List<HeaderData>();
            FillData(Descriptor, Data, activeItems);
            return activeItems;
        }

        /// <inheritdoc/>
        public void Serialize(FRWriter writer)
        {
            writer.ItemName = name;
            // do not write root item
            foreach (HeaderDescriptor d in Descriptor.Items)
                writer.Write(d);
        }

        /// <inheritdoc/>
        public void Deserialize(FRReader reader)
        {
            reader.Read(Descriptor);
        }

        internal void Assign(MatrixHeader src)
        {
            Descriptor = src.Descriptor;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="MatrixHeader"/> class.
        /// </summary>
        public MatrixHeader(AdvMatrixObject matrix, string name)
        {
            this.matrix = matrix;
            this.name = name;
            Descriptor = new HeaderDescriptor();
            Data = new HeaderData(null, null);
        }
    }
}