#include "ValueChangeTransition.hpp"
#include "ParallelTransition.hpp"
#include "AtomicSequentialTransition.hpp"
#include "ScaleTransition.hpp"
#include "MoveTransition.hpp"
#include "RotateTransition.hpp"
#include "BlendTransition.hpp"
#include "Delay.hpp"
#include "Bar.hpp"
#include "StackedBar.hpp"
#include "PieSector.hpp"

ValueChangeTransition::ValueChangeTransition(Chart* pInitialChart, DataSet* pNewDataSet) {
  iType = VALUE_CHANGE_TRANSITION;
  this->pInitialChart = pInitialChart;
  
  switch (this->pInitialChart->GetType()) {
    case BAR_CHART: {
      pResultChart = new BarChart(pNewDataSet);
      std::vector<DataElement*> vInitialDataElements = pInitialChart->GetDataElements();
      std::vector<DataEntry*> vResultDataEntries = pResultChart->GetDataSet()->GetAllDataEntries();

      //sequential transition order grid rescaling and scaling/sorting of the data elements
      AtomicSequentialTransition* pAtomicSequentialTransition = new AtomicSequentialTransition();
      AddTransition(pAtomicSequentialTransition);
      
      AtomicSequentialTransition* pGridScaleAtomicSequentialTransition = NULL;

      //fade out the labels (headline) of the chart
      ParallelTransition* pFadeOutLabelsParallelTransition = new ParallelTransition();
      pAtomicSequentialTransition->AddTransition(pFadeOutLabelsParallelTransition);

      std::vector<Label*> vInitialLabels = pInitialChart->GetLabels();

      for (unsigned int i = 0; i < vInitialLabels.size(); i++) {
        pFadeOutLabelsParallelTransition->AddTransition(new BlendTransition(vInitialLabels.at(i), -1.0f));
      }

      //check if the grid has to be rescaled
      double dHightestInitialValue = pInitialChart->GetHighestVisibleValue();
      double dHightestResultValue = pResultChart->GetHighestVisibleValue();

      bool bGridScale = false;
      bool bScaleDown = false;

      if (((dHightestInitialValue <= GRID_LOW_VALUE) && (dHightestResultValue > GRID_LOW_VALUE)) ||
        ((dHightestInitialValue <= GRID_MID_VALUE) && (dHightestResultValue > GRID_MID_VALUE)) ||
        ((dHightestInitialValue <= GRID_SECOND_HIGH_VALUE) && (dHightestResultValue > GRID_SECOND_HIGH_VALUE))) {
        bGridScale = true;
        bScaleDown = false;
      }
      else if (((dHightestResultValue <= GRID_LOW_VALUE) && (dHightestInitialValue > GRID_LOW_VALUE)) ||
        ((dHightestResultValue <= GRID_MID_VALUE) && (dHightestInitialValue > GRID_MID_VALUE)) ||
        ((dHightestResultValue <= GRID_SECOND_HIGH_VALUE) && (dHightestInitialValue > GRID_SECOND_HIGH_VALUE))) {
        bGridScale = true;
        bScaleDown = true;
      }
      
      //sequential transition to sort the elements after scaling
      AtomicSequentialTransition* pScaleAndSortAtomicSequentialTransition = new AtomicSequentialTransition();

      double dResultHighValue = 0.0;
      double dInitialHighValue = 0.0;

      if (bGridScale) {
        pGridScaleAtomicSequentialTransition = new AtomicSequentialTransition();

        //calculate the grid scaling factor
        int iInitialStepSize = 0;

        if (dHightestInitialValue <= GRID_LOW_VALUE) {
          dInitialHighValue = GRID_LOW_VALUE;
          iInitialStepSize = GRID_LOW_VALUE_STEP_SIZE;
        }
        else if (dHightestInitialValue <= GRID_MID_VALUE) {
          dInitialHighValue = GRID_MID_VALUE;
          iInitialStepSize = GRID_MID_VALUE_STEP_SIZE;
        }
        else if (dHightestInitialValue <= GRID_SECOND_HIGH_VALUE) {
          dInitialHighValue = GRID_SECOND_HIGH_VALUE;
          iInitialStepSize = GRID_SECOND_HIGH_VALUE_STEP_SIZE;
        }
        else {
          dInitialHighValue = GRID_HIGH_VALUE;
          iInitialStepSize = GRID_HIGH_VALUE_STEP_SIZE;
        }

        int iResultStepSize = 0;

        if (dHightestResultValue <= GRID_LOW_VALUE) {
          dResultHighValue = GRID_LOW_VALUE;
          iResultStepSize = GRID_LOW_VALUE_STEP_SIZE;
        }
        else if (dHightestResultValue <= GRID_MID_VALUE) {
          dResultHighValue = GRID_MID_VALUE;
          iResultStepSize = GRID_MID_VALUE_STEP_SIZE;
        }
        else if (dHightestResultValue <= GRID_SECOND_HIGH_VALUE) {
          dResultHighValue = GRID_SECOND_HIGH_VALUE;
          iResultStepSize = GRID_SECOND_HIGH_VALUE_STEP_SIZE;
        }
        else {
          dResultHighValue = GRID_HIGH_VALUE;
          iResultStepSize = GRID_HIGH_VALUE_STEP_SIZE;
        }

        ParallelTransition* pGridScaleElementsParallelTransition = new ParallelTransition();
        pGridScaleAtomicSequentialTransition->AddTransition(pGridScaleElementsParallelTransition);

        //scale every data element
        for (unsigned int i = 0; i < vInitialDataElements.size(); i++) {
          Bar* pDataElement = (Bar*) vInitialDataElements.at(i);

          if (bScaleDown) {
            DataEntry* pResultDataEntry = GetDataEntryByName(vResultDataEntries, pDataElement->GetDataEntry()->GetName());
            Bar* pResultDataElement = (Bar*) pResultChart->GetConnectedDataElement(pResultDataEntry);

            pGridScaleElementsParallelTransition->AddTransition(new ScaleTransition(pDataElement,
              0.0f, pResultDataElement->GetHeight() - pResultDataElement->GetDataEntry()->GetValue() /
              dInitialHighValue * (UPPER_Y - ORIGIN_Y), FILTERING_SCALE_STEPS));
          }
          else {
            pGridScaleElementsParallelTransition->AddTransition(new ScaleTransition(pDataElement,
              0.0f, pDataElement->GetDataEntry()->GetValue() / dResultHighValue * (UPPER_Y - ORIGIN_Y) -
              pDataElement->GetHeight(), FILTERING_SCALE_STEPS));
          }
        }

        //add new invisible grid lines if upscaling
        if (!bScaleDown) {
          int iNewGridLines = (int) (dResultHighValue - dInitialHighValue) / iInitialStepSize;

          for (int i = 0; i < iNewGridLines; i++) {
            GridLine* pGridLine = new GridLine(GRID_LINES * iInitialStepSize + (i + 1) * iInitialStepSize, GRID_LINES + i + 1);
            pGridLine->ChangeAlpha(-1.0f);
            pInitialChart->AddGridLine(pGridLine);
          }
        }

        //move every grid line
        std::vector<GridLine*> vGridLines = pInitialChart->GetGridLines();

        for (unsigned int i = 0; i < vGridLines.size(); i++) {
          GridLine* pGridLine = vGridLines.at(i);

          if (bScaleDown) {
            pGridScaleElementsParallelTransition->AddTransition(new MoveTransition(pGridLine,
              0.0f, pGridLine->GetValue() / dInitialHighValue * (UPPER_Y - ORIGIN_Y), 0.0f, false));

            //add new invisible grid lines if downscaling
            for (int j = 1; j < GRID_LINES + 1; j++) {
              GridLine* pGridLine = new GridLine(j * iResultStepSize, j);
              pGridLine->ChangeAlpha(-1.0f);
              pInitialChart->AddGridLine(pGridLine);
            }
          }
          else {
            ParallelTransition* pBlendInAndMoveGridLineParallelTransition = new ParallelTransition();
            pGridScaleElementsParallelTransition->AddTransition(pBlendInAndMoveGridLineParallelTransition);

            AtomicSequentialTransition* pBlendInAndMoveGridLineAtomicSequentialTransition = new AtomicSequentialTransition();
            pBlendInAndMoveGridLineParallelTransition->AddTransition(pBlendInAndMoveGridLineAtomicSequentialTransition);

            //blend in the new grid lines delayed
            if (i >= GRID_LINES) {
              pBlendInAndMoveGridLineAtomicSequentialTransition->AddTransition(new Delay(NEW_GRID_LINE_DELAY_IN_MS * (i - GRID_LINES)));
              pBlendInAndMoveGridLineAtomicSequentialTransition->AddTransition(new BlendTransition(pGridLine, 1.0f));
            }

            pBlendInAndMoveGridLineParallelTransition->AddTransition(new MoveTransition(pGridLine,
              0.0f, -pGridLine->GetValue() / dResultHighValue * (UPPER_Y - ORIGIN_Y), 0.0f, false));
          }
        }

        //new grid lines might have been added
        vGridLines = pInitialChart->GetGridLines();

        //fade out the obsolete and fade in the additional grid lines
        ParallelTransition* pFadeOutGridLinesParallelTransition = new ParallelTransition();
        pGridScaleAtomicSequentialTransition->AddTransition(pFadeOutGridLinesParallelTransition);

        for (unsigned int i = 0; i < vGridLines.size(); i++) {
          GridLine* pGridLine = vGridLines.at(i);

          //blend out the new grid lines delayed
          if ((!bScaleDown) && ((int) pGridLine->GetValue() % iResultStepSize != 0)) {
            pFadeOutGridLinesParallelTransition->AddTransition(new BlendTransition(pGridLine, -1.0f));
          }
          else if (bScaleDown) {
            if (pGridLine->GetValue() > dResultHighValue) {
              pFadeOutGridLinesParallelTransition->AddTransition(new BlendTransition(pGridLine, -1.0f));
            }
            else {
              pFadeOutGridLinesParallelTransition->AddTransition(new BlendTransition(pGridLine, 1.0f));
            }
          }
        }

        //scale the grid lines first if upscaling
        if (bScaleDown) {
          pAtomicSequentialTransition->AddTransition(pScaleAndSortAtomicSequentialTransition);
          pAtomicSequentialTransition->AddTransition(pGridScaleAtomicSequentialTransition);
        }
        else {
          pAtomicSequentialTransition->AddTransition(pGridScaleAtomicSequentialTransition);
          pAtomicSequentialTransition->AddTransition(pScaleAndSortAtomicSequentialTransition);
        }
      }
      else {
        pAtomicSequentialTransition->AddTransition(pScaleAndSortAtomicSequentialTransition);
      }

      //parallel transition to scale all elements at once
      ParallelTransition* pScalingParallelTransition = new ParallelTransition();
      pScaleAndSortAtomicSequentialTransition->AddTransition(pScalingParallelTransition);

      //parallel transition to move all elements at once
      ParallelTransition* pMovingParallelTransition = new ParallelTransition();
      pScaleAndSortAtomicSequentialTransition->AddTransition(pMovingParallelTransition);

      for (unsigned int i = 0; i < vInitialDataElements.size(); i++) {
        //match the data elements of the initial and result chart by comparing the names of their connected data entries
        Bar* pInitialDataElement = (Bar*) vInitialDataElements.at(i);
        DataEntry* pResultDataEntry = GetDataEntryByName(vResultDataEntries, pInitialDataElement->GetDataEntry()->GetName());
        Bar* pResultDataElement = (Bar*) pResultChart->GetConnectedDataElement(pResultDataEntry);

        //scale the element
        if ((dInitialHighValue == 0.0) || (dResultHighValue == 0.0)) {
          pScalingParallelTransition->AddTransition(new ScaleTransition(pInitialDataElement,
            0.0f, pResultDataElement->GetHeight() - pInitialDataElement->GetHeight(), VALUE_CHANGE_SCALE_STEPS));
        }
        else {
          if (bScaleDown) {
            pScalingParallelTransition->AddTransition(new ScaleTransition(pInitialDataElement,
              0.0f, pResultDataElement->GetDataEntry()->GetValue() / dInitialHighValue * (UPPER_Y - ORIGIN_Y) - pInitialDataElement->GetHeight(),
              VALUE_CHANGE_SCALE_STEPS));
          }
          else {
            pScalingParallelTransition->AddTransition(new ScaleTransition(pInitialDataElement,
              0.0f, pResultDataElement->GetHeight() - pInitialDataElement->GetDataEntry()->GetValue() / dResultHighValue * (UPPER_Y - ORIGIN_Y),
              VALUE_CHANGE_SCALE_STEPS));
          }
        }

        //move the element on the x axis for sorting
        if (pResultDataElement->GetLeft() - pInitialDataElement->GetLeft() != 0.0f) {
          pMovingParallelTransition->AddTransition(new MoveTransition(pInitialDataElement,
            pResultDataElement->GetLeft() - pInitialDataElement->GetLeft(), 0.0f, 0.0f, false));
        }
      }

      //fade in the labels (headline) of the result chart
      ParallelTransition* pFadeInLabelsParallelTransition = new ParallelTransition();
      pAtomicSequentialTransition->AddTransition(pFadeInLabelsParallelTransition);

      std::vector<Label*> vResultLabels = pResultChart->GetLabels();

      if (vResultLabels.size() == vInitialLabels.size()) {
        for (unsigned int i = 0; i < vInitialLabels.size(); i++) {
          Label* pLabel = new Label(vResultLabels.at(i)->GetText(), vResultLabels.at(i)->GetStyle(),
            vResultLabels.at(i)->GetLeft(), vResultLabels.at(i)->GetTop(), vResultLabels.at(i)->GetOrientation(),
            vResultLabels.at(i)->GetAlignment());
          pInitialChart->AddLabel(pLabel);
          pLabel->ChangeAlpha(-1.0f);
          pFadeInLabelsParallelTransition->AddTransition(new BlendTransition(pLabel, 1.0f));
        }
      }

      break;
    }
    case GROUPED_BAR_CHART: {
      pResultChart = new GroupedBarChart(pNewDataSet);
      std::vector<DataElement*> vInitialAllBars = ((GroupedBarChart*) pInitialChart)->GetAllBars();
      std::vector<DataEntry*> vResultDataEntries = pResultChart->GetDataSet()->GetAllDataEntries();

      //sequential transition order grid rescaling and scaling/sorting of the data elements
      AtomicSequentialTransition* pAtomicSequentialTransition = new AtomicSequentialTransition();
      AddTransition(pAtomicSequentialTransition);

      AtomicSequentialTransition* pGridScaleAtomicSequentialTransition = NULL;

      //fade out the labels (headline) of the chart
      ParallelTransition* pFadeOutLabelsParallelTransition = new ParallelTransition();
      pAtomicSequentialTransition->AddTransition(pFadeOutLabelsParallelTransition);

      std::vector<Label*> vInitialLabels = pInitialChart->GetLabels();

      for (unsigned int i = 0; i < vInitialLabels.size(); i++) {
        pFadeOutLabelsParallelTransition->AddTransition(new BlendTransition(vInitialLabels.at(i), -1.0f));
      }

      //check if the grid has to be rescaled
      double dHightestInitialValue = pInitialChart->GetHighestVisibleValue();
      double dHightestResultValue = pResultChart->GetHighestVisibleValue();

      bool bGridScale = false;
      bool bScaleDown = false;

      if (((dHightestInitialValue <= GRID_LOW_VALUE) && (dHightestResultValue > GRID_LOW_VALUE)) ||
        ((dHightestInitialValue <= GRID_MID_VALUE) && (dHightestResultValue > GRID_MID_VALUE))) {
        bGridScale = true;
        bScaleDown = false;
      }
      else if (((dHightestResultValue <= GRID_LOW_VALUE) && (dHightestInitialValue > GRID_LOW_VALUE)) ||
        ((dHightestResultValue <= GRID_MID_VALUE) && (dHightestInitialValue > GRID_MID_VALUE))) {
        bGridScale = true;
        bScaleDown = true;
      }

      //sequential transition to sort the elements after scaling
      AtomicSequentialTransition* pScaleAndSortAtomicSequentialTransition = new AtomicSequentialTransition();

      double dResultHighValue = 0.0;
      double dInitialHighValue = 0.0;

      if (bGridScale) {
        pGridScaleAtomicSequentialTransition = new AtomicSequentialTransition();

        //calculate the grid scaling factor
        int iInitialStepSize = 0;

        if (dHightestInitialValue <= GRID_LOW_VALUE) {
          dInitialHighValue = GRID_LOW_VALUE;
          iInitialStepSize = GRID_LOW_VALUE_STEP_SIZE;
        }
        else if (dHightestInitialValue <= GRID_MID_VALUE) {
          dInitialHighValue = GRID_MID_VALUE;
          iInitialStepSize = GRID_MID_VALUE_STEP_SIZE;
        }
        else if (dHightestInitialValue <= GRID_SECOND_HIGH_VALUE) {
          dInitialHighValue = GRID_SECOND_HIGH_VALUE;
          iInitialStepSize = GRID_SECOND_HIGH_VALUE_STEP_SIZE;
        }
        else {
          dInitialHighValue = GRID_HIGH_VALUE;
          iInitialStepSize = GRID_HIGH_VALUE_STEP_SIZE;
        }

        int iResultStepSize = 0;

        if (dHightestResultValue <= GRID_LOW_VALUE) {
          dResultHighValue = GRID_LOW_VALUE;
          iResultStepSize = GRID_LOW_VALUE_STEP_SIZE;
        }
        else if (dHightestResultValue <= GRID_MID_VALUE) {
          dResultHighValue = GRID_MID_VALUE;
          iResultStepSize = GRID_MID_VALUE_STEP_SIZE;
        }
        else if (dHightestResultValue <= GRID_SECOND_HIGH_VALUE) {
          dResultHighValue = GRID_SECOND_HIGH_VALUE;
          iResultStepSize = GRID_SECOND_HIGH_VALUE_STEP_SIZE;
        }
        else {
          dResultHighValue = GRID_HIGH_VALUE;
          iResultStepSize = GRID_HIGH_VALUE_STEP_SIZE;
        }

        ParallelTransition* pGridScaleElementsParallelTransition = new ParallelTransition();
        pGridScaleAtomicSequentialTransition->AddTransition(pGridScaleElementsParallelTransition);

        //scale every data element
        for (unsigned int i = 0; i < vInitialAllBars.size(); i++) {
          Bar* pDataElement = (Bar*) vInitialAllBars.at(i);

          if (bScaleDown) {
            DataEntry* pResultDataEntry = GetDataEntryByName(vResultDataEntries, pDataElement->GetDataEntry()->GetName());
            Bar* pResultDataElement = (Bar*) ((GroupedBarChart*) pResultChart)->GetConnectedBar(pResultDataEntry);

            pGridScaleElementsParallelTransition->AddTransition(new ScaleTransition(pDataElement,
              0.0f, pResultDataElement->GetHeight() - pResultDataElement->GetDataEntry()->GetValue() /
              dInitialHighValue * (UPPER_Y - ORIGIN_Y), FILTERING_SCALE_STEPS));
          }
          else {
            pGridScaleElementsParallelTransition->AddTransition(new ScaleTransition(pDataElement,
              0.0f, pDataElement->GetDataEntry()->GetValue() / dResultHighValue * (UPPER_Y - ORIGIN_Y) -
              pDataElement->GetHeight(), FILTERING_SCALE_STEPS));
          }
        }

        //add new invisible grid lines if upscaling
        if (!bScaleDown) {
          int iNewGridLines = (int) (dResultHighValue - dInitialHighValue) / iInitialStepSize;

          for (int i = 0; i < iNewGridLines; i++) {
            GridLine* pGridLine = new GridLine(GRID_LINES * iInitialStepSize + (i + 1) * iInitialStepSize, GRID_LINES + i + 1);
            pGridLine->ChangeAlpha(-1.0f);
            pInitialChart->AddGridLine(pGridLine);
          }
        }

        //move every grid line
        std::vector<GridLine*> vGridLines = pInitialChart->GetGridLines();

        for (unsigned int i = 0; i < vGridLines.size(); i++) {
          GridLine* pGridLine = vGridLines.at(i);

          if (bScaleDown) {
            pGridScaleElementsParallelTransition->AddTransition(new MoveTransition(pGridLine,
              0.0f, pGridLine->GetValue() / dInitialHighValue * (UPPER_Y - ORIGIN_Y), 0.0f, false));

            //add new invisible grid lines if downscaling
            for (int j = 1; j < GRID_LINES + 1; j++) {
              GridLine* pGridLine = new GridLine(j * iResultStepSize, j);
              pGridLine->ChangeAlpha(-1.0f);
              pInitialChart->AddGridLine(pGridLine);
            }
          }
          else {
            ParallelTransition* pBlendInAndMoveGridLineParallelTransition = new ParallelTransition();
            pGridScaleElementsParallelTransition->AddTransition(pBlendInAndMoveGridLineParallelTransition);

            AtomicSequentialTransition* pBlendInAndMoveGridLineAtomicSequentialTransition = new AtomicSequentialTransition();
            pBlendInAndMoveGridLineParallelTransition->AddTransition(pBlendInAndMoveGridLineAtomicSequentialTransition);

            //blend in the new grid lines delayed
            if (i >= GRID_LINES) {
              pBlendInAndMoveGridLineAtomicSequentialTransition->AddTransition(new Delay(NEW_GRID_LINE_DELAY_IN_MS * (i - GRID_LINES)));
              pBlendInAndMoveGridLineAtomicSequentialTransition->AddTransition(new BlendTransition(pGridLine, 1.0f));
            }

            pBlendInAndMoveGridLineParallelTransition->AddTransition(new MoveTransition(pGridLine,
              0.0f, -pGridLine->GetValue() / dResultHighValue * (UPPER_Y - ORIGIN_Y), 0.0f, false));
          }
        }

        //new grid lines might have been added, update vector
        vGridLines = pInitialChart->GetGridLines();

        //fade out the obsolete and fade in the additional grid lines
        ParallelTransition* pFadeOutGridLinesParallelTransition = new ParallelTransition();
        pGridScaleAtomicSequentialTransition->AddTransition(pFadeOutGridLinesParallelTransition);

        for (unsigned int i = 0; i < vGridLines.size(); i++) {
          GridLine* pGridLine = vGridLines.at(i);

          //blend out the new grid lines delayed
          if ((!bScaleDown) && ((int) pGridLine->GetValue() % iResultStepSize != 0)) {
            pFadeOutGridLinesParallelTransition->AddTransition(new BlendTransition(pGridLine, -1.0f));
          }
          else if (bScaleDown) {
            if (pGridLine->GetValue() > dResultHighValue) {
              pFadeOutGridLinesParallelTransition->AddTransition(new BlendTransition(pGridLine, -1.0f));
            }
            else {
              pFadeOutGridLinesParallelTransition->AddTransition(new BlendTransition(pGridLine, 1.0f));
            }
          }
        }

        //scale the grid lines first if upscaling
        if (bScaleDown) {
          pAtomicSequentialTransition->AddTransition(pScaleAndSortAtomicSequentialTransition);
          pAtomicSequentialTransition->AddTransition(pGridScaleAtomicSequentialTransition);
        }
        else {
          pAtomicSequentialTransition->AddTransition(pGridScaleAtomicSequentialTransition);
          pAtomicSequentialTransition->AddTransition(pScaleAndSortAtomicSequentialTransition);
        }
      }
      else {
        pAtomicSequentialTransition->AddTransition(pScaleAndSortAtomicSequentialTransition);
      }

      //parallel transition to scale all elements at once
      ParallelTransition* pScalingParallelTransition = new ParallelTransition();
      pScaleAndSortAtomicSequentialTransition->AddTransition(pScalingParallelTransition);

      //parallel transition to move all elements at once
      ParallelTransition* pMovingParallelTransition = new ParallelTransition();
      pScaleAndSortAtomicSequentialTransition->AddTransition(pMovingParallelTransition);

      for (unsigned int i = 0; i < vInitialAllBars.size(); i++) {
        //match the data elements of the initial and result chart by comparing the names of their connected data entries
        Bar* pInitialDataElement = (Bar*) vInitialAllBars.at(i);
        DataEntry* pResultDataEntry = GetDataEntryByName(vResultDataEntries, pInitialDataElement->GetDataEntry()->GetName());
        Bar* pResultDataElement = (Bar*) ((GroupedBarChart*) pResultChart)->GetConnectedBar(pResultDataEntry);

        //scale the element
        if ((dInitialHighValue == 0.0) || (dResultHighValue == 0.0)) {
          pScalingParallelTransition->AddTransition(new ScaleTransition(pInitialDataElement,
            0.0f, pResultDataElement->GetHeight() - pInitialDataElement->GetHeight(), VALUE_CHANGE_SCALE_STEPS));
        }
        else {
          if (bScaleDown) {
            pScalingParallelTransition->AddTransition(new ScaleTransition(pInitialDataElement,
              0.0f, pResultDataElement->GetDataEntry()->GetValue() / dInitialHighValue * (UPPER_Y - ORIGIN_Y) - pInitialDataElement->GetHeight(),
              VALUE_CHANGE_SCALE_STEPS));
          }
          else {
            pScalingParallelTransition->AddTransition(new ScaleTransition(pInitialDataElement,
              0.0f, pResultDataElement->GetHeight() - pInitialDataElement->GetDataEntry()->GetValue() / dResultHighValue * (UPPER_Y - ORIGIN_Y),
              VALUE_CHANGE_SCALE_STEPS));
          }
        }

        //move the element on the x axis for sorting
        if (pResultDataElement->GetLeft() - pInitialDataElement->GetLeft() != 0.0f) {
          pMovingParallelTransition->AddTransition(new MoveTransition(pInitialDataElement,
            pResultDataElement->GetLeft() - pInitialDataElement->GetLeft(), 0.0f, 0.0f, false));
        }
      }

      //fade in the labels (headline) of the result chart
      ParallelTransition* pFadeInLabelsParallelTransition = new ParallelTransition();
      pAtomicSequentialTransition->AddTransition(pFadeInLabelsParallelTransition);

      std::vector<Label*> vResultLabels = pResultChart->GetLabels();

      if (vResultLabels.size() == vInitialLabels.size()) {
        for (unsigned int i = 0; i < vInitialLabels.size(); i++) {
          Label* pLabel = new Label(vResultLabels.at(i)->GetText(), vResultLabels.at(i)->GetStyle(),
            vResultLabels.at(i)->GetLeft(), vResultLabels.at(i)->GetTop(), vResultLabels.at(i)->GetOrientation(),
            vResultLabels.at(i)->GetAlignment());
          pInitialChart->AddLabel(pLabel);
          pLabel->ChangeAlpha(-1.0f);
          pFadeInLabelsParallelTransition->AddTransition(new BlendTransition(pLabel, 1.0f));
        }
      }

      break;
    }
    case STACKED_BAR_CHART: {
      pResultChart = new StackedBarChart(pNewDataSet);
      std::vector<DataElement*> vInitialStackedBars = pInitialChart->GetDataElements();
      std::vector<DataEntry*> vResultDataEntries = pResultChart->GetDataSet()->GetAllDataEntries();

      //sequential transition order grid rescaling and scaling/sorting of the data elements
      AtomicSequentialTransition* pAtomicSequentialTransition = new AtomicSequentialTransition();
      AddTransition(pAtomicSequentialTransition);

      AtomicSequentialTransition* pGridScaleAtomicSequentialTransition = NULL;

      //fade out the labels (headline) of the chart
      ParallelTransition* pFadeOutLabelsParallelTransition = new ParallelTransition();
      pAtomicSequentialTransition->AddTransition(pFadeOutLabelsParallelTransition);

      std::vector<Label*> vInitialLabels = pInitialChart->GetLabels();

      for (unsigned int i = 0; i < vInitialLabels.size(); i++) {
        pFadeOutLabelsParallelTransition->AddTransition(new BlendTransition(vInitialLabels.at(i), -1.0f));
      }

      //check if the grid has to be rescaled
      double dHightestInitialValue = ((StackedBarChart*) pInitialChart)->GetHighestVisibleSum();
      double dHightestResultValue = ((StackedBarChart*) pResultChart)->GetHighestVisibleSum();

      bool bGridScale = false;
      bool bScaleDown = false;

      if (((dHightestInitialValue <= GRID_LOW_VALUE) && (dHightestResultValue > GRID_LOW_VALUE)) ||
        ((dHightestInitialValue <= GRID_MID_VALUE) && (dHightestResultValue > GRID_MID_VALUE)) ||
        ((dHightestInitialValue <= GRID_SECOND_HIGH_VALUE) && (dHightestResultValue > GRID_SECOND_HIGH_VALUE))) {
        bGridScale = true;
        bScaleDown = false;
      }
      else if (((dHightestResultValue <= GRID_LOW_VALUE) && (dHightestInitialValue > GRID_LOW_VALUE)) ||
        ((dHightestResultValue <= GRID_MID_VALUE) && (dHightestInitialValue > GRID_MID_VALUE)) ||
        ((dHightestResultValue <= GRID_SECOND_HIGH_VALUE) && (dHightestInitialValue > GRID_SECOND_HIGH_VALUE))) {
        bGridScale = true;
        bScaleDown = true;
      }

      //sequential transition to sort the elements after scaling
      AtomicSequentialTransition* pScaleAndSortAtomicSequentialTransition = new AtomicSequentialTransition();

      double dResultHighValue = 0.0;
      double dInitialHighValue = 0.0;

      if (bGridScale) {
        pGridScaleAtomicSequentialTransition = new AtomicSequentialTransition();

        //calculate the grid scaling factor
        int iInitialStepSize = 0;

        if (dHightestInitialValue <= GRID_LOW_VALUE) {
          dInitialHighValue = GRID_LOW_VALUE;
          iInitialStepSize = GRID_LOW_VALUE_STEP_SIZE;
        }
        else if (dHightestInitialValue <= GRID_MID_VALUE) {
          dInitialHighValue = GRID_MID_VALUE;
          iInitialStepSize = GRID_MID_VALUE_STEP_SIZE;
        }
        else if (dHightestInitialValue <= GRID_SECOND_HIGH_VALUE) {
          dInitialHighValue = GRID_SECOND_HIGH_VALUE;
          iInitialStepSize = GRID_SECOND_HIGH_VALUE_STEP_SIZE;
        }
        else {
          dInitialHighValue = GRID_HIGH_VALUE;
          iInitialStepSize = GRID_HIGH_VALUE_STEP_SIZE;
        }

        int iResultStepSize = 0;

        if (dHightestResultValue <= GRID_LOW_VALUE) {
          dResultHighValue = GRID_LOW_VALUE;
          iResultStepSize = GRID_LOW_VALUE_STEP_SIZE;
        }
        else if (dHightestResultValue <= GRID_MID_VALUE) {
          dResultHighValue = GRID_MID_VALUE;
          iResultStepSize = GRID_MID_VALUE_STEP_SIZE;
        }
        else if (dHightestResultValue <= GRID_SECOND_HIGH_VALUE) {
          dResultHighValue = GRID_SECOND_HIGH_VALUE;
          iResultStepSize = GRID_SECOND_HIGH_VALUE_STEP_SIZE;
        }
        else {
          dResultHighValue = GRID_HIGH_VALUE;
          iResultStepSize = GRID_HIGH_VALUE_STEP_SIZE;
        }

        ParallelTransition* pGridScaleElementsParallelTransition = new ParallelTransition();
        pGridScaleAtomicSequentialTransition->AddTransition(pGridScaleElementsParallelTransition);

        //scale every data element
        for (unsigned int i = 0; i < vInitialStackedBars.size(); i++) {
          StackedBar* pInitialStackedBar = (StackedBar*) vInitialStackedBars.at(i);
          std::vector<Bar*> vInitialBars = pInitialStackedBar->GetBars();

          //sequential transition to scale all elements of a stacked bar
          AtomicSequentialTransition* pScalingAtomicSequentialTransition = new AtomicSequentialTransition();
          pGridScaleElementsParallelTransition->AddTransition(pScalingAtomicSequentialTransition);

          //scale on bar of the stacked bar at the time
          for (unsigned int j = 0; j < vInitialBars.size(); j++) {
            Bar* pInitialBar = vInitialBars.at(j);

            //get the matching bar
            DataEntry* pResultDataEntry = GetDataEntryByName(vResultDataEntries, pInitialBar->GetDataEntry()->GetName());
            Bar* pResultBar = (Bar*) ((StackedBarChart*) pResultChart)->GetConnectedBar(pResultDataEntry);

            if (pResultBar != NULL) {
              ParallelTransition* pGridScaleSingleElementParallelTransition = new ParallelTransition();
              pScalingAtomicSequentialTransition->AddTransition(pGridScaleSingleElementParallelTransition);

              if (bScaleDown) {
                pGridScaleSingleElementParallelTransition->AddTransition(new ScaleTransition(pInitialBar,
                  0.0f, pResultBar->GetHeight() - pResultBar->GetDataEntry()->GetValue() /
                  dInitialHighValue * (UPPER_Y - ORIGIN_Y), FILTERING_SCALE_STEPS));
              }
              else {
                pGridScaleElementsParallelTransition->AddTransition(new ScaleTransition(pInitialBar,
                  0.0f, pInitialBar->GetDataEntry()->GetValue() / dResultHighValue * (UPPER_Y - ORIGIN_Y) -
                  pInitialBar->GetHeight(), FILTERING_SCALE_STEPS));
              }

              //move all other elements on the y axis
              for (unsigned int k = j + 1; k < vInitialBars.size(); k++) {
                pGridScaleSingleElementParallelTransition->AddTransition(new MoveTransition(vInitialBars.at(k),
                  0.0f, pResultBar->GetHeight() - pInitialBar->GetHeight(), pResultBar->GetHeight() - pInitialBar->GetHeight(),
                  false, FILTERING_SCALE_STEPS));
              }
            }
          }
        }

        //add new invisible grid lines if upscaling
        if (!bScaleDown) {
          int iNewGridLines = (int) (dResultHighValue - dInitialHighValue) / iInitialStepSize;

          for (int i = 0; i < iNewGridLines; i++) {
            GridLine* pGridLine = new GridLine(GRID_LINES * iInitialStepSize + (i + 1) * iInitialStepSize, GRID_LINES + i + 1);
            pGridLine->ChangeAlpha(-1.0f);
            pInitialChart->AddGridLine(pGridLine);
          }
        }

        //move every grid line
        std::vector<GridLine*> vGridLines = pInitialChart->GetGridLines();

        for (unsigned int i = 0; i < vGridLines.size(); i++) {
          GridLine* pGridLine = vGridLines.at(i);

          if (bScaleDown) {
            pGridScaleElementsParallelTransition->AddTransition(new MoveTransition(pGridLine,
              0.0f, pGridLine->GetValue() / dInitialHighValue * (UPPER_Y - ORIGIN_Y), 0.0f, false));

            //add new invisible grid lines if downscaling
            for (int j = 1; j < GRID_LINES + 1; j++) {
              GridLine* pGridLine = new GridLine(j * iResultStepSize, j);
              pGridLine->ChangeAlpha(-1.0f);
              pInitialChart->AddGridLine(pGridLine);
            }
          }
          else {
            ParallelTransition* pBlendInAndMoveGridLineParallelTransition = new ParallelTransition();
            pGridScaleElementsParallelTransition->AddTransition(pBlendInAndMoveGridLineParallelTransition);

            AtomicSequentialTransition* pBlendInAndMoveGridLineAtomicSequentialTransition = new AtomicSequentialTransition();
            pBlendInAndMoveGridLineParallelTransition->AddTransition(pBlendInAndMoveGridLineAtomicSequentialTransition);

            //blend in the new grid lines delayed
            if (i >= GRID_LINES) {
              pBlendInAndMoveGridLineAtomicSequentialTransition->AddTransition(new Delay(NEW_GRID_LINE_DELAY_IN_MS * (i - GRID_LINES)));
              pBlendInAndMoveGridLineAtomicSequentialTransition->AddTransition(new BlendTransition(pGridLine, 1.0f));
            }

            pBlendInAndMoveGridLineParallelTransition->AddTransition(new MoveTransition(pGridLine,
              0.0f, (pGridLine->GetValue() / dResultHighValue - pGridLine->GetValue() / dInitialHighValue) * (UPPER_Y - ORIGIN_Y), 0.0f, false));
          }
        }

        //new grid lines might have been added, update vector
        vGridLines = pInitialChart->GetGridLines();

        //fade out the obsolete and fade in the additional grid lines
        ParallelTransition* pFadeOutGridLinesParallelTransition = new ParallelTransition();
        pGridScaleAtomicSequentialTransition->AddTransition(pFadeOutGridLinesParallelTransition);

        for (unsigned int i = 0; i < vGridLines.size(); i++) {
          GridLine* pGridLine = vGridLines.at(i);

          //blend out the new grid lines delayed
          if ((!bScaleDown) && ((int) pGridLine->GetValue() % iResultStepSize != 0)) {
            pFadeOutGridLinesParallelTransition->AddTransition(new BlendTransition(pGridLine, -1.0f));
          }
          else if (bScaleDown) {
            if (pGridLine->GetValue() > dResultHighValue) {
              pFadeOutGridLinesParallelTransition->AddTransition(new BlendTransition(pGridLine, -1.0f));
            }
            else {
              pFadeOutGridLinesParallelTransition->AddTransition(new BlendTransition(pGridLine, 1.0f));
            }
          }
        }

        //scale the grid lines first if upscaling
        if (bScaleDown) {
          pAtomicSequentialTransition->AddTransition(pScaleAndSortAtomicSequentialTransition);
          pAtomicSequentialTransition->AddTransition(pGridScaleAtomicSequentialTransition);
        }
        else {
          pAtomicSequentialTransition->AddTransition(pGridScaleAtomicSequentialTransition);
          pAtomicSequentialTransition->AddTransition(pScaleAndSortAtomicSequentialTransition);
        }
      }
      else {
        pAtomicSequentialTransition->AddTransition(pScaleAndSortAtomicSequentialTransition);
      }
        
      //explode stacked bars
      ParallelTransition* pExplodeTransition = new ParallelTransition();
      pScaleAndSortAtomicSequentialTransition->AddTransition(pExplodeTransition);

      //move back elements transition
      ParallelTransition* pMoveBackTransition = new ParallelTransition();

      double dResultHighValueForExplode = 0.0;

      if (dHightestResultValue <= GRID_LOW_VALUE) {
        dResultHighValueForExplode = GRID_LOW_VALUE;
      }
      else if (dHightestResultValue <= GRID_MID_VALUE) {
        dResultHighValueForExplode = GRID_MID_VALUE;
      }
      else if (dHightestResultValue <= GRID_SECOND_HIGH_VALUE) {
        dResultHighValueForExplode = GRID_SECOND_HIGH_VALUE;
      }
      else {
        dResultHighValueForExplode = GRID_HIGH_VALUE;
      }

      int iBarsInBiggestStack = 1;

      for (unsigned int i = 0; i < vInitialStackedBars.size(); i++) {
        StackedBar* pInitialStackedBar = (StackedBar*) vInitialStackedBars.at(i);
        std::vector<Bar*> vInitialBars = pInitialStackedBar->GetBars();

        if ((int) vInitialBars.size() > iBarsInBiggestStack) {
          iBarsInBiggestStack = vInitialBars.size();
        }
      }

      float fSpace = (dResultHighValueForExplode - dHightestInitialValue) / dResultHighValueForExplode / (iBarsInBiggestStack - 1);

      for (unsigned int i = 0; i < vInitialStackedBars.size(); i++) {
        StackedBar* pInitialStackedBar = (StackedBar*) vInitialStackedBars.at(i);
        std::vector<Bar*> vInitialBars = pInitialStackedBar->GetBars();

        for (unsigned int j = 1; j < vInitialBars.size(); j++) {
          Bar* pInitialBar = vInitialBars.at(j);

          pExplodeTransition->AddTransition(new MoveTransition(pInitialBar,
            0.0f, fSpace * j, fSpace * j, false, VALUE_CHANGE_SCALE_STEPS));
          pMoveBackTransition->AddTransition(new MoveTransition(pInitialBar,
            0.0f, -fSpace * j, -fSpace * j, false, VALUE_CHANGE_SCALE_STEPS));
        }
      }

      //scale the elements
      ParallelTransition* pScaleTransition = new ParallelTransition();
      pScaleAndSortAtomicSequentialTransition->AddTransition(pScaleTransition);

      for (unsigned int i = 0; i < vInitialStackedBars.size(); i++) {
        StackedBar* pInitialStackedBar = (StackedBar*) vInitialStackedBars.at(i);
        std::vector<Bar*> vInitialBars = pInitialStackedBar->GetBars();

        for (unsigned int j = 0; j < vInitialBars.size(); j++) {
          Bar* pInitialBar = vInitialBars.at(j);
          DataEntry* pResultDataEntry = GetDataEntryByName(vResultDataEntries, pInitialBar->GetDataEntry()->GetName());
          Bar* pResultBar = (Bar*) ((StackedBarChart*) pResultChart)->GetConnectedBar(pResultDataEntry);

          pScaleTransition->AddTransition(new ScaleTransition(pInitialBar,
            0.0f, pResultBar->GetHeight() - pInitialBar->GetHeight(), VALUE_CHANGE_SCALE_STEPS));
          pMoveBackTransition->AddTransition(new MoveTransition(pInitialBar,
            0.0f, -pResultBar->GetHeight() + pInitialBar->GetHeight(),
            0.0f, false, VALUE_CHANGE_SWAP_STEPS));
        }
      }

      //swap the elements
      ParallelTransition* pSwappingParallelTransition = new ParallelTransition();
      pScaleAndSortAtomicSequentialTransition->AddTransition(pSwappingParallelTransition);

      for (unsigned int i = 0; i < vInitialStackedBars.size(); i++) {
        StackedBar* pInitialStackedBar = (StackedBar*) vInitialStackedBars.at(i);
        std::vector<Bar*> vInitialBars = pInitialStackedBar->GetBars();

        for (unsigned int j = 0; j < vInitialBars.size(); j++) {
          Bar* pInitialBar = vInitialBars.at(j);
          DataEntry* pResultDataEntry = GetDataEntryByName(vResultDataEntries, pInitialBar->GetDataEntry()->GetName());
          Bar* pResultBar = (Bar*) ((StackedBarChart*) pResultChart)->GetConnectedBar(pResultDataEntry);

          unsigned int uInitialIndex = ((StackedBarChart*) pInitialChart)->GetIndexOfBar(pInitialBar);
          unsigned int uResultIndex = ((StackedBarChart*) pResultChart)->GetIndexOfBar(pResultBar);

          if ((uInitialIndex != -1) && (uResultIndex != -1) && (uInitialIndex != uResultIndex)) {
            AtomicSequentialTransition* pSwapBarsAtomicSequentialTransition = new AtomicSequentialTransition();
            pSwappingParallelTransition->AddTransition(pSwapBarsAtomicSequentialTransition);

            float fDeltaTop = (pResultBar->GetTop() + uResultIndex * fSpace) - (pInitialBar->GetTop() + uInitialIndex * fSpace);
            float fDeltaBottom = (pResultBar->GetBottom() + uResultIndex * fSpace) - (pInitialBar->GetBottom() + uInitialIndex * fSpace);

            if (uInitialIndex > uResultIndex) {
              pSwapBarsAtomicSequentialTransition->AddTransition(new MoveTransition(pInitialBar,
                pInitialBar->GetWidth() / 2.0f,
                fDeltaTop / 2.0f,
                fDeltaBottom / 2.0f,
                false, VALUE_CHANGE_SWAP_STEPS));
              pSwapBarsAtomicSequentialTransition->AddTransition(new MoveTransition(pInitialBar,
                -pInitialBar->GetWidth() / 2.0f,
                fDeltaTop / 2.0f,
                fDeltaBottom / 2.0f,
                false, VALUE_CHANGE_SWAP_STEPS));

              pMoveBackTransition->AddTransition(new MoveTransition(pInitialBar,
                0.0f, -pResultBar->GetTop() + pInitialBar->GetTop() + (uInitialIndex - uResultIndex) * fSpace,
                -pResultBar->GetBottom() + pInitialBar->GetBottom() + (uInitialIndex - uResultIndex) * fSpace, false, VALUE_CHANGE_SWAP_STEPS));
            }
            else {
              pSwapBarsAtomicSequentialTransition->AddTransition(new MoveTransition(pInitialBar,
                -pInitialBar->GetWidth() / 2.0f,
                fDeltaTop / 2.0f,
                fDeltaBottom / 2.0f,
                false, VALUE_CHANGE_SWAP_STEPS));
              pSwapBarsAtomicSequentialTransition->AddTransition(new MoveTransition(pInitialBar,
                pInitialBar->GetWidth() / 2.0f,
                fDeltaTop / 2.0f,
                fDeltaBottom / 2.0f,
                false, VALUE_CHANGE_SWAP_STEPS));

              pMoveBackTransition->AddTransition(new MoveTransition(pInitialBar,
                0.0f, -pResultBar->GetTop() + pInitialBar->GetTop() + uInitialIndex * fSpace - uResultIndex * fSpace,
                -pResultBar->GetBottom() + pInitialBar->GetBottom() + uInitialIndex * fSpace - uResultIndex * fSpace, false, VALUE_CHANGE_SWAP_STEPS));
            }
          }
        }
      }

      for (unsigned int i = 0; i < vInitialStackedBars.size(); i++) {
        StackedBar* pInitialStackedBar = (StackedBar*) vInitialStackedBars.at(i);
        std::vector<Bar*> vInitialBars = pInitialStackedBar->GetBars();

        for (unsigned int j = 0; j < vInitialBars.size(); j++) {
          Bar* pInitialBar = vInitialBars.at(j);
          DataEntry* pResultDataEntry = GetDataEntryByName(vResultDataEntries, pInitialBar->GetDataEntry()->GetName());
          Bar* pResultBar = (Bar*) ((StackedBarChart*) pResultChart)->GetConnectedBar(pResultDataEntry);

          pMoveBackTransition->AddTransition(new MoveTransition(pInitialBar,
            0.0f, pResultBar->GetTop() - pInitialBar->GetTop(),
            pResultBar->GetBottom() - pInitialBar->GetBottom(), false, VALUE_CHANGE_SWAP_STEPS));
        }
      }

      pScaleAndSortAtomicSequentialTransition->AddTransition(pMoveBackTransition);

      //fade in the labels of the result chart
      ParallelTransition* pFadeInLabelsParallelTransition = new ParallelTransition();
      pAtomicSequentialTransition->AddTransition(pFadeInLabelsParallelTransition);

      std::vector<Label*> vResultLabels = pResultChart->GetLabels();

      if (vResultLabels.size() == vInitialLabels.size()) {
        for (unsigned int i = 0; i < vInitialLabels.size(); i++) {
          Label* pLabel = new Label(vResultLabels.at(i)->GetText(), vResultLabels.at(i)->GetStyle(),
            vResultLabels.at(i)->GetLeft(), vResultLabels.at(i)->GetTop(), vResultLabels.at(i)->GetOrientation(),
            vResultLabels.at(i)->GetAlignment());
          pInitialChart->AddLabel(pLabel);
          pLabel->ChangeAlpha(-1.0f);
          pFadeInLabelsParallelTransition->AddTransition(new BlendTransition(pLabel, 1.0f));
        }
      }

      break;
    }
    case PIE_CHART: {
      pResultChart = new PieChart(pNewDataSet);
      std::vector<DataElement*> vInitialPieSectors = pInitialChart->GetDataElements();
      std::vector<DataElement*> vResultPieSectors = pResultChart->GetDataElements();
      std::vector<DataEntry*> vResultDataEntries = pResultChart->GetDataSet()->GetAllDataEntries();

      //sequential transition order label blending and scaling/sorting of the data elements
      AtomicSequentialTransition* pAtomicSequentialTransition = new AtomicSequentialTransition();
      AddTransition(pAtomicSequentialTransition);

      //fade out the labels (headline and group names) of the chart
      ParallelTransition* pFadeOutLabelsParallelTransition = new ParallelTransition();
      pAtomicSequentialTransition->AddTransition(pFadeOutLabelsParallelTransition);

      std::vector<Label*> vInitialLabels = pInitialChart->GetLabels();

      for (unsigned int i = 0; i < vInitialLabels.size(); i++) {
        pFadeOutLabelsParallelTransition->AddTransition(new BlendTransition(vInitialLabels.at(i), -1.0f));
      }

      for (unsigned int i = 0; i < vInitialPieSectors.size(); i++) {
        pFadeOutLabelsParallelTransition->AddTransition(new BlendTransition(((PieSector*) vInitialPieSectors.at(i))->GetLabel(), -1.0f));
      }

      //sequential transition to sort the elements after scaling
      AtomicSequentialTransition* pScaleAndSortAtomicSequentialTransition = new AtomicSequentialTransition();
      pAtomicSequentialTransition->AddTransition(pScaleAndSortAtomicSequentialTransition);

      //parallel transition to do sequential scaling in all pie sectors at the same time
      ParallelTransition* pAllPieSlicesScalingTransition = new ParallelTransition();
      pScaleAndSortAtomicSequentialTransition->AddTransition(pAllPieSlicesScalingTransition);

      for (unsigned int i = 0; i < vInitialPieSectors.size(); i++) {
        PieSector* pInitialPieSector = (PieSector*) vInitialPieSectors.at(i);
        std::vector<PieSlice*> vInitialPieSlices = pInitialPieSector->GetSlices();

        //scale and move all slices
        for (unsigned int j = 0; j < vInitialPieSlices.size(); j++) {
          PieSlice* pInitialSlice = vInitialPieSlices.at(j);

          //get the matching slice
          DataEntry* pResultDataEntry = GetDataEntryByName(vResultDataEntries, pInitialSlice->GetDataEntry()->GetName());
          PieSlice* pResultSlice = (PieSlice*) ((PieChart*) pResultChart)->GetConnectedSlice(pResultDataEntry);

          if (pResultSlice != NULL) {
            //sequential transition to scale all elements of a pie sector
            AtomicSequentialTransition* pScalingAtomicSequentialTransition = new AtomicSequentialTransition();
            pAllPieSlicesScalingTransition->AddTransition(pScalingAtomicSequentialTransition);

            //parallel transition to scale and move the initial slice
            ParallelTransition* pScaleAndRotateTransition = new ParallelTransition();
            pScalingAtomicSequentialTransition->AddTransition(pScaleAndRotateTransition);

            //scale the slice to a third of its size and move it to the middle of the space
            pScaleAndRotateTransition->AddTransition(new ScaleTransition(pInitialSlice,
              -pInitialSlice->GetWidth() * 2.0f / 3.0f, 0.0f, VALUE_CHANGE_SCALE_STEPS));
            pScaleAndRotateTransition->AddTransition(new RotateTransition(pInitialSlice,
              pInitialSlice->GetWidth() / 3.0f, VALUE_CHANGE_SCALE_STEPS));

            //add a small delay to gain an overview over the changes
            pScalingAtomicSequentialTransition->AddTransition(new Delay(250));

            //parallel transition to scale and move the slice to a third of its result value
            ParallelTransition* pScaleAndRotateAfterwardsTransition = new ParallelTransition();
            pScalingAtomicSequentialTransition->AddTransition(pScaleAndRotateAfterwardsTransition);

            //scale the slice to a third of its new size and move it to the middle of the space again
            pScaleAndRotateAfterwardsTransition->AddTransition(new ScaleTransition(pInitialSlice,
              -(pResultSlice->GetWidth() - pInitialSlice->GetWidth()) / 3.0f, 0.0f, VALUE_CHANGE_SCALE_STEPS));
            pScaleAndRotateAfterwardsTransition->AddTransition(new RotateTransition(pInitialSlice,
              (pResultSlice->GetWidth() - pInitialSlice->GetWidth()) / 6.0f, VALUE_CHANGE_SCALE_STEPS));

            //add a small delay to gain an overview over the changes
            pScalingAtomicSequentialTransition->AddTransition(new Delay(250));

            //move the slice to the middle of its new location
            pScalingAtomicSequentialTransition->AddTransition(new RotateTransition(pInitialSlice,
              (pResultSlice->GetBeginValue() + pResultSlice->GetWidth() / 3.0f) -
              ((pInitialSlice->GetBeginValue() + pInitialSlice->GetWidth() / 3.0f) -
              (pResultSlice->GetWidth() - pInitialSlice->GetWidth()) / 3.0f + 
              (pResultSlice->GetWidth() - pInitialSlice->GetWidth()) / 6.0f), VALUE_CHANGE_SCALE_STEPS));

            //add a small delay to gain an overview over the changes
            pScalingAtomicSequentialTransition->AddTransition(new Delay(150));

            //parallel transition to scale and move the slice to its full result value
            ParallelTransition* pScaleAndRotateFinalTransition = new ParallelTransition();
            pScalingAtomicSequentialTransition->AddTransition(pScaleAndRotateFinalTransition);

            //rescale the slice to its new value and rotate it
            pScaleAndRotateFinalTransition->AddTransition(new ScaleTransition(pInitialSlice,
              pResultSlice->GetWidth() * 2.0f / 3.0f + 2.0f * (pResultSlice->GetWidth() - pInitialSlice->GetWidth()) / 3.0f,
              0.0f, VALUE_CHANGE_SCALE_STEPS));
            pScaleAndRotateFinalTransition->AddTransition(new RotateTransition(pInitialSlice,
              -pResultSlice->GetWidth() / 3.0f - (pResultSlice->GetWidth() - pInitialSlice->GetWidth()) / 3.0f,
              VALUE_CHANGE_SCALE_STEPS));
          }
        }
      }

      //fade in the labels (headline and group names) of the result chart
      ParallelTransition* pFadeInLabelsParallelTransition = new ParallelTransition();
      pAtomicSequentialTransition->AddTransition(pFadeInLabelsParallelTransition);

      std::vector<Label*> vResultLabels = pResultChart->GetLabels();

      if (vResultLabels.size() == vInitialLabels.size()) {
        for (unsigned int i = 0; i < vInitialLabels.size(); i++) {
          Label* pLabel = new Label(vResultLabels.at(i)->GetText(), vResultLabels.at(i)->GetStyle(),
            vResultLabels.at(i)->GetLeft(), vResultLabels.at(i)->GetTop(), vResultLabels.at(i)->GetOrientation(),
            vResultLabels.at(i)->GetAlignment());
          pInitialChart->AddLabel(pLabel);
          pLabel->ChangeAlpha(-1.0f);
          pFadeInLabelsParallelTransition->AddTransition(new BlendTransition(pLabel, 1.0f));
        }
      }

      for (unsigned int i = 0; i < vResultPieSectors.size(); i++) {
        Label* pResultLabel = ((PieSector*) vResultPieSectors.at(i))->GetLabel();
        Label* pNewLabel = new Label(pResultLabel->GetText(), pResultLabel->GetStyle(),
          pResultLabel->GetLeft(), pResultLabel->GetTop(), pResultLabel->GetOrientation(),
          pResultLabel->GetAlignment());
        pNewLabel->ChangeAlpha(-1.0f);

        pInitialChart->AddLabel(pNewLabel);

        pFadeInLabelsParallelTransition->AddTransition(new BlendTransition(pNewLabel, 1.0f));
      }

      break;
    }
    default: {
      //this should not be possible, fall back to bar chart
      pResultChart = new BarChart(pNewDataSet);
    }
  }
}

ValueChangeTransition::~ValueChangeTransition() {
  while (vChildren.size() > 0) {
    std::vector<Transition*>::iterator it = vChildren.begin();

    if (it != vChildren.end()) {
      delete *it;
      vChildren.erase(it);
    }
  }

  while (vToBeDeleted.size() > 0) {
    std::vector<AlterableObject*>::iterator it = vToBeDeleted.begin();

    if (it != vToBeDeleted.end()) {
      delete *it;
      vToBeDeleted.erase(it);
    }
  }
}

void ValueChangeTransition::Update() {
  if ((vChildren.size() > 0) && (vChildren.at(0)->Complete())) {
    delete *vChildren.begin();
    vChildren.erase(vChildren.begin());
  }

  if (vChildren.size() > 0) {
    vChildren.at(0)->Update();
  }
}

void ValueChangeTransition::Draw(QPainter* pPainter) {
  pInitialChart->Draw(pPainter);
  pInitialChart->DrawLabels(pPainter);
}

bool ValueChangeTransition::Complete() {
  for (unsigned int i = 0; i < vChildren.size(); i++) {
    if (!vChildren.at(i)->Complete()) {
      return false;
    }
  }

  return true;
}

DataEntry* ValueChangeTransition::GetDataEntryByName(std::vector<DataEntry*> vDataEntries, std::string sName) {
  for (unsigned int i = 0; i < vDataEntries.size(); i++) {
    DataEntry* pDataEntry = vDataEntries.at(i);

    if (pDataEntry->GetName().compare(sName) == 0) {
      return pDataEntry;
    }
  }

  return NULL;
}

Chart* ValueChangeTransition::GetActiveChart() {
  return pInitialChart;
}