/*******************************************************************************
 * 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		2009-10-01
 * Jan Källman		License changed GPL-->LGPL 2011-12-16
 *******************************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Collections;
using OfficeOpenXml.Table.PivotTable;
namespace OfficeOpenXml.Drawing.Chart
{
    public sealed class ExcelBubbleChartSeries : ExcelChartSeries
    {
        internal ExcelBubbleChartSeries(ExcelChart chart, XmlNamespaceManager ns, XmlNode node, bool isPivot)
           : base(chart,ns,node, isPivot)
        {
            //_chartSeries = new ExcelChartSeries(this, _drawings.NameSpaceManager, _chartNode, isPivot);
        }
        public ExcelChartSerie Add(ExcelRangeBase Serie, ExcelRangeBase XSerie, ExcelRangeBase BubbleSize)
        {
            return base.AddSeries(Serie.FullAddressAbsolute, XSerie.FullAddressAbsolute, BubbleSize.FullAddressAbsolute);
        }
        public ExcelChartSerie Add(string SerieAddress, string XSerieAddress, string BubbleSizeAddress)
        {
            return base.AddSeries(SerieAddress, XSerieAddress, BubbleSizeAddress);
        }
    }
    /// <summary>
   /// Collection class for chart series
   /// </summary>
    public class ExcelChartSeries : XmlHelper, IEnumerable
    {
       List<ExcelChartSerie> _list=new List<ExcelChartSerie>();
       internal ExcelChart _chart;
       XmlNode _node;
       XmlNamespaceManager _ns;
       internal ExcelChartSeries(ExcelChart chart, XmlNamespaceManager ns, XmlNode node, bool isPivot)
           : base(ns,node)
       {
           _ns = ns;
           _chart=chart;
           _node=node;
           _isPivot = isPivot;
           SchemaNodeOrder = new string[] { "view3D", "plotArea", "barDir", "grouping", "scatterStyle", "varyColors", "ser", "explosion", "dLbls", "firstSliceAng", "holeSize", "shape", "legend", "axId" };
           foreach(XmlNode n in node.SelectNodes("c:ser",ns))
           {
               ExcelChartSerie s;
                if (chart.ChartNode.LocalName == "scatterChart")
                {
                    s = new ExcelScatterChartSerie(this, ns, n, isPivot);
                }
                else if (chart.ChartNode.LocalName == "lineChart")
                {
                    s = new ExcelLineChartSerie(this, ns, n, isPivot);
                }
                else if (chart.ChartNode.LocalName == "pieChart" ||
                         chart.ChartNode.LocalName == "ofPieChart" ||
                         chart.ChartNode.LocalName == "pie3DChart" ||
                         chart.ChartNode.LocalName == "doughnutChart")
                {
                    s = new ExcelPieChartSerie(this, ns, n, isPivot);
                }
                else if (chart.ChartNode.LocalName == "bubbleChart")
                {
                    s = new ExcelBubbleChartSerie(this, ns, n, isPivot);
                }
                else
                {
                    s = new ExcelChartSerie(this, ns, n, isPivot);
                }
               _list.Add(s);
           }
       }

       #region IEnumerable Members

       public IEnumerator GetEnumerator()
       {
           return (_list.GetEnumerator());
       }
       /// <summary>
       /// Returns the serie at the specified position.  
       /// </summary>
       /// <param name="PositionID">The position of the series.</param>
       /// <returns></returns>
       public ExcelChartSerie this[int PositionID]
       {
           get
           {
               return (_list[PositionID]);
           }
       }
       public int Count
       {
           get
           {
               return _list.Count;
           }
       }
       /// <summary>
       /// Delete the chart at the specific position
       /// </summary>
       /// <param name="PositionID">Zero based</param>
       public void Delete(int PositionID)
       {
           ExcelChartSerie ser = _list[PositionID];
           ser.TopNode.ParentNode.RemoveChild(ser.TopNode);
           _list.RemoveAt(PositionID);
       }
       #endregion
       /// <summary>
       /// A reference to the chart object
       /// </summary>
       public ExcelChart Chart
       {
           get
           {
               return _chart;
           }
       }
       #region "Add Series"

       /// <summary>
       /// Add a new serie to the chart. Do not apply to pivotcharts.
       /// </summary>
       /// <param name="Serie">The Y-Axis range</param>
       /// <param name="XSerie">The X-Axis range</param>
       /// <returns></returns>
       public virtual ExcelChartSerie Add(ExcelRangeBase Serie, ExcelRangeBase XSerie)
       {
           if (_chart.PivotTableSource != null)
           {
               throw (new InvalidOperationException("Can't add a serie to a pivotchart"));
           }
           return AddSeries(Serie.FullAddressAbsolute, XSerie.FullAddressAbsolute,"");
       }
       /// <summary>
       /// Add a new serie to the chart.Do not apply to pivotcharts.
       /// </summary>
       /// <param name="SerieAddress">The Y-Axis range</param>
       /// <param name="XSerieAddress">The X-Axis range</param>
       /// <returns></returns>
       public virtual ExcelChartSerie Add(string SerieAddress, string XSerieAddress)
       {
           if (_chart.PivotTableSource != null)
           {
               throw (new InvalidOperationException("Can't add a serie to a pivotchart"));
           }
           return AddSeries(SerieAddress, XSerieAddress, "");
       }
       internal protected ExcelChartSerie AddSeries(string SeriesAddress, string XSeriesAddress, string bubbleSizeAddress)
        {
               XmlElement ser = _node.OwnerDocument.CreateElement("ser", ExcelPackage.schemaChart);
               XmlNodeList node = _node.SelectNodes("c:ser", _ns);
               if (node.Count > 0)
               {
                   _node.InsertAfter(ser, node[node.Count-1]);
               }
               else
               {
                   InserAfter(_node, "c:varyColors,c:grouping,c:barDir,c:scatterStyle,c:ofPieType", ser);
                }
               int idx = FindIndex();
               ser.InnerXml = string.Format("<c:idx val=\"{1}\" /><c:order val=\"{1}\" /><c:tx><c:strRef><c:f></c:f><c:strCache><c:ptCount val=\"1\" /></c:strCache></c:strRef></c:tx>{5}{0}{2}{3}{4}", AddExplosion(Chart.ChartType), idx, AddScatterPoint(Chart.ChartType), AddAxisNodes(Chart.ChartType), AddSmooth(Chart.ChartType), AddMarker(Chart.ChartType));
               ExcelChartSerie serie;
               switch (Chart.ChartType)
               {
                   case eChartType.Bubble:
                   case eChartType.Bubble3DEffect:
                       serie = new ExcelBubbleChartSerie(this, NameSpaceManager, ser, _isPivot)
                       {
                           Bubble3D=Chart.ChartType==eChartType.Bubble3DEffect,
                           Series = SeriesAddress,
                           XSeries = XSeriesAddress,
                           BubbleSize = bubbleSizeAddress                            
                       };
                       break;
                   case eChartType.XYScatter:
                   case eChartType.XYScatterLines:
                   case eChartType.XYScatterLinesNoMarkers:
                   case eChartType.XYScatterSmooth:
                   case eChartType.XYScatterSmoothNoMarkers:
                       serie = new ExcelScatterChartSerie(this, NameSpaceManager, ser, _isPivot);
                       break;
                   case eChartType.Radar:
                   case eChartType.RadarFilled:
                   case eChartType.RadarMarkers:
                       serie = new ExcelRadarChartSerie(this, NameSpaceManager, ser, _isPivot);
                       break;
                   case eChartType.Surface:
                   case eChartType.SurfaceTopView:
                   case eChartType.SurfaceTopViewWireframe:
                   case eChartType.SurfaceWireframe:
                       serie = new ExcelSurfaceChartSerie(this, NameSpaceManager, ser, _isPivot);
                       break;
                   case eChartType.Pie:
                   case eChartType.Pie3D:
                   case eChartType.PieExploded:
                   case eChartType.PieExploded3D:
                   case eChartType.PieOfPie:
                   case eChartType.Doughnut:
                   case eChartType.DoughnutExploded:
                   case eChartType.BarOfPie:
                       serie = new ExcelPieChartSerie(this, NameSpaceManager, ser, _isPivot);
                       break;
                   case eChartType.Line:
                   case eChartType.LineMarkers:
                   case eChartType.LineMarkersStacked:
                   case eChartType.LineMarkersStacked100:
                   case eChartType.LineStacked:
                   case eChartType.LineStacked100:
                       serie = new ExcelLineChartSerie(this, NameSpaceManager, ser, _isPivot);
                       if (Chart.ChartType == eChartType.LineMarkers ||
                           Chart.ChartType == eChartType.LineMarkersStacked ||
                           Chart.ChartType == eChartType.LineMarkersStacked100)
                       {
                           ((ExcelLineChartSerie)serie).Marker = eMarkerStyle.Square;
                       }
                       ((ExcelLineChartSerie)serie).Smooth = ((ExcelLineChart)Chart).Smooth;
                       break;
                   case eChartType.BarClustered:
                   case eChartType.BarStacked:
                   case eChartType.BarStacked100:
                   case eChartType.ColumnClustered:
                   case eChartType.ColumnStacked:
                   case eChartType.ColumnStacked100:
                   case eChartType.BarClustered3D:
                   case eChartType.BarStacked3D:
                   case eChartType.BarStacked1003D:
                   case eChartType.ColumnClustered3D:
                   case eChartType.ColumnStacked3D:
                   case eChartType.ColumnStacked1003D:
                   case eChartType.ConeBarClustered:
                   case eChartType.ConeBarStacked:
                   case eChartType.ConeBarStacked100:
                   case eChartType.ConeCol:
                   case eChartType.ConeColClustered:
                   case eChartType.ConeColStacked:
                   case eChartType.ConeColStacked100:
                   case eChartType.CylinderBarClustered:
                   case eChartType.CylinderBarStacked:
                   case eChartType.CylinderBarStacked100:
                   case eChartType.CylinderCol:
                   case eChartType.CylinderColClustered:
                   case eChartType.CylinderColStacked:
                   case eChartType.CylinderColStacked100:
                   case eChartType.PyramidBarClustered:
                   case eChartType.PyramidBarStacked:
                   case eChartType.PyramidBarStacked100:
                   case eChartType.PyramidCol:
                   case eChartType.PyramidColClustered:
                   case eChartType.PyramidColStacked:
                   case eChartType.PyramidColStacked100:
                       serie = new ExcelBarChartSerie(this, NameSpaceManager, ser, _isPivot);
                       ((ExcelBarChartSerie)serie).InvertIfNegative=false;
                       break;
                   default:
                       serie = new ExcelChartSerie(this, NameSpaceManager, ser, _isPivot);
                       break;
               }               
               serie.Series = SeriesAddress;
               serie.XSeries = XSeriesAddress;                     
           _list.Add(serie);
               return serie;
        }
       bool _isPivot;
       internal void AddPivotSerie(ExcelPivotTable pivotTableSource)
       {
           var r=pivotTableSource.WorkSheet.Cells[pivotTableSource.Address.Address];
           _isPivot = true;
           AddSeries(r.Offset(0, 1, r._toRow - r._fromRow + 1, 1).FullAddressAbsolute, r.Offset(0, 0, r._toRow - r._fromRow + 1, 1).FullAddressAbsolute,"");
       }
       private int FindIndex()
       {    
           int ret = 0, newID=0;
           if (_chart.PlotArea.ChartTypes.Count > 1)
           {
               foreach (var chart in _chart.PlotArea.ChartTypes)
               {
                   if (newID>0)
                   {
                       foreach (ExcelChartSerie serie in chart.Series)
                       {
                           serie.SetID((++newID).ToString());
                       }
                   }
                   else
                   {
                       if (chart == _chart)
                       {
                           ret += _list.Count + 1;
                           newID=ret;
                       }
                       else
                       {
                           ret += chart.Series.Count;
                       }
                   }
               }
               return ret-1;
           }
           else
           {
               return _list.Count;
           }
       }
       #endregion
       #region "Xml init Functions"
       private string AddMarker(eChartType chartType)
       {
           if (chartType == eChartType.Line ||
               chartType == eChartType.LineStacked ||
               chartType == eChartType.LineStacked100 ||
               chartType == eChartType.XYScatterLines ||
               chartType == eChartType.XYScatterSmooth ||
               chartType == eChartType.XYScatterLinesNoMarkers ||
               chartType == eChartType.XYScatterSmoothNoMarkers)
           {
               return "<c:marker><c:symbol val=\"none\" /></c:marker>";
           }
           else
           {
               return "";
           }
       }
       private string AddScatterPoint(eChartType chartType)
       {
           if (chartType == eChartType.XYScatter)
           {
               return "<c:spPr><a:ln w=\"28575\"><a:noFill /></a:ln></c:spPr>";
           }
           else
           {
               return "";
           }
       }
       private string AddAxisNodes(eChartType chartType)
       {
           if ( chartType == eChartType.XYScatter ||
                chartType == eChartType.XYScatterLines ||
                chartType == eChartType.XYScatterLinesNoMarkers ||
                chartType == eChartType.XYScatterSmooth ||
                chartType == eChartType.XYScatterSmoothNoMarkers || 
                chartType == eChartType.Bubble ||
                chartType == eChartType.Bubble3DEffect)
           {
               return "<c:xVal /><c:yVal />";
           }
           else
           {
               return "<c:val />";
           }
       }

       private string AddExplosion(eChartType chartType)
       {
           if (chartType == eChartType.PieExploded3D ||
              chartType == eChartType.PieExploded ||
               chartType == eChartType.DoughnutExploded)
           {
               return "<c:explosion val=\"25\" />"; //Default 25;
           }
           else
           {
               return "";
           }
       }
       private string AddSmooth(eChartType chartType)
       {
           if (chartType == eChartType.XYScatterSmooth ||
              chartType == eChartType.XYScatterSmoothNoMarkers)
           {
               return "<c:smooth val=\"1\" />"; //Default 25;
           }
           else
           {
               return "";
           }
       }
        #endregion
    }
}
