using System.Collections.Generic;
using System.Linq;
using Consulenza.ReportWriter.Business.Entity;
using Dundas.Charting.WebControl;
using Consulenza.ReportWriter.Business.OBJ_PDF;
using Consulenza.ReportCommon;
using System;
using ceTe.DynamicPDF;
using System.Globalization;


namespace Consulenza.ReportWriter.Business.CHART_PDF
{
    public class StackedPDF : ChartPDF
    {
        #region Field
        public float LabelsYOffset { get; set; }

        public float LabelsAxisYOffset { get; set; }
        /// <summary>
        /// bool che indica se avvicinare la label dei valori alle rispettive barre del grafico. Di default = false.
        /// </summary>
        public bool DinamicFloatXEtichettaLateraleBarra { get; set; }
        /// <summary>
        /// bool che indica se mostrare la legenda. Di default = false.
        /// </summary>
        public bool ShowLegend { get; set; }

        /// <summary>
        /// Lista di interi che rappresentano le larghezze personalizzabili delle colonne della tabella che compone la Leggenda.
        /// Colonna1: Immagine, Colonna2: Spazio, Colonna3: Testo.
        /// Di default = (20,15,125)
        /// </summary>
        public List<int> LegendWidthColumns { get; set; }

        /// <summary>
        /// Imposta o recupera l'altezza del simbolo della colonna1 della tabella che compone la legenda.
        /// Di default= 10.
        /// </summary>
        public int LegendSymbolHeight { get; set; }

        /// <summary>
        /// Imposta o recupera il delta Y (intesto come spostamento in alto in basso della Y del simbolo della legenda).
        /// Di default=4.
        /// </summary>
        public float LegendSymbolDeltaY { get; set; }

        public bool RemoveWhiteSpaceBetweenChartAndAxisX { get; set; }

        /// <summary>
        /// Imposta o recupera il deltaX (intesto come spostamento a destra o sinistra della X della legenda).
        /// Di default=0.
        /// </summary>
        public float LegendDeltaX { get; set; }

        /// <summary>
        /// Imposta o recupera il font size del testo della legenda.
        /// Di default= 8.
        /// </summary>
        public int LegendFontSizeText { get; set; }

        /// <summary>
        /// bool che indica se mostrare il border top. Di default = false.
        /// </summary>
        public bool BorderLineTop { get; set; }

        /// <summary>
        /// Imposta o recupera la X di BorderLineTop quando BorderLineTop = true.
        /// </summary>
        public float BorderLineX { get; set; }

        /// <summary>
        /// Imposta o recupera la Width di BorderLineTop quando BorderLineTop = true.
        /// </summary>
        public float BorderLineWidth { get; set; }

        /// <summary>
        /// Imposta o recupera un bool che indica se mostare la linea sull'asse y in corrispondeza dell'intervallo.
        /// Di default = false.
        /// </summary>
        public bool MinorGridAxisY { get; set; }

        /// <summary>
        /// Imposta o recupera il margine nell'asse y.
        /// Di default = 14.
        /// </summary>
        public float MarginAxisY { get; set; }
        /// <summary>
        /// Imposta o recupera il margine nell'asse y da top.
        /// Di default = 7.
        /// </summary>
        public float MarginAxisYFromTop { get; set; }
        /// <summary>
        /// Imposta o recupera un bool che indica se mostrare la linea dell'asse Y.
        /// Di default = True.
        /// </summary>
        public bool ShowLineAxisY { get; set; }

        /// <summary>
        /// Imposta o recupera un bool che indica se mostrare la linea dell'asse X.
        /// Di default = False.
        /// </summary>
        public bool ShowLineAxisX { get; set; }

        /// <summary>
        /// Imposta o recupera il numero di intervalli per l'asse Y.
        /// Di default = 4.
        /// </summary>
        public int IntervalNumberAxisY { get; set; }

        /// <summary>
        /// Imposta o recupera il tipo di formato delle etichette dell'asse Y.
        /// Di default = FormatType.Intero
        /// </summary>
        public FormatType LabelFormatAxisY { get; set; }

        /// <summary>
        /// Imposta o recupera un bool che indica se mostrare le label dell'asse Y.
        /// Di default = True.
        /// </summary>
        public bool ShowLabelAxisY { get; set; }

        /// <summary>
        /// Imposta o recupera un bool che indica se effettuare il reverse del grafico (effetto a specchio).                
        /// Se false disegna barre da sinistra a destra. Se true il contrario.
        /// Di default = False.
        /// </summary>
        public bool Reverse { get; set; }

        /// <summary>
        /// Imposta o recupera l'altezza di ogni singola barra.        
        /// Quando=0 il grafico tiene in considerazione la Height.
        /// Quando>0 il grafico non tiene in considerazione la Height.
        /// Di default= 0.
        /// </summary>
        public float HeightSingleBar { get; set; }


        /// <summary>
        /// Imposta o recupera un bool che indica se il grafico potrà effettuare il salto pagina.
        /// Di default=false.
        /// </summary>
        public bool PageBreak { get; set; }

        /// <summary>
        /// Imposta o recupera il valore massimo tra tutti i Points di tutte le Serie aggiunte al grafico.
        /// Se 0 il valore viene recuperato automaticamente.
        /// Di default=0.
        /// </summary>
        public double MaximumValueAxisY { get; set; }

        public bool RemoveWhiteSpaceToTheRightAxisX { get; set; }

        ///// <summary>
        ///// Imposta o recupera un bool che indica se mostrare un margine (definito da dundas) e l'inizio delle barre sull'asse X.
        ///// Di default= false.
        ///// </summary>
        //public bool ShowMarginX { get; set; }

        ///// <summary>
        ///// Imposta o recupera un bool che indica se mostrare un margine (definito da dundas) e l'inizio delle barre sull'asse Y.
        ///// Di default= false.
        ///// </summary>
        //public bool ShowMarginY { get; set; }

        public List<FormattedTextAreaPDF> Headers { get; set; }

        public List<VerticalMarkerWithLine> VerticalMarkersWithLines { get; set; }

        public bool FirstTwoRowsSumLinked { get; set; }

        /// <summary>
        /// Changes how multiline labels are aligned
        /// Default is = false
        /// </summary>
        public bool AlignMultilineLabelAxisX { get; set; }

        /// <summary>
        /// Changes the distance bwtween ticks and bars/lines
        /// </summary>
        public int IncreaseYTickLengthOffset { get; set; }

        #endregion

        #region Costruttori

        /// <summary>
        /// Costruttore
        /// </summary>
        public StackedPDF()
        {
            ObjectType = ObjectTypePdf.CHART;
            ChartType = ChartTypePdf.STACKED;
            ChartBase.ChartAreas.Add("Default");
            DinamicFloatXEtichettaLateraleBarra = true;
            //// Imposto la ChartAreas di default in modalidtà 3D
            ChartBase.ChartAreas["Default"].Area3DStyle.Enable3D = true;
            ChartBase.ChartAreas["Default"].Area3DStyle.XAngle = 5;

            MinorGridAxisY = false;

            MarginAxisY = 14;//deve essere il doppio del MarginAxisYFromTop per non esserne "annullato"
            MarginAxisYFromTop = -7;
            ShowLineAxisX = false;
            ShowLineAxisY = true;
            ShowLabelAxisY = true;

            IntervalNumberAxisY = 4;

            LabelFormatAxisY = FormatType.Intero;

            HeightSingleBar = 0;

            PageBreak = false;
            //ShowMarginX = false;
            //ShowMarginY = false;

            LegendWidthColumns = new List<int>() { 20, 15, 125 };
            LegendSymbolHeight = 10;
            LegendFontSizeText = 8;
            LegendSymbolDeltaY = 4;
            Headers = new List<FormattedTextAreaPDF>();
            FirstTwoRowsSumLinked = false;
            RemoveWhiteSpaceToTheRightAxisX = false;
            VerticalMarkersWithLines = new List<VerticalMarkerWithLine>();
        }

        /// <summary>
        /// Costruttore
        /// </summary>
        /// <param name="x"></param>
        public StackedPDF(float x)
            : this()
        {
            X = x;
        }

        /// <summary>
        /// Costruttore
        /// </summary>
        /// <param name="x"></param>
        /// <param name="scale"></param>
        public StackedPDF(float x, float scale)
            : this(x)
        {
            Scale = scale;
        }

        private float labelAxisXMinValue = 0;
        private float labelAxisXMaxValue = 0;

        /// <summary>
        /// Costruttore
        /// </summary>
        /// <param name="x"></param>
        /// <param name="scale"></param>
        /// <param name="y"></param>
        public StackedPDF(float x, float scale, float y)
            : this(x, scale)
        {
            Y = y;
        }

        #endregion

        #region Metodi

        /// <summary>
        /// Recupero il valore del Point massimo di ciascuna Serie aggiunta.
        /// </summary>
        /// <returns></returns>
        public double GetMaximumValuePoints()
        {
            // Recupero il valore del Point massimo di ciascuna serie aggiunta. Tra tutti recupero il più alto. 
            // Recupero i valori delle etichette           
            var maximum = 0D;
            var maximumSeriePrecedente = 0D;
            foreach (var itemSerie in SeriesCollection)
            {
                maximum = maximum < itemSerie.Points.Select(a => a.Value).Max() + maximumSeriePrecedente ?
                    itemSerie.Points.Select(a => a.Value).Max() + maximumSeriePrecedente :
                    maximum;

                maximumSeriePrecedente = itemSerie.Points.Select(a => a.Value).Max();
            }

            return maximum;
        }

        /// <summary>
        /// Converte in ObjectPDF l'oggetto StackedPDF (ChartPDF). 
        /// </summary>
        /// <returns></returns>
        public override PageElement ToElement()
        {
            //patryk3
            //SeriesCollection[0].Points[3].Value = 520075.07f;
            //SeriesCollection[0].Points[3].LabelAxisY = 520075.07f.ToString("N");
            //SeriesCollection[0].Points[4].Value = 1520075.07f;
            //SeriesCollection[0].Points[4].LabelAxisY = 1520075.07f.ToString("N");

            var listReturn = new List<ObjectPDF>();

            TotalHeight = HeightSingleBar * SeriesCollection[0].Points.Count + MarginAxisY + MarginAxisYFromTop;

            Height = GetHeight();

            #region BorderLineTop

            if (BorderLineTop)
            {
                listReturn.Add(new LinePDF(BorderLineX, BorderLineX + BorderLineWidth, 0.5F, ColorPDF.Immobiliare_Grigio_TitoloPiccolo));
                listReturn.Add(new SpacePDF(10));
            }
            if (Headers.Count > 0)
            {
                foreach (FormattedTextAreaPDF header in Headers)
                {
                    listReturn.Add(header);
                }
                listReturn.Add(new SpacePDF(10));
            }

            #endregion

            #region Creo il grafico

            CreateChart();

            #endregion

            #region Ritorno l'immagine contenente il grafico

            listReturn.Add(ToImage(true));

            #endregion

            #region Ritorno la linea e le etichette dell'Asse Y

            float altezzaLinea_Marcatore_Testo_AsseY = 0;

            listReturn.Add(new SpacePDF(Height));

            //if (RemoveWhiteSpaceBetweenChartAndAxisX)
            //{
            //    MarginAxisY = 0;
            //}

            listReturn.Add(new SpacePDF(MarginAxisY + MarginAxisYFromTop));
            // Linea dell' asse Y
            if (ShowLineAxisY)
                listReturn.Add(new LinePDF(X, (X + Width), 0.5F));

            if (ShowLabelAxisY)
            {

                FormattedTextAreaPDF etichettaAsse = null; // Sarà sempre valorizzata
                LinePDF marcatoreAsse = null;  // Sarà sempre valorizzata               

                //var valoreEtichetta = (MaximumValueAxisY > 0 ? Helper.Round(MaximumValueAxisY, RemoveWhiteSpaceToTheRightAxisX) : Helper.Round(GetMaximumValuePoints(), RemoveWhiteSpaceToTheRightAxisX)) / IntervalNumberAxisY;
                var valoreEtichetta = (ChartBase.ChartAreas[0].AxisY.Maximum - ChartBase.ChartAreas[0].AxisY.Minimum) / IntervalNumberAxisY;
                var xEtichetta = X;
                var larghezzaEtichetta = 5F;
                var altezzaMarcatore = 5F + IncreaseYTickLengthOffset;
                var labelPosition = X;

                double tmpLabelValue = ChartBase.ChartAreas[0].AxisY.Minimum;
                bool zeroLabelOccured = false;

                for (var i = 0; i <= IntervalNumberAxisY; i++)
                {
                    // Marcatore
                    //marcatoreAsse = new LinePDF(xEtichetta + (larghezzaEtichetta / 2), xEtichetta + (larghezzaEtichetta / 2), 0.5F) { DeltaY2ForVerticalLine = altezzaMarcatore, AutoIncrementYWritable = false };
                    marcatoreAsse = new LinePDF(xEtichetta, xEtichetta, 0.5F) { DeltaY2ForVerticalLine = altezzaMarcatore, AutoIncrementYWritable = false };
                    marcatoreAsse.DeltaY = -IncreaseYTickLengthOffset;
                    listReturn.Add(marcatoreAsse);

                    // Etichetta
                    var sEtichettaAsse = string.Empty;
                    if (tmpLabelValue == 0)
                    {
                        zeroLabelOccured = true;
                    }
                    switch (LabelFormatAxisY)
                    {
                        case FormatType.Intero:
                            sEtichettaAsse = Helper.FormatInteger(Convert.ToString(tmpLabelValue));
                            break;
                        case FormatType.Decimale0:
                            sEtichettaAsse = Helper.FormatDecimal(Convert.ToString(tmpLabelValue), 0);
                            break;
                        case FormatType.Decimale2:
                            sEtichettaAsse = Helper.FormatDecimal(Convert.ToString(tmpLabelValue), 2);
                            break;
                        case FormatType.Decimale2ConSeparatore:
                            sEtichettaAsse = Helper.FormatCurrency(Convert.ToString(tmpLabelValue));
                            break;
                        case FormatType.Valuta:
                            sEtichettaAsse = Helper.FormatCurrencyWithSymbol(Convert.ToString(tmpLabelValue));
                            break;
                        case FormatType.Percentuale:
                            sEtichettaAsse = Helper.FormatPercentage(Convert.ToString(tmpLabelValue), 0);
                            break;
                    }
                    larghezzaEtichetta = Width / IntervalNumberAxisY; //(ottenuto da 300= larghezza grafico diviso il numero di intervalli =AxisYIntervalNumber)

                    float labelWidth = GetNumericStringWidth(sEtichettaAsse);
                    labelPosition = X + (i * larghezzaEtichetta) - (labelWidth / 2);

                    etichettaAsse = new FormattedTextAreaPDF(sEtichettaAsse, labelPosition, labelWidth) { TextHorizontalAlign = i == 0 ? TextAlign.Left : TextAlign.Center, AutoIncrementYWritable = false, FontSize = 7, DeltaY = marcatoreAsse.DeltaY2ForVerticalLine + marcatoreAsse.DeltaY, FixedHeight = 8 };
                    if (sEtichettaAsse != string.Empty)
                    {
                        string etichetta = sEtichettaAsse.Replace(".", "").Replace("%", "").Replace("€", "");
                        float intSEtichettaAsse = float.Parse(etichetta);
                        if (intSEtichettaAsse < labelAxisXMinValue)
                        {
                            labelAxisXMinValue = intSEtichettaAsse;
                        }
                        if (intSEtichettaAsse > labelAxisXMaxValue)
                        {
                            labelAxisXMaxValue = intSEtichettaAsse;
                        }
                    }

                    listReturn.Add(etichettaAsse);

                    //if (i == 0)
                    //    labelPosition += GetNumericStringWidth(sEtichettaAsse);
                    //else
                    //    labelPosition += larghezzaEtichetta;

                    xEtichetta += larghezzaEtichetta;
                    tmpLabelValue += valoreEtichetta;
                }

                if (zeroLabelOccured == false && ChartBase.ChartAreas[0].AxisY.Minimum < 0)
                {
                    var sEtichettaAsse = string.Empty;
                    switch (LabelFormatAxisY)
                    {
                        case FormatType.Intero:
                            sEtichettaAsse = Helper.FormatInteger(Convert.ToString(0));
                            break;
                        case FormatType.Decimale0:
                            sEtichettaAsse = Helper.FormatDecimal(Convert.ToString(0), 0);
                            break;
                        case FormatType.Decimale2:
                            sEtichettaAsse = Helper.FormatDecimal(Convert.ToString(0), 2);
                            break;
                        case FormatType.Decimale2ConSeparatore:
                            sEtichettaAsse = Helper.FormatCurrency(Convert.ToString(0));
                            break;
                        case FormatType.Valuta:
                            sEtichettaAsse = Helper.FormatCurrencyWithSymbol(Convert.ToString(0));
                            break;
                        case FormatType.Percentuale:
                            sEtichettaAsse = Helper.FormatPercentage(Convert.ToString(0), 0);
                            break;
                    }
                    float labelWidth = GetNumericStringWidth(sEtichettaAsse);
                    labelPosition = X + Width * (float)(Math.Abs(ChartBase.ChartAreas[0].AxisY.Minimum) / (ChartBase.ChartAreas[0].AxisY.Maximum - ChartBase.ChartAreas[0].AxisY.Minimum));
                    marcatoreAsse = new LinePDF(labelPosition, labelPosition, 0.5F) { DeltaY2ForVerticalLine = altezzaMarcatore, AutoIncrementYWritable = false };
                    marcatoreAsse.DeltaY = -IncreaseYTickLengthOffset;
                    labelPosition -= (labelWidth / 2);
                    etichettaAsse = new FormattedTextAreaPDF(sEtichettaAsse, labelPosition, labelWidth) { TextHorizontalAlign = TextAlign.Center, AutoIncrementYWritable = false, FontSize = 7, DeltaY = marcatoreAsse.DeltaY2ForVerticalLine + marcatoreAsse.DeltaY, FixedHeight = 8 };
                    listReturn.Add(marcatoreAsse);
                    listReturn.Add(etichettaAsse);
                }

                altezzaLinea_Marcatore_Testo_AsseY = etichettaAsse.Height + marcatoreAsse.DeltaY2ForVerticalLine;
                TotalHeight += altezzaLinea_Marcatore_Testo_AsseY;
            }


            #endregion

            #region Ritorno la linea dell'Asse X

            if (ShowLineAxisX)
                listReturn.Add(new LinePDF(X, X) { DeltaY2ForVerticalLine = -(Height + MarginAxisY + MarginAxisYFromTop), AutoIncrementYWritable = false });

            #endregion

            #region Ritorno le etichette dei Points su AsseY e AsseX

            foreach (Serie serie in SeriesCollection)
            {
                //  etichette Barre
                serie.Points.Reverse();

                #region Calcolo dell'altezza delle singole barre

                decimal pointWidth;
                bool b = Decimal.TryParse(serie.PointWidth, NumberStyles.Any, new CultureInfo("en-US"), out pointWidth);
                if (!b)
                    throw new Exception("StackedPDF.ToElement - conversione di PointWidth non riuscita");

                var numeroSpazi = serie.Points.Count - 1;
                var altezzaSingolaBarra = (Height / serie.Points.Count) * (float)pointWidth;
                var altezzaSingoloSpazio = (Height - (altezzaSingolaBarra * serie.Points.Count)) / numeroSpazi;

                float margin = 0;
                if (RemoveWhiteSpaceBetweenChartAndAxisX)
                {
                    var singleRowWithSpace = (Height / (serie.Points.Count + 1));
                    margin = singleRowWithSpace;
                    altezzaSingolaBarra = singleRowWithSpace * (float)pointWidth;
                    altezzaSingoloSpazio = singleRowWithSpace * (1 - (float)pointWidth);
                }

                if (altezzaSingoloSpazio < 0 || numeroSpazi == 0)
                    altezzaSingoloSpazio = 0;

                var scartoTotale = (((altezzaSingoloSpazio + altezzaSingolaBarra) * serie.Points.Count) - Height) / serie.Points.Count;
                var scartoSingolaBarra = scartoTotale / serie.Points.Count;
                var h = ((altezzaSingoloSpazio + altezzaSingolaBarra - scartoTotale) * (serie.Points.Count)) + MarginAxisY + MarginAxisYFromTop;

                if (RemoveWhiteSpaceBetweenChartAndAxisX)
                {
                    scartoTotale = (((altezzaSingoloSpazio + altezzaSingolaBarra) * serie.Points.Count) + margin - Height) / serie.Points.Count;
                    scartoSingolaBarra = scartoTotale / serie.Points.Count;
                    h = ((altezzaSingoloSpazio + altezzaSingolaBarra - scartoTotale) * (serie.Points.Count)) + margin + MarginAxisY + MarginAxisYFromTop;
                }

                #endregion

                listReturn.Add(new SpacePDF(-h));
                var larghezzaSingolaBarra = (Width / serie.Points.Sum(o => o.Value)) * (float)pointWidth;
                var p = 0;
                float dinamicFloatXEtichettaAxisY = 0;
                float currentPosY = 0;
                float currentPosX = 0;
                float deltaTmpY = 0;

                float axisYLabelsOffset = 0;
                if (RemoveWhiteSpaceBetweenChartAndAxisX)
                {
                    axisYLabelsOffset += margin / 2;
                    axisYLabelsOffset += altezzaSingoloSpazio / 2;
                }

                foreach (Point point in serie.Points)
                {
                    //
                    // (float)Convert.ToDecimal(point.Value + 50);// +(float)Convert.ToDecimal(serie.Points[p].Value) + 110; 
                    if (point.ID == "debug")
                        listReturn.Add(new LinePDF(X, X + Width, 1, ColorPDF.Rosso) { AutoIncrementYWritable = false });

                    #region Label Asse Y (in questo grafico gli assi sono invertiti)
                    if (point.ShowLabelAxisY)
                    {
                        LinePDF horizontalFirstRowLine = null;
                        LinePDF horizontalSecondRowLine = null;
                        LinePDF verticalConnectingLine = null;
                        RectanglePDF backgroundRectangle = null;
                        RectanglePDF shadowRectangle = null;
                        FormattedTextAreaPDF firstTwoRowsTextArea = null;

                        double firstTwoRowsValuesSum = 0;
                        int rectanglePosition = 0; // 0 - middle, 1 - first row, 2 - second row
                        var xEtichettaAxisY = X;
                        var allineamentoEtichettaY = Reverse ? TextAlign.Left : TextAlign.Right;
                        dinamicFloatXEtichettaAxisY = X;

                        FormattedTextAreaPDF etichettaAsseY = null;

                        if (!DinamicFloatXEtichettaLateraleBarra)
                        {
                            etichettaAsseY = new FormattedTextAreaPDF(point.LabelAxisY, xEtichettaAxisY, Width) { TextHorizontalAlign = allineamentoEtichettaY, FontSize = point.FontSizeLabelAxisY, FontBold = point.FontBoldLabelAxisY, AutoIncrementYWritable = false, FontColor = point.FontColorLabelAxisY };
                        }
                        else
                        {
                            //PATRYK - dynamic label positioning
                            //tmp
                            //point.LabelAxisY = "50.100.000,00";
                            int textAreaWidth = GetNumericStringWidth(point.LabelAxisY);
                            string textAreaText = point.LabelAxisY;
                            int chartWidth = (int)this.Width;
                            int maximumValue = (int)GetMaximumValuePoints();
                            if (labelAxisXMaxValue == 0)
                            {
                                labelAxisXMaxValue = (float)ChartBase.ChartAreas[0].AxisY.Maximum;
                            }
                            //int barWidth = (int)(chartWidth * (point.Value - labelAxisXMinValue) / (labelAxisXMaxValue - labelAxisXMinValue));
                            int barWidth = (int)(chartWidth * (Math.Abs(point.Value)) / (labelAxisXMaxValue - labelAxisXMinValue));
                            float positiveBarPositionOffset = (int)(chartWidth * (Math.Abs(labelAxisXMinValue)) / (labelAxisXMaxValue - labelAxisXMinValue));
                            //currentPosX = X + barWidth;
                            int difference = chartWidth - barWidth + (int)positiveBarPositionOffset - 5;

                            if (FirstTwoRowsSumLinked)
                            {
                                if (SeriesCollection[0].Points.Count >= 2 && p < 2)
                                {
                                    firstTwoRowsValuesSum = (SeriesCollection[0].Points[0].Value + SeriesCollection[0].Points[1].Value);

                                    string sumValue = firstTwoRowsValuesSum.ToString();// firstTwoRowsValuesSum.ToString("N");
                                    int sumWidth = GetNumericStringWidth(sumValue) + 15;
                                    int firstBarSpaceTaken =
                                        (8 + GetNumericStringWidth(SeriesCollection[0].Points[0].Value.ToString("N")) + (int)(chartWidth * SeriesCollection[0].Points[0].Value / labelAxisXMaxValue)) < chartWidth ?
                                        8 + GetNumericStringWidth(SeriesCollection[0].Points[0].Value.ToString("N")) + (int)(chartWidth * SeriesCollection[0].Points[0].Value / labelAxisXMaxValue) :
                                        chartWidth;
                                    int secondBarSpaceTaken =
                                        (8 + GetNumericStringWidth(SeriesCollection[0].Points[1].Value.ToString("N")) + (int)(chartWidth * SeriesCollection[0].Points[1].Value / labelAxisXMaxValue)) < chartWidth ?
                                        (8 + GetNumericStringWidth(SeriesCollection[0].Points[1].Value.ToString("N")) + (int)(chartWidth * SeriesCollection[0].Points[1].Value / labelAxisXMaxValue)) :
                                        chartWidth;
                                    int spaceLeft = chartWidth - (firstBarSpaceTaken > secondBarSpaceTaken ? firstBarSpaceTaken : secondBarSpaceTaken);
                                    int xFirstRowStart = (int)X + firstBarSpaceTaken;
                                    int xSecondRowStart = (int)X + secondBarSpaceTaken;
                                    int xEndHorizontalLines = 0;
                                    int xRectangleStart = 0;
                                    int firstRowDifference = chartWidth - firstBarSpaceTaken;
                                    int secondRowDifference = chartWidth - secondBarSpaceTaken;
                                    if (spaceLeft > sumWidth) // middle - in bettween of 2 rows
                                    {
                                        rectanglePosition = 0;
                                        xEndHorizontalLines = (int)(X + chartWidth - spaceLeft + spaceLeft / 2);
                                        xRectangleStart = xEndHorizontalLines - sumWidth / 2;
                                    }
                                    else  // not middle (1st or 2nd row)
                                    {
                                        xEndHorizontalLines = (int)(X + chartWidth + 20);

                                        if (firstBarSpaceTaken > secondBarSpaceTaken) // bottom - 2nd row
                                        {
                                            rectanglePosition = 2;
                                            xRectangleStart = xSecondRowStart + (xEndHorizontalLines - xSecondRowStart) / 2 - textAreaWidth / 2;

                                        }
                                        else // top - 1st row
                                        {
                                            rectanglePosition = 1;
                                            xRectangleStart = xFirstRowStart + (xEndHorizontalLines - xFirstRowStart) / 2 - textAreaWidth / 2;
                                        }
                                    }
                                    if (p == 0)
                                    {
                                        horizontalFirstRowLine = new LinePDF()
                                        {
                                            AutoIncrementYWritable = false,
                                            X1 = xFirstRowStart,
                                            X2 = xEndHorizontalLines,
                                            Color = new ColorPDF(0, 0, 0)
                                        };
                                        float widthVerticlLine = 41;
                                        if (RemoveWhiteSpaceBetweenChartAndAxisX)
                                        {
                                            widthVerticlLine = margin;// margin = height of single row + its spazzio
                                        }
                                        verticalConnectingLine = new LinePDF()
                                        {
                                            AutoIncrementYWritable = false,
                                            X1 = xEndHorizontalLines - 0.15f,
                                            X2 = xEndHorizontalLines,
                                            Width = widthVerticlLine,
                                            Color = new ColorPDF(0, 0, 0)
                                        };
                                    }
                                    if (p == 1)
                                    {
                                        horizontalSecondRowLine = new LinePDF()
                                        {
                                            AutoIncrementYWritable = false,
                                            X1 = xSecondRowStart,
                                            X2 = xEndHorizontalLines,
                                            Color = new ColorPDF(0, 0, 0)
                                        };
                                        backgroundRectangle = new RectanglePDF(xRectangleStart, 12.5f, sumWidth, new ColorPDF(255, 255, 255))
                                        {
                                            AutoIncrementYWritable = false,
                                            BorderColor = new ColorPDF(0, 0, 0),
                                            BorderWidth = 0.15f,
                                            BorderStyle = LineStylePDF.Solid,
                                            CornerRadius = 3
                                        };
                                        shadowRectangle = new RectanglePDF(xRectangleStart + 1, 12.5f, sumWidth, new ColorPDF(255, 255, 255))
                                        {
                                            AutoIncrementYWritable = false,
                                            BorderColor = new ColorPDF(195, 195, 195),
                                            BorderWidth = 0.15f,
                                            BorderStyle = LineStylePDF.Solid,
                                            CornerRadius = 3,
                                            Color = new ColorPDF(195, 195, 195)
                                        };
                                        firstTwoRowsTextArea = new FormattedTextAreaPDF(Helper.FormatCurrency(sumValue), xRectangleStart, sumWidth)
                                        {
                                            FontSize = point.FontSizeLabelAxisY,
                                            FontBold = point.FontBoldLabelAxisY,
                                            AutoIncrementYWritable = false,
                                            FontColor = point.FontColorLabelAxisY,
                                            TextHorizontalAlign = TextAlign.Center
                                        };
                                    }
                                }
                            }

                            if (point.Value >= 0)
                            {
                                if (difference > textAreaWidth)
                                {
                                    etichettaAsseY = new FormattedTextAreaPDF(point.LabelAxisY, X + barWidth + positiveBarPositionOffset + 5, Width) { FontSize = point.FontSizeLabelAxisY, FontBold = point.FontBoldLabelAxisY, AutoIncrementYWritable = false, FontColor = point.FontColorLabelAxisY };
                                }
                                else
                                {
                                    etichettaAsseY = new FormattedTextAreaPDF(point.LabelAxisY, X + chartWidth - textAreaWidth, Width) { FontSize = point.FontSizeLabelAxisY, FontBold = point.FontBoldLabelAxisY, AutoIncrementYWritable = false, FontColor = point.FontColorLabelAxisY };
                                }
                            }
                            else
                            {
                                etichettaAsseY = new FormattedTextAreaPDF(point.LabelAxisY, X + positiveBarPositionOffset - barWidth - textAreaWidth, Width) { FontSize = point.FontSizeLabelAxisY, FontBold = point.FontBoldLabelAxisY, AutoIncrementYWritable = false, FontColor = point.FontColorLabelAxisY };
                            }
                        }

                        //Allineamento verticale
                        switch (point.VerticalAlignment)
                        {
                            case VerticalAlignmentType.Alto:
                            case VerticalAlignmentType.Nonimpostato:
                                //**NESSUNA ELABORAZIONE**
                                break;
                            case VerticalAlignmentType.Centrato:
                                etichettaAsseY.DeltaY = ((altezzaSingolaBarra - point.FontSizeLabelAxisY) / 2) - point.FontSizeLabelAxisY / 4;
                                break;
                            case VerticalAlignmentType.Basso:
                                etichettaAsseY.DeltaY = altezzaSingolaBarra - point.FontSizeLabelAxisY;
                                break;
                            default:
                                break;
                        }

                        etichettaAsseY.DeltaY -= p * scartoSingolaBarra;
                        if (horizontalFirstRowLine != null)
                        {
                            if (RemoveWhiteSpaceBetweenChartAndAxisX)
                            {
                                horizontalFirstRowLine.DeltaY = etichettaAsseY.DeltaY + 4 + margin / 2 + altezzaSingoloSpazio / 2;
                                verticalConnectingLine.DeltaY = etichettaAsseY.DeltaY + margin + altezzaSingoloSpazio + 0.5f;
                            }
                            else
                            {
                                horizontalFirstRowLine.DeltaY = etichettaAsseY.DeltaY + 4;
                                verticalConnectingLine.DeltaY = etichettaAsseY.DeltaY + 24.7f;
                            }
                            listReturn.Add(horizontalFirstRowLine);
                            listReturn.Add(verticalConnectingLine);
                        }
                        if (horizontalSecondRowLine != null)
                        {
                            if (rectanglePosition == 0) // middle - in between of 2 first rows
                            {
                                if (RemoveWhiteSpaceBetweenChartAndAxisX)
                                {
                                    backgroundRectangle.DeltaY = 8;
                                }
                                else
                                {
                                    backgroundRectangle.DeltaY = etichettaAsseY.DeltaY - 24;
                                }
                            }
                            else if (rectanglePosition == 1) // first row
                            {
                                if (RemoveWhiteSpaceBetweenChartAndAxisX)
                                {
                                    backgroundRectangle.DeltaY = -8;
                                }
                                else
                                {
                                    backgroundRectangle.DeltaY = etichettaAsseY.DeltaY - 45;
                                }

                            }
                            else if (rectanglePosition == 2) // second row
                            {
                                if (RemoveWhiteSpaceBetweenChartAndAxisX)
                                {
                                    backgroundRectangle.DeltaY = 26;
                                }
                                else
                                {
                                    backgroundRectangle.DeltaY = etichettaAsseY.DeltaY - 3;
                                }
                            }
                            if (RemoveWhiteSpaceBetweenChartAndAxisX)
                            {
                                shadowRectangle.DeltaY = backgroundRectangle.DeltaY + 1.5f;
                                firstTwoRowsTextArea.DeltaY = backgroundRectangle.DeltaY + 2;
                                horizontalSecondRowLine.DeltaY = etichettaAsseY.DeltaY + 4 + margin / 2 + altezzaSingoloSpazio / 2;
                            }
                            else
                            {
                                shadowRectangle.DeltaY = backgroundRectangle.DeltaY + 1.5f;
                                firstTwoRowsTextArea.DeltaY = backgroundRectangle.DeltaY + 2;
                                horizontalSecondRowLine.DeltaY = etichettaAsseY.DeltaY + 4;
                            }
                            listReturn.Add(horizontalSecondRowLine);
                            listReturn.Add(shadowRectangle);
                            listReturn.Add(backgroundRectangle);
                            listReturn.Add(firstTwoRowsTextArea);
                        }

                        currentPosY = etichettaAsseY.DeltaY;
                        deltaTmpY = etichettaAsseY.DeltaY;
                        etichettaAsseY.DeltaY += LabelsYOffset;
                        etichettaAsseY.DeltaY += axisYLabelsOffset;
                        listReturn.Add(etichettaAsseY);
                    }

                    #endregion

                    #region Label Asse X (in questo grafico gli assi sono invertiti)

                    if (point.ShowLabelAxisX)
                    {
                        var xEtichettaAxisX = X - 110;
                        var etichettaAsseX = new FormattedTextAreaPDF(point.LabelAxisX, xEtichettaAxisX, 100) { TextHorizontalAlign = TextAlign.Right, FontSize = point.FontSizeLabelAxisX, FontBold = point.FontBoldLabelAxisX, AutoIncrementYWritable = false, FontColor = point.FontColorLabelAxisX };
                        ImagePDF immagineEtichettaAsseX = null;
                        if (!string.IsNullOrEmpty(point.ImagePathLabelAsixX))
                            immagineEtichettaAsseX = new ImagePDF(xEtichettaAxisX + point.ImageDeltaXLabelAsixX, point.ImageScaleLabelAsixX, point.ImagePathLabelAsixX);


                        //Allineamento verticale
                        switch (point.VerticalAlignment)
                        {
                            case VerticalAlignmentType.Alto:
                            case VerticalAlignmentType.Nonimpostato:
                                //**NESSUNA ELABORAZIONE**
                                break;
                            case VerticalAlignmentType.Centrato:
                                if (etichettaAsseX.Text.Length > 25 && (etichettaAsseX.FontSize == 8))
                                    etichettaAsseX.DeltaY = ((altezzaSingolaBarra - point.FontSizeLabelAxisX) / 2) - point.FontSizeLabelAxisX;
                                else if (AlignMultilineLabelAxisX && etichettaAsseX.Text.Length > 25)
                                {
                                    etichettaAsseX.DeltaY = (altezzaSingolaBarra - point.FontSizeLabelAxisX * 2) / 2 - point.FontSizeLabelAxisX / 4;
                                }
                                else
                                    etichettaAsseX.DeltaY = ((altezzaSingolaBarra - point.FontSizeLabelAxisX) / 2) - point.FontSizeLabelAxisX / 4;
                                break;
                            case VerticalAlignmentType.Basso:
                                etichettaAsseX.DeltaY = altezzaSingolaBarra - point.FontSizeLabelAxisX;
                                break;
                            default:
                                break;
                        }

                        // Delta Y dell'etichetta
                        etichettaAsseX.DeltaY -= p * scartoSingolaBarra;
                        etichettaAsseX.DeltaY += axisYLabelsOffset;
                        etichettaAsseX.DeltaY += LabelsAxisYOffset;

                        if (immagineEtichettaAsseX != null)
                        {
                            immagineEtichettaAsseX.DeltaY = etichettaAsseX.DeltaY; // l'immagine dell'etichetta avrà la stessa Y dell'etichetta
                            listReturn.Add(immagineEtichettaAsseX);
                        }

                        listReturn.Add(etichettaAsseX);
                    }

                    #endregion

                    listReturn.Add(new SpacePDF(altezzaSingoloSpazio + altezzaSingolaBarra));

                    if (point.ID == "debug")
                        listReturn.Add(new LinePDF(X, X + Width, 1, ColorPDF.Giallo) { AutoIncrementYWritable = false });

                    p++;
                }
                if (RemoveWhiteSpaceBetweenChartAndAxisX)
                {
                    listReturn.Add(new SpacePDF(margin / 2 + 10));
                }
                listReturn.Add(new SpacePDF(-(scartoTotale * serie.Points.Count) + MarginAxisY + MarginAxisYFromTop));
            }

            #endregion

            #region Ritorno la Legenda

            if (ShowLegend)
            {
                // Aggiungo lo spazio del MarginAxisY e dell'altezza della Linea, marcatore e del testo dell'asse Y 
                listReturn.Add(new SpacePDF(altezzaLinea_Marcatore_Testo_AsseY + 10));

                var xLegenda = (X - 40) + LegendDeltaX;
                var tabellaLegenda = new TablePDF(xLegenda, SeriesCollection.Count * 3, 1)
                {
                    ID = "legend",
                    Footer = false,
                    Header = false,
                    PageBreak = false
                }; // *3 pari al numero di colonne di cui si compone la legenda (immagine, spazio, testo)

                var j = 0;
                foreach (var serie in SeriesCollection)
                {
                    tabellaLegenda.Columns.Add(new ColumnPDF("immagine" + j, LegendWidthColumns[0], HorizontalAlignmentType.Sinistra, false, false, LegendFontSizeText, ColumnType.Objectpdf));
                    tabellaLegenda.Columns.Add(new ColumnPDF("spazio" + j, LegendWidthColumns[1], HorizontalAlignmentType.Sinistra, false, false, LegendFontSizeText, ColumnType.Testo));
                    tabellaLegenda.Columns.Add(new ColumnPDF("testo" + j, LegendWidthColumns[2], HorizontalAlignmentType.Sinistra, false, false, LegendFontSizeText, ColumnType.Testo));

                    tabellaLegenda.Cells[j, 0].ValueObject = new RectanglePDF(LegendSymbolHeight, LegendWidthColumns[0], serie.Color) { AutoIncrementYWritable = false, DeltaY = LegendSymbolDeltaY };
                    tabellaLegenda.Cells[j + 1, 0].Value = string.Empty;
                    tabellaLegenda.Cells[j + 2, 0].Value = serie.Text;

                    j += 3;
                }

                listReturn.Add(tabellaLegenda);

                TotalHeight += 10; // +10 =  tabellaLegenda.Cells[j, 0].Height
            }

            #endregion

            Elements.AddRange(listReturn);

            return this;
        }

        public float GetHeight()
        {
            NumberFormatInfo nf = new NumberFormatInfo() { NumberDecimalSeparator = "." };
            float pointWidthFloat = float.Parse(SeriesCollection[0].PointWidth, nf); // by default 0.8
            float pointWidthSpace = 1 - pointWidthFloat; // by default should be 0.2

            float height = (HeightSingleBar * pointWidthFloat * SeriesCollection[0].Points.Count) +      // n bars
                     (HeightSingleBar * pointWidthSpace * (SeriesCollection[0].Points.Count - 1)) +  // (n -1) spaces between bars
                      MarginAxisY + MarginAxisYFromTop;

            if (RemoveWhiteSpaceBetweenChartAndAxisX)
            {
                height += HeightSingleBar * 1.2f; // additional margin (top and bottom) added by dundas lib
            }
            return height;
        }
        private int GetNumericStringWidth(string number)
        {
            int width = 0;
            foreach (char s in number)
            {
                if (s == '.' || s == ',')
                {
                    width += 1;
                }
                else if (s == '%')
                {
                    width += 8;
                }
                else
                {
                    width += 5;
                }
            }
            return width;
        }
        /// <summary>
        /// Crea il grafico di tipo StackedBar
        /// </summary>
        private void CreateChart()
        {
            // Imposto tutte le serie aggiunte al chart come StackedBar.

            base.SeriesCollection.ForEach(o => o.Type = SeriesChartType.StackedBar);

            //patryk
            //base.SeriesCollection.ForEach(o => o.Type = SeriesChartType.Bar);

            ChartBase.AntiAliasing = AntiAliasing.All; // AntiAliasing.Graphics and AntiAliasing.Text            
            ChartBase.TextAntiAliasingQuality = TextAntiAliasingQuality.High;

            //Deve essere White: si usano altri colori per fare test in sviluppo sulle dimensioni delle cornici esterne.
            ChartBase.ChartAreas[0].BackColor = System.Drawing.Color.White;
            ChartBase.ChartAreas[0].BorderColor = System.Drawing.Color.Empty;
            ChartBase.ChartAreas[0].BorderWidth = 0;

            ChartBase.ChartAreas[0].Position.Auto = false;
            ChartBase.ChartAreas[0].Position.X = 0;
            ChartBase.ChartAreas[0].Position.Y = 0;
            ChartBase.ChartAreas[0].Position.Width = 100;
            ChartBase.ChartAreas[0].Position.Height = 100;

            // Set the plotting area position. Coordinates of a plotting 
            // area are relative to a chart area position.
            ChartBase.ChartAreas[0].InnerPlotPosition.Auto = true;

            //ASSE X
            ChartBase.ChartAreas[0].AxisX.Margin = false;
            ChartBase.ChartAreas[0].AxisX.LabelStyle.Enabled = false;
            ChartBase.ChartAreas[0].AxisX.MajorGrid.Enabled = false;
            ChartBase.ChartAreas[0].AxisX.MinorTickMark.Enabled = false;
            ChartBase.ChartAreas[0].AxisX.MajorTickMark.Enabled = false;
            ChartBase.ChartAreas[0].AxisX.LineStyle = ChartDashStyle.NotSet;

            //ASSE Y     

            #region Reverse
            ChartBase.ChartAreas[0].AxisY.Reverse = Reverse;
            #endregion

            //Imposto Maximum, Minimum e Interval.
            ChartBase.ChartAreas[0].AxisY.Maximum = MaximumValueAxisY > 0 ? Helper.Round(MaximumValueAxisY, RemoveWhiteSpaceToTheRightAxisX) : Helper.Round(GetMaximumValuePoints(), RemoveWhiteSpaceToTheRightAxisX);
            string minLabel = string.Empty;
            //labelAxisXMinValue = GetMinAxisXValue(out minLabel);
            labelAxisXMinValue = EntireChartMinAxisXVal ?? 0;
            minLabel = EntireChartMinAxisXValLbl;
            if (labelAxisXMinValue < 0)
            {
                int minLabelWidth = GetNumericStringWidth(minLabel);
                float offsetToAddToMinValue = (float)((ChartBase.ChartAreas[0].AxisY.Maximum - labelAxisXMinValue) * minLabelWidth / Width * 100) / 100;
                labelAxisXMinValue -= offsetToAddToMinValue;
                labelAxisXMinValue = (int)(labelAxisXMinValue - 3);

                ChartBase.ChartAreas[0].AxisY.StripLines.Add(new StripLine()
                {
                    BorderStyle = ChartDashStyle.Dash,
                    StripWidth = 0,
                    BackColor = System.Drawing.Color.Gray,
                    BorderColor = System.Drawing.Color.Gray,
                    Interval = 1000000,
                    IntervalOffset = labelAxisXMinValue * (-1)
                });
            }
            ChartBase.ChartAreas[0].AxisY.Minimum = labelAxisXMinValue;
            ChartBase.ChartAreas[0].AxisY.Interval = (ChartBase.ChartAreas[0].AxisY.Maximum - ChartBase.ChartAreas[0].AxisY.Minimum) / IntervalNumberAxisY;

            ChartBase.ChartAreas[0].AxisY.Margin = false;
            ChartBase.ChartAreas[0].AxisY.LabelsAutoFit = true;
            ChartBase.ChartAreas[0].AxisY.LabelStyle.Enabled = false;
            ChartBase.ChartAreas[0].AxisY.MajorGrid.Enabled = false;
            ChartBase.ChartAreas[0].AxisY.MajorTickMark.Enabled = false;

            ChartBase.ChartAreas[0].AxisY.LineStyle = ChartDashStyle.NotSet;

            // MinorGrid
            if (MinorGridAxisY)
            {
                ChartBase.ChartAreas[0].AxisY.MinorGrid.Enabled = true;
                ChartBase.ChartAreas[0].AxisY.MinorGrid.LineStyle = ChartDashStyle.Dot;
                ChartBase.ChartAreas[0].AxisY.MinorGrid.Interval = ChartBase.ChartAreas[0].AxisY.Interval;
                ChartBase.ChartAreas[0].AxisY.MinorGrid.LineColor = System.Drawing.Color.Gray;
            }

            // Disabilito il 3D
            ChartBase.ChartAreas[0].Area3DStyle.Enable3D = false;

            if (RemoveWhiteSpaceBetweenChartAndAxisX)
            {
                //patryk2
                ChartBase.ChartAreas[0].InnerPlotPosition.Width = 100;
                ChartBase.ChartAreas[0].InnerPlotPosition.Height = 100;
                ChartBase.ChartAreas[0].AxisX.Margin = true;
            }


            // Impostazioni delle Serie
            foreach (Serie item in SeriesCollection)
            {
                ChartBase.Series.Add(item.Name);
                ChartBase.Series[item.Name]["StackedGroupName"] = "Group1";

                // Imposto il tipo di grafico
                ChartBase.Series[item.Name].Type = item.Type;

                //patryk
                //ChartBase.Series[item.Name].ShowLabelAsValue = true;
                //ChartBase.Series[item.Name].LabelFormat = "n2";

                // Imposto la larghezza delle barre di ciascuna Serie
                ChartBase.Series[item.Name]["PointWidth"] = item.PointWidth;

                // Bordo
                if (item.Border)
                {
                    ChartBase.Series[item.Name].BorderColor = System.Drawing.Color.White;
                    ChartBase.Series[item.Name].BorderWidth = 3;
                    ChartBase.Series[item.Name].BorderStyle = ChartDashStyle.Solid;
                    ChartBase.Series[item.Name].ShadowOffset = 4;
                }

                int i = 0;
                foreach (var point in item.Points)
                {
                    ChartBase.Series[item.Name].Points.AddY(point.Value);

                    var r = point.Color.Red;
                    var g = point.Color.Green;
                    var b = point.Color.Blue;

                    ChartBase.Series[item.Name].Points[i].Color = System.Drawing.Color.FromArgb(r, g, b);

                    i++;
                }
            }

            // Legenda disabilitata
            ChartBase.Legend.Enabled = false;
        }

        #endregion
    }

    public class VerticalMarkerWithLine
    {
        public double XValue { get; set; }
        public string ImagePath { get; set; }
        public ColorPDF Color { get; set; }
        public float ImageYOffset { get; set; }
    }
}
