using System;
using System.Collections;
using System.Collections.Generic;
using FastReport.Data;

namespace FastReport.AdvMatrix
{
    /// <summary>
    /// Contains a set of properties and methods to hold and manipulate the matrix descriptors.
    /// </summary>
    /// <remarks>
    /// This class contains two collections of descriptors such as <see cref="Columns"/> and
    /// <see cref="Rows"/>. Use collections' methods to add/remove descriptors. 
    /// When you are done, call the <see cref="AdvMatrixObject.BuildTemplate"/> method to refresh the matrix.
    /// </remarks>
    public class MatrixData
    {
        internal AdvMatrixObject Matrix { get; private set; }
        internal bool Completed { get; private set; }

        /// <summary>
        /// Gets a collection of column descriptors.
        /// </summary>
        /// <remarks>
        /// Note: after you add or remove items in this collection, call the 
        /// <see cref="AdvMatrixObject.BuildTemplate"/> method to refresh the matrix.
        /// </remarks>
        public MatrixHeader Columns { get; private set; }

        /// <summary>
        /// Gets a collection of row descriptors.
        /// </summary>
        /// <remarks>
        /// Note: after you add or remove items in this collection, call the 
        /// <see cref="AdvMatrixObject.BuildTemplate"/> method to refresh the matrix.
        /// </remarks>
        public MatrixHeader Rows { get; private set; }
        
        internal CellData CellData { get; private set; }
        
        /// <summary>
        /// Gets context required for aggregate calculation.
        /// </summary>
        public Context Context { get; private set; }

        internal InteractiveSortInfo InteractiveSort { get; private set; }

        internal bool IsEmpty { get { return Columns.Data.Items.Count == 0; } }
        
        internal string[] GetExpressions()
        {
            List<string> expressions = new List<string>();

            Columns.GetExpressions(expressions);
            Rows.GetExpressions(expressions);
            CellData.GetExpressions(expressions);
            
            return expressions.ToArray();
        }

        internal void Init()
        {
            Columns.Init();
            Rows.Init();
            CellData.Init();
        }

        internal void Done()
        {
            if (!Completed)
            {
                if (IsEmpty && Matrix.PrintIfEmpty)
                    ProcessDataRow();
                TopNBuilder.Process(Matrix);
                Completed = true;
            }

            if (!IsEmpty) 
                ResultBuilder.BuildResult(Matrix);
        }

        internal void ProcessDataRows()
        {
            DataSourceBase dataSource = Matrix.DataSource;
            if (dataSource != null)
            {
                dataSource.Init(Matrix.Filter);
                while (dataSource.HasMoreRows)
                {
                    ProcessDataRow();
                    dataSource.Next();
                }
            }
            else
            {
                Matrix.OnManualBuild(EventArgs.Empty);
            }
        }

        /// <summary>
        /// Processes single data row.
        /// </summary>
        /// <remarks>
        /// This method is used internally to process current data row. The matrix fills the column, row and cell data.
        /// You should use this method if you fill a matrix in code using <b>ManualBuild</b> event.
        /// </remarks>
        public void ProcessDataRow()
        {
            CellData.ProcessDataRow(Columns.ProcessDataRow(), Rows.ProcessDataRow());
        }

        /// <summary>
        /// Resets the data from the previous report run.
        /// </summary>
        public void Reset()
        {
            Completed = false;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="MatrixData"/> class.
        /// </summary>
        /// <param name="matrix">Reference to owner matrix.</param>
        public MatrixData(AdvMatrixObject matrix)
        {
            Matrix = matrix;
            Columns = new MatrixHeader(matrix, "Columns");
            Rows = new MatrixHeader(matrix, "Rows");
            CellData = new CellData(matrix);
            Context = new Context();
            InteractiveSort = new InteractiveSortInfo(this);
        }
    }
}