/*******************************************************************************
 * You may amend and distribute as you like, but don't remove this header!
 *
 * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets.
 * See http://www.codeplex.com/EPPlus for details.
 *
 * Copyright (C) 2011  Jan Källman
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.

 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
 * See the GNU Lesser General Public License for more details.
 *
 * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php
 * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html
 *
 * All code and executables are provided "as is" with no warranty either express or implied. 
 * The author accepts no liability for any damage or loss of business that this product may cause.
 *
 * Code change notes:
 * 
 * Author							Change						Date
 * ******************************************************************************
 * Jan Källman		Added		21-MAR-2011
 * Jan Källman		License changed GPL-->LGPL 2011-12-16
 *******************************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

namespace OfficeOpenXml.Table.PivotTable
{
    /// <summary>
    /// Base collection class for pivottable fields
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class ExcelPivotTableFieldCollectionBase<T> : IEnumerable<T>
    {
        protected ExcelPivotTable _table;
        internal List<T> _list = new List<T>();
        internal ExcelPivotTableFieldCollectionBase(ExcelPivotTable table)
        {
            _table = table;
        }
        public IEnumerator<T> GetEnumerator()
        {
            return _list.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return _list.GetEnumerator();
        }
        public int Count
        {
            get
            {
                return _list.Count;
            }
        }
        internal void AddInternal(T field)
        {
            _list.Add(field);
        }
        internal void Clear()
        {
            _list.Clear();
        }
        public T this[int Index]
        {
            get
            {
                if (Index < 0 || Index >= _list.Count)
                {
                    throw (new ArgumentOutOfRangeException("Index out of range"));
                }
                return _list[Index];
            }
        }
    }
    public class ExcelPivotTableFieldCollection : ExcelPivotTableFieldCollectionBase<ExcelPivotTableField>
    {
        internal ExcelPivotTableFieldCollection(ExcelPivotTable table, string topNode) :
            base(table)
        {

        }
        /// <summary>
        /// Indexer by name
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public ExcelPivotTableField this[string name]
        {
            get
            {
                foreach (var field in _list)
                {
                    if (field.Name.Equals(name,StringComparison.InvariantCultureIgnoreCase))
                    {
                        return field;
                    }
                }
                return null;
            }
        }
        /// <summary>
        /// Returns the date group field.
        /// </summary>
        /// <param name="GroupBy">The type of grouping</param>
        /// <returns>The matching field. If none is found null is returned</returns>
        public ExcelPivotTableField GetDateGroupField(eDateGroupBy GroupBy)
        {
            foreach (var fld in _list)
            {
                if (fld.Grouping is ExcelPivotTableFieldDateGroup && (((ExcelPivotTableFieldDateGroup)fld.Grouping).GroupBy) == GroupBy)
                {
                    return fld;
                }
            }
            return null;
        }
        /// <summary>
        /// Returns the numeric group field.
        /// </summary>
        /// <returns>The matching field. If none is found null is returned</returns>
        public ExcelPivotTableField GetNumericGroupField()
        {
            foreach (var fld in _list)
            {
                if (fld.Grouping is ExcelPivotTableFieldNumericGroup)
                {
                    return fld;
                }
            }
            return null;
        }
    }
    /// <summary>
    /// Collection class for Row and column fields in a Pivottable 
    /// </summary>
    public class ExcelPivotTableRowColumnFieldCollection : ExcelPivotTableFieldCollectionBase<ExcelPivotTableField>
    {
        internal string _topNode;
        internal ExcelPivotTableRowColumnFieldCollection(ExcelPivotTable table, string topNode) :
            base(table)
	    {
            _topNode=topNode;
	    }

        /// <summary>
        /// Add a new row/column field
        /// </summary>
        /// <param name="Field">The field</param>
        /// <returns>The new field</returns>
        public ExcelPivotTableField Add(ExcelPivotTableField Field)
        {
            SetFlag(Field, true);
            _list.Add(Field);
            return Field;
        }
        /// <summary>
        /// Insert a new row/column field
        /// </summary>
        /// <param name="Field">The field</param>
        /// <param name="Index">The position to insert the field</param>
        /// <returns>The new field</returns>
        internal ExcelPivotTableField Insert(ExcelPivotTableField Field, int Index)
        {
            SetFlag(Field, true);
            _list.Insert(Index, Field);
            return Field;
        }
        private void SetFlag(ExcelPivotTableField field, bool value)
        {
            switch (_topNode)
            {
                case "rowFields":
                    if (field.IsColumnField || field.IsPageField)
                    {
                        throw(new Exception("This field is a column or page field. Can't add it to the RowFields collection"));
                    }
                    field.IsRowField = value;
                    field.Axis = ePivotFieldAxis.Row;
                    break;
                case "colFields":
                    if (field.IsRowField || field.IsPageField)
                    {
                        throw (new Exception("This field is a row or page field. Can't add it to the ColumnFields collection"));
                    }
                    field.IsColumnField = value;
                    field.Axis = ePivotFieldAxis.Column;
                    break;
                case "pageFields":
                    if (field.IsColumnField || field.IsRowField)
                    {
                        throw (new Exception("Field is a column or row field. Can't add it to the PageFields collection"));
                    }
                    if (_table.Address._fromRow < 3)
                    {
                        throw(new Exception(string.Format("A pivot table with page fields must be located above row 3. Currenct location is {0}", _table.Address.Address)));
                    }
                    field.IsPageField = value;
                    field.Axis = ePivotFieldAxis.Page;
                    break;
                case "dataFields":
                    
                    break;
            }
        }
        /// <summary>
        /// Remove a field
        /// </summary>
        /// <param name="Field"></param>
        public void Remove(ExcelPivotTableField Field)
        {
            if(!_list.Contains(Field))
            {
                throw new ArgumentException("Field not in collection");
            }
            SetFlag(Field, false);            
            _list.Remove(Field);            
        }
        /// <summary>
        /// Remove a field at a specific position
        /// </summary>
        /// <param name="Index"></param>
        public void RemoveAt(int Index)
        {
            if (Index > -1 && Index < _list.Count)
            {
                throw(new IndexOutOfRangeException());
            }
            SetFlag(_list[Index], false);
            _list.RemoveAt(Index);      
        }
    }
    /// <summary>
    /// Collection class for data fields in a Pivottable 
    /// </summary>
    public class ExcelPivotTableDataFieldCollection : ExcelPivotTableFieldCollectionBase<ExcelPivotTableDataField>
    {
        internal ExcelPivotTableDataFieldCollection(ExcelPivotTable table) :
            base(table)
        {

        }
        /// <summary>
        /// Add a new datafield
        /// </summary>
        /// <param name="field">The field</param>
        /// <returns>The new datafield</returns>
        public ExcelPivotTableDataField Add(ExcelPivotTableField field)
        {
            var dataFieldsNode = field.TopNode.SelectSingleNode("../../d:dataFields", field.NameSpaceManager);
            if (dataFieldsNode == null)
            {
                _table.CreateNode("d:dataFields");
                dataFieldsNode = field.TopNode.SelectSingleNode("../../d:dataFields", field.NameSpaceManager);
            }

            XmlElement node = _table.PivotTableXml.CreateElement("dataField", ExcelPackage.schemaMain);
            node.SetAttribute("fld", field.Index.ToString());
            dataFieldsNode.AppendChild(node);

            //XmlElement node = field.AppendField(dataFieldsNode, field.Index, "dataField", "fld");
            field.SetXmlNodeBool("@dataField", true,false);

            var dataField = new ExcelPivotTableDataField(field.NameSpaceManager, node, field);
            ValidateDupName(dataField);

            _list.Add(dataField);
            return dataField;
        }
        private void ValidateDupName(ExcelPivotTableDataField dataField)
        {
            if(ExistsDfName(dataField.Field.Name, null))
            {
                var index = 2;
                string name;
                do
                {
                    name = dataField.Field.Name + "_" + index++.ToString();
                }
                while (ExistsDfName(name,null));
                dataField.Name = name;
            }
        }

        internal bool ExistsDfName(string name, ExcelPivotTableDataField datafield)
        {
            foreach (var df in _list)
            {
                if (((!string.IsNullOrEmpty(df.Name) && df.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase) ||
                     (string.IsNullOrEmpty(df.Name) && df.Field.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)))) && datafield != df)
                {
                    return true;
                }
            }
            return false;
        }
        /// <summary>
        /// Remove a datafield
        /// </summary>
        /// <param name="dataField"></param>
        public void Remove(ExcelPivotTableDataField dataField)
        {
            XmlElement node = dataField.Field.TopNode.SelectSingleNode(string.Format("../../d:dataFields/d:dataField[@fld={0}]", dataField.Index), dataField.NameSpaceManager) as XmlElement;
            if (node != null)
            {
                node.ParentNode.RemoveChild(node);
            }
            _list.Remove(dataField);
        }
    }
}