/*******************************************************************************
 * 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.Globalization;
using System.Text;
using System.Xml;
using System.IO;
using OfficeOpenXml.Table.PivotTable;
using OfficeOpenXml.Utils;
using OfficeOpenXml.Packaging;

namespace OfficeOpenXml.Drawing.Chart
{
    #region "Chart Enums"
    /// <summary>
    /// Chart type
    /// </summary>
    public enum eChartType
    {
        Area3D=-4098,
        AreaStacked3D=78,
        AreaStacked1003D=79,
        BarClustered3D= 60,
        BarStacked3D=61,
        BarStacked1003D=62,
        Column3D=-4100,
        ColumnClustered3D=54,
        ColumnStacked3D=55,
        ColumnStacked1003D=56,
        Line3D=-4101,
        Pie3D=-4102,
        PieExploded3D=70,
        Area=1,
        AreaStacked=76,
        AreaStacked100=77,
        BarClustered=57,
        BarOfPie=71,
        BarStacked=58,
        BarStacked100=59,
        Bubble=15,
        Bubble3DEffect=87,
        ColumnClustered=51,
        ColumnStacked=52,
        ColumnStacked100=53,
        ConeBarClustered=102,
        ConeBarStacked=103,
        ConeBarStacked100=104,
        ConeCol=105,
        ConeColClustered=99,
        ConeColStacked=100,
        ConeColStacked100=101,
        CylinderBarClustered=95,
        CylinderBarStacked=96,
        CylinderBarStacked100=97,
        CylinderCol=98,
        CylinderColClustered=92,
        CylinderColStacked=93,
        CylinderColStacked100=94,
        Doughnut=-4120,
        DoughnutExploded=80,
        Line=4,
        LineMarkers=65,
        LineMarkersStacked=66,
        LineMarkersStacked100=67,
        LineStacked=63,
        LineStacked100=64,
        Pie=5,
        PieExploded=69,
        PieOfPie=68,
        PyramidBarClustered=109,
        PyramidBarStacked=110,
        PyramidBarStacked100=111,
        PyramidCol=112,
        PyramidColClustered=106,
        PyramidColStacked=107,
        PyramidColStacked100=108,
        Radar=-4151,
        RadarFilled=82,
        RadarMarkers=81,
        StockHLC=88,
        StockOHLC=89,
        StockVHLC=90,
        StockVOHLC=91,
        Surface=83,
        SurfaceTopView=85,
        SurfaceTopViewWireframe=86,
        SurfaceWireframe=84,
        XYScatter=-4169,
        XYScatterLines=74,
        XYScatterLinesNoMarkers=75,
        XYScatterSmooth=72,
        XYScatterSmoothNoMarkers=73
}
    /// <summary>
    /// Bar or column
    /// </summary>
    public enum eDirection
    {
        Column,
        Bar
    }
    /// <summary>
    /// How the series are grouped
    /// </summary>
    public enum eGrouping
    {
        Standard,
        Clustered,
        Stacked,
        PercentStacked
    }
    /// <summary>
    /// Shape for bar charts
    /// </summary>
    public enum eShape
    {
        Box,
        Cone,
        ConeToMax,
        Cylinder,
        Pyramid,
        PyramidToMax
    }
    /// <summary>
    /// Smooth or lines markers
    /// </summary>
    public enum eScatterStyle
    {
        LineMarker,
        SmoothMarker,
     }
    public enum eRadarStyle
    {
        /// <summary>
        /// Specifies that the radar chart shall be filled and have lines but no markers.
        /// </summary>
        Filled,
        /// <summary>
        /// Specifies that the radar chart shall have lines and markers but no fill.
        /// </summary>
        Marker,
        /// <summary>
        /// Specifies that the radar chart shall have lines but no markers and no fill.
        /// </summary>
        Standard 
    }
    /// <summary>
    /// Bar or pie
    /// </summary>
    public enum ePieType
    {
        Bar,
        Pie
    }
    /// <summary>
    /// Position of the labels
    /// </summary>
    public enum eLabelPosition
    {
        BestFit,
        Left,
        Right,
        Center,
        Top,
        Bottom,
        InBase,
        InEnd,
        OutEnd
    }
    /// <summary>
    /// Axis label position
    /// </summary>
    public enum eTickLabelPosition
    {
        High,
        Low,
        NextTo,
        None
    }
    /// <summary>
    /// Markerstyle
    /// </summary>
    public enum eMarkerStyle
    {        
        Circle,
        Dash,
        Diamond,
        Dot,
        None,
        Picture,
        Plus,
        Square,
        Star,
        Triangle,
        X,
    }
    /// <summary>
    /// The time unit of major and minor datetime axis values
    /// </summary>
    public enum eTimeUnit
    {        
        Years,
        Months,
        Days,
    }
    /// <summary>
    /// The build in style of the chart.
    /// </summary>
    public enum eChartStyle
    {
        None,
        Style1,
        Style2,
        Style3,
        Style4,
        Style5,
        Style6,
        Style7,
        Style8,
        Style9,
        Style10,
        Style11,
        Style12,
        Style13,
        Style14,
        Style15,
        Style16,
        Style17,
        Style18,
        Style19,
        Style20,
        Style21,
        Style22,
        Style23,
        Style24,
        Style25,
        Style26,
        Style27,
        Style28,
        Style29,
        Style30,
        Style31,
        Style32,
        Style33,
        Style34,
        Style35,
        Style36,
        Style37,
        Style38,
        Style39,
        Style40,
        Style41,
        Style42,
        Style43,
        Style44,
        Style45,
        Style46,
        Style47,
        Style48
    }
    /// <summary>
    /// Type of Trendline for a chart
    /// </summary>
    public enum eTrendLine
    {
        /// <summary>
        /// Specifies the trendline shall be an exponential curve in the form
        /// </summary>
        Exponential,
        /// <summary>
        /// Specifies the trendline shall be a logarithmic curve in the form , where log is the natural
        /// </summary>
        Linear,
        /// <summary>
        /// Specifies the trendline shall be a logarithmic curve in the form , where log is the natural
        /// </summary>
        Logarithmic,
        /// <summary>
        /// Specifies the trendline shall be a moving average of period Period
        /// </summary>
        MovingAvgerage,
        /// <summary>
        /// Specifies the trendline shall be a polynomial curve of order Order in the form 
        /// </summary>
        Polynomial,
        /// <summary>
        /// Specifies the trendline shall be a power curve in the form
        /// </summary>
        Power
    }
    /// <summary>
    /// Specifies the possible ways to display blanks
    /// </summary>
    public enum eDisplayBlanksAs
    {
        /// <summary>
        /// Blank values shall be left as a gap
        /// </summary>
        Gap,
        /// <summary>
        /// Blank values shall be spanned with a line (Line charts)
        /// </summary>
        Span,
        /// <summary>
        /// Blank values shall be treated as zero
        /// </summary>
        Zero
    }
    public enum eSizeRepresents
    {
        /// <summary>
        /// Specifies the area of the bubbles shall be proportional to the bubble size value.
        /// </summary>
        Area,
        /// <summary>
        /// Specifies the radius of the bubbles shall be proportional to the bubble size value.
        /// </summary>
        Width
    }
    #endregion
    
    
    /// <summary>
   /// Base class for Chart object.
   /// </summary>
    public class ExcelChart : ExcelDrawing
    {
       const string rootPath = "c:chartSpace/c:chart/c:plotArea";
       //string _chartPath;
       protected internal ExcelChartSeries _chartSeries;
       internal ExcelChartAxis[] _axis;
       protected XmlHelper _chartXmlHelper;
       #region "Constructors"
       internal ExcelChart(ExcelDrawings drawings, XmlNode node, eChartType type, bool isPivot) :
           base(drawings, node, "xdr:graphicFrame/xdr:nvGraphicFramePr/xdr:cNvPr/@name")
       {
           ChartType = type;
           CreateNewChart(drawings, type, null);

           Init(drawings, _chartNode);

           _chartSeries = new ExcelChartSeries(this, drawings.NameSpaceManager, _chartNode, isPivot);

           SetTypeProperties();
           LoadAxis();
       }
       internal ExcelChart(ExcelDrawings drawings, XmlNode node, eChartType type, ExcelChart topChart, ExcelPivotTable PivotTableSource) :
           base(drawings, node, "xdr:graphicFrame/xdr:nvGraphicFramePr/xdr:cNvPr/@name")
       {
           ChartType = type;
           CreateNewChart(drawings, type, topChart);

           Init(drawings, _chartNode);

           _chartSeries = new ExcelChartSeries(this, drawings.NameSpaceManager, _chartNode, PivotTableSource!=null);
           if (PivotTableSource != null) SetPivotSource(PivotTableSource);

           SetTypeProperties();
           if (topChart == null)
               LoadAxis();
           else
           {
               _axis = topChart.Axis;
               if (_axis.Length > 0)
               {
                   XAxis = _axis[0];
                   YAxis = _axis[1];
               }
           }
       }
       internal ExcelChart(ExcelDrawings drawings, XmlNode node, Uri uriChart, ZipPackagePart part, XmlDocument chartXml, XmlNode chartNode) :
           base(drawings, node, "xdr:graphicFrame/xdr:nvGraphicFramePr/xdr:cNvPr/@name")
       {
           UriChart = uriChart;
           Part = part;
           ChartXml = chartXml;
           _chartNode = chartNode;
           InitChartLoad(drawings, chartNode);
           ChartType = GetChartType(chartNode.LocalName);
       }
       internal ExcelChart(ExcelChart topChart, XmlNode chartNode) :
           base(topChart._drawings, topChart.TopNode, "xdr:graphicFrame/xdr:nvGraphicFramePr/xdr:cNvPr/@name")
       {
           UriChart = topChart.UriChart;
           Part = topChart.Part;
           ChartXml = topChart.ChartXml;
           _plotArea = topChart.PlotArea;
           _chartNode = chartNode;

           InitChartLoad(topChart._drawings, chartNode);
       }
       private void InitChartLoad(ExcelDrawings drawings, XmlNode chartNode)
       {
           //SetChartType();
           bool isPivot = false;
           Init(drawings, chartNode);
           _chartSeries = new ExcelChartSeries(this, drawings.NameSpaceManager, _chartNode, isPivot /*ChartXml.SelectSingleNode(_chartPath, drawings.NameSpaceManager)*/);
           LoadAxis();
       }

       private void Init(ExcelDrawings drawings, XmlNode chartNode)
       {
           //_chartXmlHelper = new XmlHelper(drawings.NameSpaceManager, chartNode);
           _chartXmlHelper = XmlHelperFactory.Create(drawings.NameSpaceManager, chartNode);
           _chartXmlHelper.SchemaNodeOrder = new string[] { "ofPieType", "title", "pivotFmt", "autoTitleDeleted", "view3D", "floor", "sideWall", "backWall", "plotArea", "wireframe", "barDir", "grouping", "scatterStyle", "radarStyle", "varyColors", "ser", "dLbls", "bubbleScale", "showNegBubbles", "dropLines", "upDownBars", "marker", "smooth", "shape", "legend", "plotVisOnly", "dispBlanksAs", "gapWidth", "showDLblsOverMax", "overlap", "bandFmts", "axId", "spPr", "printSettings" };
           WorkSheet = drawings.Worksheet;
       }
       #endregion
       #region "Private functions"
       private void SetTypeProperties()
       {
           /******* Grouping *******/
           if (IsTypeClustered())
           {
               Grouping = eGrouping.Clustered;
           }
           else if (
               IsTypeStacked())
           {
               Grouping = eGrouping.Stacked;
           }
           else if (
           IsTypePercentStacked())
           {
               Grouping = eGrouping.PercentStacked;
           }

           /***** 3D Perspective *****/
           if (IsType3D())             
           {
               View3D.RotY = 20;
               View3D.Perspective = 30;    //Default to 30
               if (IsTypePieDoughnut())
               {
                   View3D.RotX = 30;
               }
               else
               {
                   View3D.RotX = 15;
               }
           }
       }
       private void CreateNewChart(ExcelDrawings drawings, eChartType type, ExcelChart topChart)
       {
           if (topChart == null)
           {
               XmlElement graphFrame = TopNode.OwnerDocument.CreateElement("graphicFrame", ExcelPackage.schemaSheetDrawings);
               graphFrame.SetAttribute("macro", "");
               TopNode.AppendChild(graphFrame); 
               graphFrame.InnerXml = string.Format("<xdr:nvGraphicFramePr><xdr:cNvPr id=\"{0}\" name=\"Chart 1\" /><xdr:cNvGraphicFramePr /></xdr:nvGraphicFramePr><xdr:xfrm><a:off x=\"0\" y=\"0\" /> <a:ext cx=\"0\" cy=\"0\" /></xdr:xfrm><a:graphic><a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/chart\"><c:chart xmlns:c=\"http://schemas.openxmlformats.org/drawingml/2006/chart\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" r:id=\"rId1\" />   </a:graphicData>  </a:graphic>",_id);
               TopNode.AppendChild(TopNode.OwnerDocument.CreateElement("clientData", ExcelPackage.schemaSheetDrawings));

               var package = drawings.Worksheet._package.Package;
               UriChart = GetNewUri(package, "/xl/charts/chart{0}.xml");

               ChartXml = new XmlDocument();
               ChartXml.PreserveWhitespace = ExcelPackage.preserveWhitespace;
               LoadXmlSafe(ChartXml, ChartStartXml(type), Encoding.UTF8);

               // save it to the package
               Part = package.CreatePart(UriChart, "application/vnd.openxmlformats-officedocument.drawingml.chart+xml", _drawings._package.Compression);

               StreamWriter streamChart = new StreamWriter(Part.GetStream(FileMode.Create, FileAccess.Write));
               ChartXml.Save(streamChart);
               streamChart.Close();
               package.Flush();

               var chartRelation = drawings.Part.CreateRelationship(UriHelper.GetRelativeUri(drawings.UriDrawing, UriChart), Packaging.TargetMode.Internal, ExcelPackage.schemaRelationships + "/chart");
               graphFrame.SelectSingleNode("a:graphic/a:graphicData/c:chart", NameSpaceManager).Attributes["r:id"].Value = chartRelation.Id;
               package.Flush();
               _chartNode = ChartXml.SelectSingleNode(string.Format("c:chartSpace/c:chart/c:plotArea/{0}", GetChartNodeText()), NameSpaceManager);
           }
           else
           {
               ChartXml = topChart.ChartXml;
               Part = topChart.Part;
               _plotArea = topChart.PlotArea;
               UriChart = topChart.UriChart;
               _axis = topChart._axis;

               XmlNode preNode = _plotArea.ChartTypes[_plotArea.ChartTypes.Count - 1].ChartNode;
               _chartNode = ((XmlDocument)ChartXml).CreateElement(GetChartNodeText(), ExcelPackage.schemaChart);
               preNode.ParentNode.InsertAfter(_chartNode, preNode);
               if (topChart.Axis.Length == 0)
               {
                   AddAxis();
               }
               string serieXML = GetChartSerieStartXml(type, int.Parse(topChart.Axis[0].Id), int.Parse(topChart.Axis[1].Id), topChart.Axis.Length>2?int.Parse(topChart.Axis[2].Id) : -1);
               _chartNode.InnerXml = serieXML;
           }
            GetPositionSize();
       }
       private void LoadAxis()
       {
           XmlNodeList nl = _chartNode.SelectNodes("c:axId", NameSpaceManager);
           List<ExcelChartAxis> l = new List<ExcelChartAxis>();
           foreach (XmlNode node in nl)
           {
               string id = node.Attributes["val"].Value;
               var axNode = ChartXml.SelectNodes(rootPath + string.Format("/*/c:axId[@val=\"{0}\"]", id), NameSpaceManager);
               if (axNode != null && axNode.Count>1)
               {
                   foreach (XmlNode axn in axNode)
                   {
                       if (axn.ParentNode.LocalName.EndsWith("Ax"))
                       {
                           XmlNode axisNode = axNode[1].ParentNode;
                           ExcelChartAxis ax = new ExcelChartAxis(NameSpaceManager, axisNode);
                           l.Add(ax);
                       }
                   }
               }
           }
           _axis = l.ToArray();

           if(_axis.Length > 0) XAxis = _axis[0];
           if (_axis.Length > 1) YAxis = _axis[1];
       }
       //private void SetChartType()
       //{
       //    ChartType = 0;
       //    //_plotArea = new ExcelChartPlotArea(NameSpaceManager, ChartXml.SelectSingleNode("c:chartSpace/c:chart/c:plotArea", NameSpaceManager));
       //    int pos=0;
       //    foreach (XmlElement n in ChartXml.SelectSingleNode(rootPath, _drawings.NameSpaceManager).ChildNodes)
       //    {
       //        if (pos == 0)
       //        {
       //            ChartType = GetChartType(n.Name);
       //            if (ChartType != 0)
       //            {
       //                //_chartPath = rootPath + "/" + n.Name;
       //                PlotArea.ChartTypes.Add(this);
       //            }
       //        }
       //        else
       //        {
       //            var chartSerieType = GetChart(_drawings, TopNode/*, n*/);
       //            chartSerieType = GetChart(n, _drawings, TopNode, UriChart, Part, ChartXml, null, isPivot);
       //            PlotArea.ChartTypes.Add(chartSerieType);
       //            //var chartType = GetChartType(n.Name);
       //        }
       //        if (ChartType != 0)
       //        {
       //            pos++;
       //        }
       //    }
       //}
       internal virtual eChartType GetChartType(string name)
       {
           
           switch (name)
           {
               case "area3DChart":
                   if(Grouping==eGrouping.Stacked)
                   {
                       return eChartType.AreaStacked3D;
                   }
                   else if (Grouping == eGrouping.PercentStacked)
                   {
                       return eChartType.AreaStacked1003D;
                   }
                   else
                   {
                       return eChartType.Area3D;
                   }
               case "areaChart":
                   if (Grouping == eGrouping.Stacked)
                   {
                       return eChartType.AreaStacked;
                   }
                   else if (Grouping == eGrouping.PercentStacked)
                   {
                       return eChartType.AreaStacked100;
                   }
                   else
                   {
                       return eChartType.Area;
                   }
               case "doughnutChart":
                   return eChartType.Doughnut;
               case "pie3DChart":
                   return eChartType.Pie3D;
               case "pieChart":
                   return eChartType.Pie;
               case "radarChart":
                   return eChartType.Radar;
               case "scatterChart":
                   return eChartType.XYScatter;
               case "surface3DChart":
               case "surfaceChart":
                   return eChartType.Surface;
               case "stockChart":
                   return eChartType.StockHLC;
               default:
                   return 0;
           }           
       }
       #region "Xml init Functions"
       private string ChartStartXml(eChartType type)
       {
           StringBuilder xml=new StringBuilder();
           int axID=1;
           int xAxID=2;
           int serAxID = IsTypeSurface() ? 3 : -1;

           xml.Append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
           xml.AppendFormat("<c:chartSpace xmlns:c=\"{0}\" xmlns:a=\"{1}\" xmlns:r=\"{2}\">", ExcelPackage.schemaChart, ExcelPackage.schemaDrawings, ExcelPackage.schemaRelationships);       
           xml.Append("<c:chart>");
           xml.AppendFormat("{0}{1}<c:plotArea><c:layout/>",AddPerspectiveXml(type), AddSurfaceXml(type));

           string chartNodeText = GetChartNodeText();
           xml.AppendFormat("<{0}>", chartNodeText);
           xml.Append(GetChartSerieStartXml(type, axID, xAxID, serAxID));
           xml.AppendFormat("</{0}>", chartNodeText);

           //Axis
           if (!IsTypePieDoughnut())
           {
               if (IsTypeScatterBubble())
               {
                   xml.AppendFormat("<c:valAx><c:axId val=\"{0}\"/><c:scaling><c:orientation val=\"minMax\"/></c:scaling><c:delete val=\"0\"/><c:axPos val=\"b\"/><c:tickLblPos val=\"nextTo\"/><c:crossAx val=\"{1}\"/><c:crosses val=\"autoZero\"/></c:valAx>", axID, xAxID);
               }
               else
               {
                   xml.AppendFormat("<c:catAx><c:axId val=\"{0}\"/><c:scaling><c:orientation val=\"minMax\"/></c:scaling><c:delete val=\"0\"/><c:axPos val=\"b\"/><c:tickLblPos val=\"nextTo\"/><c:crossAx val=\"{1}\"/><c:crosses val=\"autoZero\"/><c:auto val=\"1\"/><c:lblAlgn val=\"ctr\"/><c:lblOffset val=\"100\"/></c:catAx>", axID, xAxID);
               }
               xml.AppendFormat("<c:valAx><c:axId val=\"{1}\"/><c:scaling><c:orientation val=\"minMax\"/></c:scaling><c:delete val=\"0\"/><c:axPos val=\"l\"/><c:majorGridlines/><c:tickLblPos val=\"nextTo\"/><c:crossAx val=\"{0}\"/><c:crosses val=\"autoZero\"/><c:crossBetween val=\"between\"/></c:valAx>", axID, xAxID);
               if (serAxID==3) //Sureface Chart
               {
                   xml.AppendFormat("<c:serAx><c:axId val=\"{0}\"/><c:scaling><c:orientation val=\"minMax\"/></c:scaling><c:delete val=\"0\"/><c:axPos val=\"b\"/><c:tickLblPos val=\"nextTo\"/><c:crossAx val=\"{1}\"/><c:crosses val=\"autoZero\"/></c:serAx>", serAxID, xAxID);
               }
           }

           xml.AppendFormat("</c:plotArea><c:legend><c:legendPos val=\"r\"/><c:layout/><c:overlay val=\"0\" /></c:legend><c:plotVisOnly val=\"1\"/></c:chart>", axID, xAxID);

           xml.Append("<c:printSettings><c:headerFooter/><c:pageMargins b=\"0.75\" l=\"0.7\" r=\"0.7\" t=\"0.75\" header=\"0.3\" footer=\"0.3\"/><c:pageSetup/></c:printSettings></c:chartSpace>");          
           return xml.ToString();
       }

       private string GetChartSerieStartXml(eChartType type, int axID, int xAxID, int serAxID)
       {
           StringBuilder xml = new StringBuilder();

           xml.Append(AddScatterType(type));
           xml.Append(AddRadarType(type));
           xml.Append(AddBarDir(type));
           xml.Append(AddGrouping());
           xml.Append(AddVaryColors());
           xml.Append(AddHasMarker(type));
           xml.Append(AddShape(type));
           xml.Append(AddFirstSliceAng(type));
           xml.Append(AddHoleSize(type));
           if (ChartType == eChartType.BarStacked100 || 
               ChartType == eChartType.BarStacked ||
               ChartType == eChartType.ColumnStacked ||
               ChartType == eChartType.ColumnStacked100)
           {
               xml.Append("<c:overlap val=\"100\"/>");
           }
           if (IsTypeSurface())
           {
               xml.Append("<c:bandFmts/>");
           }
           xml.Append(AddAxisId(axID, xAxID, serAxID));

           return xml.ToString();
       }
       private string AddAxisId(int axID,int xAxID, int serAxID)
       {
           if (!IsTypePieDoughnut())
           {
               if (IsTypeSurface())
               {                                      
                   return string.Format("<c:axId val=\"{0}\"/><c:axId val=\"{1}\"/><c:axId val=\"{2}\"/>", axID, xAxID, serAxID);
               }
               else
               {
                   return string.Format("<c:axId val=\"{0}\"/><c:axId val=\"{1}\"/>", axID, xAxID);
               }
           }
           else
           {
               return "";
           }
       }
       private string AddAxType()
       {
           switch(ChartType)
           {
               case eChartType.XYScatter:
               case eChartType.XYScatterLines:
               case eChartType.XYScatterLinesNoMarkers:
               case eChartType.XYScatterSmooth:
               case eChartType.XYScatterSmoothNoMarkers:
               case eChartType.Bubble:
               case eChartType.Bubble3DEffect:
                   return "valAx";
               default:
                   return "catAx";
           }
       }
       private string AddScatterType(eChartType type)
       {
           if (type == eChartType.XYScatter ||
               type == eChartType.XYScatterLines ||
               type == eChartType.XYScatterLinesNoMarkers ||
               type == eChartType.XYScatterSmooth ||
               type == eChartType.XYScatterSmoothNoMarkers)
           {
               return "<c:scatterStyle val=\"\" />";
           }
           else
           {
               return "";
           }
       }
       private string AddRadarType(eChartType type)
       {
           if (type == eChartType.Radar ||
               type == eChartType.RadarFilled||
               type == eChartType.RadarMarkers)
           {
               return "<c:radarStyle val=\"\" />";
           }
           else
           {
               return "";
           }
       }
       private string AddGrouping()
       {
           //IsTypeClustered() || IsTypePercentStacked() || IsTypeStacked() || 
           if(IsTypeShape() || IsTypeLine())
           {
               return "<c:grouping val=\"standard\"/>";
           }
           else
           {
               return "";
           }
       }
       private string AddHoleSize(eChartType type)
       {
           if (type == eChartType.Doughnut ||
               type == eChartType.DoughnutExploded)
           {
               return "<c:holeSize val=\"50\" />";
           }
           else
           {
               return "";
           }
       }
       private string AddFirstSliceAng(eChartType type)
       {
           if (type == eChartType.Doughnut ||
               type == eChartType.DoughnutExploded)
           {
               return "<c:firstSliceAng val=\"0\" />";
           }
           else
           {
               return "";
           }
       }
       private string AddVaryColors()
       {
           if (IsTypePieDoughnut())
            {
               return "<c:varyColors val=\"1\" />";
           }
           else
           {
               return "<c:varyColors val=\"0\" />";
           }
       }
       private string AddHasMarker(eChartType type)
       {
           if (type == eChartType.LineMarkers ||
               type == eChartType.LineMarkersStacked ||
               type == eChartType.LineMarkersStacked100 /*||
               type == eChartType.XYScatterLines ||
               type == eChartType.XYScatterSmooth*/)
           {
               return "<c:marker val=\"1\"/>";
           }
           else
           {
               return "";
           }
       }
       private string AddShape(eChartType type)
       {
           if (IsTypeShape())
           {
               return "<c:shape val=\"box\" />";
           }
           else
           {
               return "";
           }
       }
       private string AddBarDir(eChartType type)
       {
           if (IsTypeShape())
            {
                return "<c:barDir val=\"col\" />";
            }
            else
            {
                return "";
            }
       }
       private string AddPerspectiveXml(eChartType type)
        {
 	        //Add for 3D sharts
            if (IsType3D())
            {
                return "<c:view3D><c:perspective val=\"30\" /></c:view3D>";
            }
            else
            {
                return "";
            }
        }
       private string AddSurfaceXml(eChartType type)
       {
           if (IsTypeSurface())
           {
               return AddSurfacePart("floor") + AddSurfacePart("sideWall") + AddSurfacePart("backWall");
           }
           else
           {
               return "";
           }
       }

       private string AddSurfacePart(string name)
       {
           return string.Format("<c:{0}><c:thickness val=\"0\"/><c:spPr><a:noFill/><a:ln><a:noFill/></a:ln><a:effectLst/><a:sp3d/></c:spPr></c:{0}>", name);
       }
       #endregion
       #endregion
       #region "Chart type functions
       internal static bool IsType3D(eChartType chartType)
        {
            return chartType == eChartType.Area3D ||
                            chartType == eChartType.AreaStacked3D ||
                            chartType == eChartType.AreaStacked1003D ||
                            chartType == eChartType.BarClustered3D ||
                            chartType == eChartType.BarStacked3D ||
                            chartType == eChartType.BarStacked1003D ||
                            chartType == eChartType.Column3D ||
                            chartType == eChartType.ColumnClustered3D ||
                            chartType == eChartType.ColumnStacked3D ||
                            chartType == eChartType.ColumnStacked1003D ||
                            chartType == eChartType.Line3D ||
                            chartType == eChartType.Pie3D ||
                            chartType == eChartType.PieExploded3D ||
                            chartType == eChartType.ConeBarClustered ||
                            chartType == eChartType.ConeBarStacked ||
                            chartType == eChartType.ConeBarStacked100 ||
                            chartType == eChartType.ConeCol ||
                            chartType == eChartType.ConeColClustered ||
                            chartType == eChartType.ConeColStacked ||
                            chartType == eChartType.ConeColStacked100 ||
                            chartType == eChartType.CylinderBarClustered ||
                            chartType == eChartType.CylinderBarStacked ||
                            chartType == eChartType.CylinderBarStacked100 ||
                            chartType == eChartType.CylinderCol ||
                            chartType == eChartType.CylinderColClustered ||
                            chartType == eChartType.CylinderColStacked ||
                            chartType == eChartType.CylinderColStacked100 ||
                            chartType == eChartType.PyramidBarClustered ||
                            chartType == eChartType.PyramidBarStacked ||
                            chartType == eChartType.PyramidBarStacked100 ||
                            chartType == eChartType.PyramidCol ||
                            chartType == eChartType.PyramidColClustered ||
                            chartType == eChartType.PyramidColStacked ||
                            chartType == eChartType.PyramidColStacked100 ||
                            chartType == eChartType.Surface ||
                            chartType == eChartType.SurfaceTopView ||
                            chartType == eChartType.SurfaceTopViewWireframe ||
                            chartType == eChartType.SurfaceWireframe;
        }
       internal protected bool IsType3D()
       {
            return IsType3D(ChartType);
       }
        protected bool IsTypeLine()
       {
           return ChartType == eChartType.Line ||
                   ChartType == eChartType.LineMarkers ||
                   ChartType == eChartType.LineMarkersStacked100 ||
                   ChartType == eChartType.LineStacked ||
                   ChartType == eChartType.LineStacked100 ||
                   ChartType == eChartType.Line3D;
       }
       protected bool IsTypeScatterBubble()
       {
           return ChartType == eChartType.XYScatter ||
                   ChartType == eChartType.XYScatterLines ||
                   ChartType == eChartType.XYScatterLinesNoMarkers ||
                   ChartType == eChartType.XYScatterSmooth ||
                   ChartType == eChartType.XYScatterSmoothNoMarkers ||
                   ChartType == eChartType.Bubble ||
                   ChartType == eChartType.Bubble3DEffect;
       }
        protected bool IsTypeSurface()
        {
            return ChartType == eChartType.Surface ||
                   ChartType == eChartType.SurfaceTopView ||
                   ChartType == eChartType.SurfaceTopViewWireframe ||
                   ChartType == eChartType.SurfaceWireframe;
        }
       protected bool IsTypeShape()
       {
            return ChartType == eChartType.BarClustered3D ||
                    ChartType == eChartType.BarStacked3D ||
                    ChartType == eChartType.BarStacked1003D ||
                    ChartType == eChartType.BarClustered3D ||
                    ChartType == eChartType.BarStacked3D ||
                    ChartType == eChartType.BarStacked1003D ||
                    ChartType == eChartType.Column3D ||
                    ChartType == eChartType.ColumnClustered3D ||
                    ChartType == eChartType.ColumnStacked3D ||
                    ChartType == eChartType.ColumnStacked1003D ||
                //ChartType == eChartType.3DPie ||
                //ChartType == eChartType.3DPieExploded ||
                    //ChartType == eChartType.Bubble3DEffect ||
                    ChartType == eChartType.ConeBarClustered ||
                    ChartType == eChartType.ConeBarStacked ||
                    ChartType == eChartType.ConeBarStacked100 ||
                    ChartType == eChartType.ConeCol ||
                    ChartType == eChartType.ConeColClustered ||
                    ChartType == eChartType.ConeColStacked ||
                    ChartType == eChartType.ConeColStacked100 ||
                    ChartType == eChartType.CylinderBarClustered ||
                    ChartType == eChartType.CylinderBarStacked ||
                    ChartType == eChartType.CylinderBarStacked100 ||
                    ChartType == eChartType.CylinderCol ||
                    ChartType == eChartType.CylinderColClustered ||
                    ChartType == eChartType.CylinderColStacked ||
                    ChartType == eChartType.CylinderColStacked100 ||
                    ChartType == eChartType.PyramidBarClustered ||
                    ChartType == eChartType.PyramidBarStacked ||
                    ChartType == eChartType.PyramidBarStacked100 ||
                    ChartType == eChartType.PyramidCol ||
                    ChartType == eChartType.PyramidColClustered ||
                    ChartType == eChartType.PyramidColStacked ||
                    ChartType == eChartType.PyramidColStacked100; //||
                    //ChartType == eChartType.Doughnut ||
                    //ChartType == eChartType.DoughnutExploded;
        }
        protected internal bool IsTypePercentStacked()
        {
            return ChartType == eChartType.AreaStacked100 ||
                           ChartType == eChartType.BarStacked100 ||
                           ChartType == eChartType.BarStacked1003D ||
                           ChartType == eChartType.ColumnStacked100 ||
                           ChartType == eChartType.ColumnStacked1003D ||
                           ChartType == eChartType.ConeBarStacked100 ||
                           ChartType == eChartType.ConeColStacked100 ||
                           ChartType == eChartType.CylinderBarStacked100 ||
                           ChartType == eChartType.CylinderColStacked ||
                           ChartType == eChartType.LineMarkersStacked100 ||
                           ChartType == eChartType.LineStacked100 ||
                           ChartType == eChartType.PyramidBarStacked100 ||
                           ChartType == eChartType.PyramidColStacked100;
        }
        protected internal bool IsTypeStacked()
        {
            return ChartType == eChartType.AreaStacked ||
                           ChartType == eChartType.AreaStacked3D ||
                           ChartType == eChartType.BarStacked ||
                           ChartType == eChartType.BarStacked3D ||
                           ChartType == eChartType.ColumnStacked3D ||
                           ChartType == eChartType.ColumnStacked ||
                           ChartType == eChartType.ConeBarStacked ||
                           ChartType == eChartType.ConeColStacked ||
                           ChartType == eChartType.CylinderBarStacked ||
                           ChartType == eChartType.CylinderColStacked ||
                           ChartType == eChartType.LineMarkersStacked ||
                           ChartType == eChartType.LineStacked ||
                           ChartType == eChartType.PyramidBarStacked ||
                           ChartType == eChartType.PyramidColStacked;
        }
        protected bool IsTypeClustered()
        {
            return ChartType == eChartType.BarClustered ||
                           ChartType == eChartType.BarClustered3D ||
                           ChartType == eChartType.ColumnClustered3D ||
                           ChartType == eChartType.ColumnClustered ||
                           ChartType == eChartType.ConeBarClustered ||
                           ChartType == eChartType.ConeColClustered ||
                           ChartType == eChartType.CylinderBarClustered ||
                           ChartType == eChartType.CylinderColClustered ||
                           ChartType == eChartType.PyramidBarClustered ||
                           ChartType == eChartType.PyramidColClustered;
        }
        protected internal bool IsTypePieDoughnut()
        {
            return ChartType == eChartType.Pie ||
                           ChartType == eChartType.PieExploded ||
                           ChartType == eChartType.PieOfPie ||
                           ChartType == eChartType.Pie3D ||
                           ChartType == eChartType.PieExploded3D ||
                           ChartType == eChartType.BarOfPie ||
                           ChartType == eChartType.Doughnut ||
                           ChartType == eChartType.DoughnutExploded;
        }
        #endregion
       /// <summary>
       /// Get the name of the chart node
       /// </summary>
       /// <returns>The name</returns>
        protected string GetChartNodeText()
        {
            switch (ChartType)
            {
                case eChartType.Area3D:
                case eChartType.AreaStacked3D:
                case eChartType.AreaStacked1003D:
                    return "c:area3DChart";
                case eChartType.Area:
                case eChartType.AreaStacked:
                case eChartType.AreaStacked100:
                    return "c:areaChart";
                case eChartType.BarClustered:
                case eChartType.BarStacked:
                case eChartType.BarStacked100:
                case eChartType.ColumnClustered:
                case eChartType.ColumnStacked:
                case eChartType.ColumnStacked100:
                    return "c:barChart";
                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:
                    return "c:bar3DChart";
                case eChartType.Bubble:
                case eChartType.Bubble3DEffect:
                    return "c:bubbleChart";
                case eChartType.Doughnut:
                case eChartType.DoughnutExploded:
                    return "c:doughnutChart";
                case eChartType.Line:
                case eChartType.LineMarkers:
                case eChartType.LineMarkersStacked:
                case eChartType.LineMarkersStacked100:
                case eChartType.LineStacked:
                case eChartType.LineStacked100:
                    return "c:lineChart";
                case eChartType.Line3D:
                    return "c:line3DChart";
                case eChartType.Pie:
                case eChartType.PieExploded:
                    return "c:pieChart";
                case eChartType.BarOfPie:
                case eChartType.PieOfPie:
                    return "c:ofPieChart";
                case eChartType.Pie3D:
                case eChartType.PieExploded3D:
                    return "c:pie3DChart";
                case eChartType.Radar:
                case eChartType.RadarFilled:
                case eChartType.RadarMarkers:
                    return "c:radarChart";
                case eChartType.XYScatter:
                case eChartType.XYScatterLines:
                case eChartType.XYScatterLinesNoMarkers:
                case eChartType.XYScatterSmooth:
                case eChartType.XYScatterSmoothNoMarkers:
                    return "c:scatterChart";
                case eChartType.Surface:
                case eChartType.SurfaceWireframe:
                    return "c:surface3DChart";
                case eChartType.SurfaceTopView:
                case eChartType.SurfaceTopViewWireframe:
                    return "c:surfaceChart";
                case eChartType.StockHLC:
                    return "c:stockChart";
                default:
                    throw(new NotImplementedException("Chart type not implemented"));
            }
        }
        /// <summary>
        /// Add a secondary axis
        /// </summary>
        internal void AddAxis()
        {
            XmlElement catAx = ChartXml.CreateElement(string.Format("c:{0}",AddAxType()), ExcelPackage.schemaChart);
            int axID;
            if (_axis.Length == 0)
            {
                _plotArea.TopNode.AppendChild(catAx);
                axID = 1;
            }
            else
            {
                _axis[0].TopNode.ParentNode.InsertAfter(catAx, _axis[_axis.Length-1].TopNode);
                axID = int.Parse(_axis[0].Id) < int.Parse(_axis[1].Id) ? int.Parse(_axis[1].Id) + 1 : int.Parse(_axis[0].Id) + 1;
            }


            XmlElement valAx = ChartXml.CreateElement("c:valAx", ExcelPackage.schemaChart);
            catAx.ParentNode.InsertAfter(valAx, catAx);

            if (_axis.Length == 0)
            {
                catAx.InnerXml = string.Format("<c:axId val=\"{0}\"/><c:scaling><c:orientation val=\"minMax\"/></c:scaling><c:delete val=\"0\" /><c:axPos val=\"b\"/><c:tickLblPos val=\"nextTo\"/><c:crossAx val=\"{1}\"/><c:crosses val=\"autoZero\"/><c:auto val=\"1\"/><c:lblAlgn val=\"ctr\"/><c:lblOffset val=\"100\"/>", axID, axID + 1);
                valAx.InnerXml = string.Format("<c:axId val=\"{1}\"/><c:scaling><c:orientation val=\"minMax\"/></c:scaling><c:delete val=\"0\" /><c:axPos val=\"l\"/><c:majorGridlines/><c:tickLblPos val=\"nextTo\"/><c:crossAx val=\"{0}\"/><c:crosses val=\"autoZero\"/><c:crossBetween val=\"between\"/>", axID, axID + 1);
            }
            else
            {
                catAx.InnerXml = string.Format("<c:axId val=\"{0}\"/><c:scaling><c:orientation val=\"minMax\"/></c:scaling><c:delete val=\"1\" /><c:axPos val=\"b\"/><c:tickLblPos val=\"none\"/><c:crossAx val=\"{1}\"/><c:crosses val=\"autoZero\"/>", axID, axID + 1);
                valAx.InnerXml = string.Format("<c:axId val=\"{0}\"/><c:scaling><c:orientation val=\"minMax\"/></c:scaling><c:delete val=\"0\" /><c:axPos val=\"r\"/><c:tickLblPos val=\"nextTo\"/><c:crossAx val=\"{1}\"/><c:crosses val=\"max\"/><c:crossBetween val=\"between\"/>", axID + 1, axID);
            }

            if (_axis.Length == 0)
            {
                _axis = new ExcelChartAxis[2];
            }
            else
            {
                ExcelChartAxis[] newAxis = new ExcelChartAxis[_axis.Length + 2];
                Array.Copy(_axis, newAxis, _axis.Length);
                _axis = newAxis;
            }

            _axis[_axis.Length - 2] = new ExcelChartAxis(NameSpaceManager, catAx);
            _axis[_axis.Length - 1] = new ExcelChartAxis(NameSpaceManager, valAx);
            foreach (var chart in _plotArea.ChartTypes)
            {
                chart._axis = _axis;
            }
        }
        internal void RemoveSecondaryAxis()
        {
            throw (new NotImplementedException("Not yet implemented"));
        }
        #region "Properties"
        /// <summary>
        /// Reference to the worksheet
        /// </summary>
        public ExcelWorksheet WorkSheet { get; internal set; }
        /// <summary>
        /// The chart xml document
        /// </summary>
        public XmlDocument ChartXml { get; internal set; }
        /// <summary>
        /// Type of chart
        /// </summary>
        public eChartType ChartType { get; internal set; }
        internal protected XmlNode _chartNode = null;
        internal XmlNode ChartNode
        {
            get
            {
                return _chartNode;
            }
        }
        /// <summary>
        /// Titel of the chart
        /// </summary>
        public ExcelChartTitle Title
        {
            get
            {
                if (_title == null)
                {
                    _title = new ExcelChartTitle(NameSpaceManager, ChartXml.SelectSingleNode("c:chartSpace/c:chart", NameSpaceManager));
                }
                return _title;
            }
        }
        /// <summary>
        /// Chart series
        /// </summary>
        public virtual ExcelChartSeries Series
        {
            get
            {
                return _chartSeries;
            }
        }
        /// <summary>
        /// An array containg all axis of all Charttypes
        /// </summary>
        public ExcelChartAxis[] Axis
        {
            get
            {
                return _axis;
            }
        }
        /// <summary>
        /// The XAxis
        /// </summary>
        public ExcelChartAxis XAxis
        {
            get;
            private set;
        }
        /// <summary>
        /// The YAxis
        /// </summary>
        public ExcelChartAxis YAxis
        {
            get;
            private set;
        }
        bool _secondaryAxis=false;
        /// <summary>
        /// If true the charttype will use the secondary axis.
        /// The chart must contain a least one other charttype that uses the primary axis.
        /// </summary>
        public bool UseSecondaryAxis
        {
            get
            {
                return _secondaryAxis;
            }
            set
            {
                 if (_secondaryAxis != value)
                {
                    if (value)
                    {
                        if (IsTypePieDoughnut())
                        {
                            throw (new Exception("Pie charts do not support axis"));
                        }
                        else if (HasPrimaryAxis() == false)
                        {
                            throw (new Exception("Can't set to secondary axis when no serie uses the primary axis"));
                        }
                        if (Axis.Length == 2)
                        {
                            AddAxis();
                        }
                        var nl = ChartNode.SelectNodes("c:axId", NameSpaceManager);
                        nl[0].Attributes["val"].Value = Axis[2].Id;
                        nl[1].Attributes["val"].Value = Axis[3].Id;
                        XAxis = Axis[2];
                        YAxis = Axis[3];
                    }
                    else
                    {
                        var nl = ChartNode.SelectNodes("c:axId", NameSpaceManager);
                        nl[0].Attributes["val"].Value = Axis[0].Id;
                        nl[1].Attributes["val"].Value = Axis[1].Id;
                        XAxis = Axis[0];
                        YAxis = Axis[1];
                    }
                    _secondaryAxis = value;
                }
            }
        }
        /// <summary>
        /// The build-in chart styles. 
        /// </summary>
        public eChartStyle Style
        {
            get
            {
                XmlNode node = ChartXml.SelectSingleNode("c:chartSpace/c:style/@val", NameSpaceManager);
                if (node == null)
                {
                    return eChartStyle.None;
                }
                else
                {
                    int v;
                    if (int.TryParse(node.Value, out v))
                    {
                        return (eChartStyle)v;
                    }
                    else
                    {
                        return eChartStyle.None;
                    }
                }

            }
            set
            {
                if (value == eChartStyle.None)
                {
                    XmlElement element = ChartXml.SelectSingleNode("c:chartSpace/c:style", NameSpaceManager) as XmlElement;
                    if (element != null)
                    {
                        element.ParentNode.RemoveChild(element);
                    }
                }
                else
                {
                    XmlElement element = ChartXml.CreateElement("c:style", ExcelPackage.schemaChart);
                    element.SetAttribute("val", ((int)value).ToString());
                    XmlElement parent = ChartXml.SelectSingleNode("c:chartSpace", NameSpaceManager) as XmlElement;
                    parent.InsertBefore(element, parent.SelectSingleNode("c:chart", NameSpaceManager));
                }
            }
        }
        const string _plotVisibleOnlyPath="../../c:plotVisOnly/@val";
        /// <summary>
        /// Show data in hidden rows and columns
        /// </summary>
        public bool ShowHiddenData
        {
            get
            {
                //!!Inverted value!!
                return !_chartXmlHelper.GetXmlNodeBool(_plotVisibleOnlyPath);
            }
            set
            {
                //!!Inverted value!!
                _chartXmlHelper.SetXmlNodeBool(_plotVisibleOnlyPath, !value);
            }
        }
        const string _displayBlanksAsPath = "../../c:dispBlanksAs/@val";
        /// <summary>
        /// Specifies the possible ways to display blanks
        /// </summary>
        public eDisplayBlanksAs DisplayBlanksAs
        {
            get
            {
                string v=_chartXmlHelper.GetXmlNodeString(_displayBlanksAsPath);
                if (string.IsNullOrEmpty(v))
                {
                    return eDisplayBlanksAs.Zero; //Issue 14715 Changed in Office 2010-?
                }
                else
                {
                    return (eDisplayBlanksAs)Enum.Parse(typeof(eDisplayBlanksAs), v, true);
                }
            }
            set
            {
                _chartSeries.SetXmlNodeString(_displayBlanksAsPath, value.ToString().ToLower(CultureInfo.InvariantCulture));
            }
        }
        const string _showDLblsOverMax = "../../c:showDLblsOverMax/@val";
        /// <summary>
        /// Specifies data labels over the maximum of the chart shall be shown
        /// </summary>
        public bool ShowDataLabelsOverMaximum
        {
            get
            {
                return _chartXmlHelper.GetXmlNodeBool(_showDLblsOverMax, true);
            }
            set
            {
                _chartXmlHelper.SetXmlNodeBool(_showDLblsOverMax,value, true);
            }
        }
        private bool HasPrimaryAxis()
        {
            if (_plotArea.ChartTypes.Count == 1)
            {
                return false;
            }
            foreach (var chart in _plotArea.ChartTypes)
            {
                if (chart != this)
                {
                    if (chart.UseSecondaryAxis == false && chart.IsTypePieDoughnut()==false)
                    {
                        return true;
                    }
                }
            }
            return false;
        }
        ///// <summary>
        ///// Sets position of the axis of a chart-serie
        ///// </summary>
        ///// <param name="XAxis">Left or Right</param>
        ///// <param name="YAxis">Top or Bottom</param>
        //internal void SetAxis(eXAxisPosition XAxis, eYAxisPosition YAxis)
        //{
        //    bool xAxisExists = false, yAxisExists = false;
        //    foreach (var axis in _axis)
        //    {
        //        if (axis.AxisPosition == (eAxisPosition)XAxis)
        //        {
        //            //Found
        //            xAxisExists=true;
        //            if (axis != this.XAxis)
        //            {
        //                CheckRemoveAxis(this.XAxis);
        //                this.XAxis = axis;
        //            }
        //        }
        //        else if (axis.AxisPosition == (eAxisPosition)YAxis)
        //        {
        //            yAxisExists = true;
        //            if (axis != this.YAxis)
        //            {
        //                CheckRemoveAxis(this.YAxis);
        //                this.YAxis = axis;
        //            }
        //        }
        //    }

        //    if (!xAxisExists)
        //    {
        //        if (ExistsAxis(this.XAxis))
        //        {
        //            AddAxis((eAxisPosition)XAxis);
        //            this.XAxis = Axis[Axis.Length - 1];
        //        }
        //        else
        //        {
        //            this.XAxis.AxisPosition = (eAxisPosition)XAxis;
        //        }
        //    }
        //    if (!yAxisExists)
        //    {
        //        if (ExistsAxis(this.XAxis))
        //        {
        //            AddAxis((eAxisPosition)YAxis);
        //            this.YAxis = Axis[Axis.Length - 1];
        //        }
        //        else
        //        {
        //            this.YAxis.AxisPosition = (eAxisPosition)YAxis;
        //        }
        //    }
        //}

        /// <summary>
        /// Remove all axis that are not used any more
        /// </summary>
        /// <param name="excelChartAxis"></param>
        private void CheckRemoveAxis(ExcelChartAxis excelChartAxis)
        {
            if (ExistsAxis(excelChartAxis))
            {
                //Remove the axis
                ExcelChartAxis[] newAxis = new ExcelChartAxis[Axis.Length - 1];
                int pos = 0;
                foreach (var ax in Axis)
                {
                    if (ax != excelChartAxis)
                    {
                        newAxis[pos] = ax;
                    }
                }

                //Update all charttypes.
                foreach (ExcelChart chartType in _plotArea.ChartTypes)
                {
                    chartType._axis = newAxis;
                }
            }
        }

        private bool ExistsAxis(ExcelChartAxis excelChartAxis)
        {
            foreach (ExcelChart chartType in _plotArea.ChartTypes)
            {
                if (chartType != this)
                {
                    if (chartType.XAxis.AxisPosition == excelChartAxis.AxisPosition ||
                       chartType.YAxis.AxisPosition == excelChartAxis.AxisPosition)
                    {
                        //The axis exists
                        return true;
                    }
                }
            }
            return false;
        }
        ExcelChartPlotArea _plotArea = null;
        /// <summary>
        /// Plotarea
        /// </summary>
        public ExcelChartPlotArea PlotArea
        {
            get
            {
                if (_plotArea == null)
                {
                    _plotArea = new ExcelChartPlotArea(NameSpaceManager, ChartXml.SelectSingleNode("c:chartSpace/c:chart/c:plotArea", NameSpaceManager), this); 
                }
                return _plotArea;
            }
        }
        ExcelChartLegend _legend = null;
        /// <summary>
        /// Legend
        /// </summary>
        public ExcelChartLegend Legend
        {
            get
            {
                if (_legend == null)
                {
                    _legend = new ExcelChartLegend(NameSpaceManager, ChartXml.SelectSingleNode("c:chartSpace/c:chart/c:legend", NameSpaceManager), this);
                }
                return _legend;
            }

        }
        ExcelDrawingBorder _border = null;
        /// <summary>
        /// Border
        /// </summary>
        public ExcelDrawingBorder Border
        {
            get
            {
                if (_border == null)
                {
                    _border = new ExcelDrawingBorder(NameSpaceManager, ChartXml.SelectSingleNode("c:chartSpace",NameSpaceManager), "c:spPr/a:ln"); 
                }
                return _border;
            }
        }
        ExcelDrawingFill _fill = null;
        /// <summary>
        /// Fill
        /// </summary>
        public ExcelDrawingFill Fill
        {
            get
            {
                if (_fill == null)
                {
                    _fill = new ExcelDrawingFill(NameSpaceManager, ChartXml.SelectSingleNode("c:chartSpace", NameSpaceManager), "c:spPr");
                }
                return _fill;
            }
        }
        /// <summary>
        /// 3D-settings
        /// </summary>
        public ExcelView3D View3D
        {
            get
            {
                if (IsType3D())
                {
                    return new ExcelView3D(NameSpaceManager, ChartXml.SelectSingleNode("//c:view3D", NameSpaceManager));
                }
                else
                {
                    throw (new Exception("Charttype does not support 3D"));
                }

            }
        }
        //string _groupingPath = "c:chartSpace/c:chart/c:plotArea/{0}/c:grouping/@val";
        string _groupingPath = "c:grouping/@val";
        public eGrouping Grouping
        {
            get
            {
                return GetGroupingEnum(_chartXmlHelper.GetXmlNodeString(_groupingPath));
            }
            internal set
            {
                _chartXmlHelper.SetXmlNodeString(_groupingPath, GetGroupingText(value));
            }
        }
        //string _varyColorsPath = "c:chartSpace/c:chart/c:plotArea/{0}/c:varyColors/@val";
        string _varyColorsPath = "c:varyColors/@val";
        /// <summary>
        /// If the chart has only one serie this varies the colors for each point.
        /// </summary>
        public bool VaryColors
        {
            get
            {
                return _chartXmlHelper.GetXmlNodeBool(_varyColorsPath);
            }
            set
            {
                if (value)
                {
                    _chartXmlHelper.SetXmlNodeString(_varyColorsPath, "1");
                }
                else
                {
                    _chartXmlHelper.SetXmlNodeString(_varyColorsPath, "0");
                }
            }
        }
        internal Packaging.ZipPackagePart Part { get; set; }
        /// <summary>
        /// Package internal URI
        /// </summary>
        internal Uri UriChart { get; set; }
        internal new string Id
        {
            get { return ""; }
        }
        ExcelChartTitle _title = null;
        #endregion
       #region "Grouping Enum Translation"
        private string GetGroupingText(eGrouping grouping)
       {
           switch (grouping)
           {
               case eGrouping.Clustered:
                   return "clustered";
               case eGrouping.Stacked:
                   return "stacked";
               case eGrouping.PercentStacked:
                   return "percentStacked";
               default:
                   return "standard";

           }
       }
       private eGrouping GetGroupingEnum(string grouping)
       {
           switch (grouping)
           {
               case "stacked":
                   return eGrouping.Stacked;
               case "percentStacked":
                   return eGrouping.PercentStacked;
               default: //"clustered":               
                   return eGrouping.Clustered;
           }         
       }
       #endregion
       internal static ExcelChart GetChart(ExcelDrawings drawings, XmlNode node/*, XmlNode chartTypeNode*/)
       {
           XmlNode chartNode = node.SelectSingleNode("xdr:graphicFrame/a:graphic/a:graphicData/c:chart", drawings.NameSpaceManager);
           if (chartNode != null)
           {
               var drawingRelation = drawings.Part.GetRelationship(chartNode.Attributes["r:id"].Value);
               var uriChart = UriHelper.ResolvePartUri(drawings.UriDrawing, drawingRelation.TargetUri);

               var part = drawings.Part.Package.GetPart(uriChart);
               var chartXml = new XmlDocument();
               LoadXmlSafe(chartXml, part.GetStream()); 

               ExcelChart topChart = null;
               foreach (XmlElement n in chartXml.SelectSingleNode(rootPath, drawings.NameSpaceManager).ChildNodes)
                {
                    if (topChart == null)
                    {
                        topChart = GetChart(n, drawings, node, uriChart, part, chartXml, null);
                        if(topChart!=null)
                        {
                            topChart.PlotArea.ChartTypes.Add(topChart);
                        }
                    }
                    else
                    {
                        var subChart = GetChart(n, null, null, null, null, null, topChart);
                        if (subChart != null)
                        {
                            topChart.PlotArea.ChartTypes.Add(subChart);
                        }
                    }
                }               
                return topChart;
           }
           else
           {
               return null;
           }           
       }
       internal static ExcelChart GetChart(XmlElement chartNode, ExcelDrawings drawings, XmlNode node,  Uri uriChart, Packaging.ZipPackagePart part, XmlDocument chartXml, ExcelChart topChart)
       {
           switch (chartNode.LocalName)
           {
               case "area3DChart":
               case "areaChart":
               case "stockChart":
                   if (topChart == null)
                   {
                       return new ExcelChart(drawings, node, uriChart, part, chartXml, chartNode);
                   }
                   else
                   {
                       return new ExcelChart(topChart, chartNode);
                   }
               case "surface3DChart":
               case "surfaceChart":
                   if (topChart == null)
                   {
                       return new ExcelSurfaceChart(drawings, node, uriChart, part, chartXml, chartNode);
                   }
                   else
                   {
                       return new ExcelSurfaceChart(topChart, chartNode);
                   }
               case "radarChart":
                   if (topChart == null)
                   {
                       return new ExcelRadarChart(drawings, node, uriChart, part, chartXml, chartNode);
                   }
                   else
                   {
                       return new ExcelRadarChart(topChart, chartNode);
                   }
               case "bubbleChart":
                   if (topChart == null)
                   {
                       return new ExcelBubbleChart(drawings, node, uriChart, part, chartXml, chartNode);
                   }
                   else
                   {
                       return new ExcelBubbleChart(topChart, chartNode);
                   }
               case "barChart":
               case "bar3DChart":
                   if (topChart == null)
                   {
                       return new ExcelBarChart(drawings, node, uriChart, part, chartXml, chartNode);
                   }
                   else
                   {
                       return new ExcelBarChart(topChart, chartNode);
                   }
               case "doughnutChart":
                   if (topChart == null)
                   {
                       return new ExcelDoughnutChart(drawings, node, uriChart, part, chartXml, chartNode);
                   }
                   else
                   {
                       return new ExcelDoughnutChart(topChart, chartNode);
                   }
               case "pie3DChart":
               case "pieChart":
                   if (topChart == null)
                   {
                       return new ExcelPieChart(drawings, node, uriChart, part, chartXml, chartNode);
                   }
                   else
                   {
                       return new ExcelPieChart(topChart, chartNode);
                   }
           case "ofPieChart":
                   if (topChart == null)
                   {
                       return new ExcelOfPieChart(drawings, node, uriChart, part, chartXml, chartNode);
                   }
                   else
                   {
                       return new ExcelBarChart(topChart, chartNode);
                   }
               case "lineChart":
               case "line3DChart":
                   if (topChart == null)
                   {
                       return new ExcelLineChart(drawings, node, uriChart, part, chartXml, chartNode);
                   }
                   else
                   {
                       return new ExcelLineChart(topChart, chartNode);
                   }
               case "scatterChart":
                   if (topChart == null)
                   {
                       return new ExcelScatterChart(drawings, node, uriChart, part, chartXml, chartNode);
                   }
                   else
                   {
                       return new ExcelScatterChart(topChart, chartNode);
                   }
               default:
                   return null;
           }       
       }
       internal static ExcelChart GetNewChart(ExcelDrawings drawings, XmlNode drawNode, eChartType chartType, ExcelChart topChart, ExcelPivotTable PivotTableSource)
       {
            switch(chartType)
            {
                case eChartType.Pie:
                case eChartType.PieExploded:
                case eChartType.Pie3D:
                case eChartType.PieExploded3D:
                    return new ExcelPieChart(drawings, drawNode, chartType, topChart, PivotTableSource);
                case eChartType.BarOfPie:
                case eChartType.PieOfPie:
                    return new ExcelOfPieChart(drawings, drawNode, chartType, topChart, PivotTableSource);
                case eChartType.Doughnut:
                case eChartType.DoughnutExploded:
                    return new ExcelDoughnutChart(drawings, drawNode, chartType, topChart, PivotTableSource);
                case eChartType.BarClustered:
                case eChartType.BarStacked:
                case eChartType.BarStacked100:
                case eChartType.BarClustered3D:
                case eChartType.BarStacked3D:
                case eChartType.BarStacked1003D:
                case eChartType.ConeBarClustered:
                case eChartType.ConeBarStacked:
                case eChartType.ConeBarStacked100:
                case eChartType.CylinderBarClustered:
                case eChartType.CylinderBarStacked:
                case eChartType.CylinderBarStacked100:
                case eChartType.PyramidBarClustered:
                case eChartType.PyramidBarStacked:
                case eChartType.PyramidBarStacked100:
                case eChartType.ColumnClustered:
                case eChartType.ColumnStacked:
                case eChartType.ColumnStacked100:
                case eChartType.Column3D:
                case eChartType.ColumnClustered3D:
                case eChartType.ColumnStacked3D:
                case eChartType.ColumnStacked1003D:
                case eChartType.ConeCol:
                case eChartType.ConeColClustered:
                case eChartType.ConeColStacked:
                case eChartType.ConeColStacked100:
                case eChartType.CylinderCol:
                case eChartType.CylinderColClustered:
                case eChartType.CylinderColStacked:
                case eChartType.CylinderColStacked100:
                case eChartType.PyramidCol:
                case eChartType.PyramidColClustered:
                case eChartType.PyramidColStacked:
                case eChartType.PyramidColStacked100:
                    return new ExcelBarChart(drawings, drawNode, chartType, topChart, PivotTableSource);
                case eChartType.XYScatter:
                case eChartType.XYScatterLines:
                case eChartType.XYScatterLinesNoMarkers:
                case eChartType.XYScatterSmooth:
                case eChartType.XYScatterSmoothNoMarkers:
                    return new ExcelScatterChart(drawings, drawNode, chartType, topChart, PivotTableSource);
                case eChartType.Line:
                case eChartType.Line3D:
                case eChartType.LineMarkers:
                case eChartType.LineMarkersStacked:
                case eChartType.LineMarkersStacked100:
                case eChartType.LineStacked:
                case eChartType.LineStacked100:
                    return new ExcelLineChart(drawings, drawNode, chartType, topChart, PivotTableSource);
                case eChartType.Bubble:
                case eChartType.Bubble3DEffect:
                    return new ExcelBubbleChart(drawings, drawNode, chartType, topChart, PivotTableSource);
                case eChartType.Radar:
                case eChartType.RadarFilled:
                case eChartType.RadarMarkers:
                    return new ExcelRadarChart(drawings, drawNode, chartType, topChart, PivotTableSource);
                case eChartType.Surface:
                case eChartType.SurfaceTopView:
                case eChartType.SurfaceTopViewWireframe:
                case eChartType.SurfaceWireframe:
                    return new ExcelSurfaceChart(drawings, drawNode, chartType, topChart, PivotTableSource);
                default:
                    return new ExcelChart(drawings, drawNode, chartType, topChart, PivotTableSource);
            }
        }
        public ExcelPivotTable PivotTableSource
        {
            get;
            private set;
        }
       internal void SetPivotSource(ExcelPivotTable pivotTableSource)
       {
           PivotTableSource = pivotTableSource;
           XmlElement chart = ChartXml.SelectSingleNode("c:chartSpace/c:chart", NameSpaceManager) as XmlElement;

           var pivotSource = ChartXml.CreateElement("pivotSource", ExcelPackage.schemaChart);
           chart.ParentNode.InsertBefore(pivotSource, chart);
           pivotSource.InnerXml = string.Format("<c:name>[]{0}!{1}</c:name><c:fmtId val=\"0\"/>", PivotTableSource.WorkSheet.Name, pivotTableSource.Name);

           var fmts = ChartXml.CreateElement("pivotFmts", ExcelPackage.schemaChart);
           chart.PrependChild(fmts);
           fmts.InnerXml = "<c:pivotFmt><c:idx val=\"0\"/><c:marker><c:symbol val=\"none\"/></c:marker></c:pivotFmt>";

           Series.AddPivotSerie(pivotTableSource);
       }
       internal override void DeleteMe()
       {
           try
           {
               Part.Package.DeletePart(UriChart);
           }
           catch (Exception ex)
           {
               throw (new InvalidDataException("EPPlus internal error when deleteing chart.", ex));
           }
           base.DeleteMe();
       }
    }
}
