#include "SortingTransition.hpp"
#include "ParallelTransition.hpp"
#include "AtomicSequentialTransition.hpp"
#include "Delay.hpp"
#include "MoveTransition.hpp"
#include "ScaleTransition.hpp"
#include "BlendTransition.hpp"
#include "GroupedBar.hpp"
#include "StackedBar.hpp"
#include "PieSector.hpp"

SortingTransition::SortingTransition(Chart* pInitialChart) {
  iType = SORTING_TRANSITION;
  bStarted = false;
  this->pInitialChart = pInitialChart;
  bool bDescending = pInitialChart->GetDataSet()->GetSortingOrder();

  std::vector<DataElement*> vInitialDataElements = pInitialChart->GetDataElements();

  switch (this->pInitialChart->GetType()) {
    case BAR_CHART: {
      pResultChart = new BarChart(pInitialChart->GetDataSet());

      //parallel transition to move all elements at once
      ParallelTransition* pParallelTransition = new ParallelTransition();
      AddTransition(pParallelTransition);

      for (unsigned int i = 0; i < vInitialDataElements.size(); i++) {
        DataElement* pResultDataElement = pResultChart->GetConnectedDataElement(vInitialDataElements.at(i)->GetDataEntry());

        if (pResultDataElement != NULL) {
          //sequential transition to delay each element according to its position
          AtomicSequentialTransition* pAtomicSequentialTransition = new AtomicSequentialTransition();
          pParallelTransition->AddTransition(pAtomicSequentialTransition);

          if (bDescending) {
            pAtomicSequentialTransition->AddTransition(new Delay(SORTING_DELAY_IN_MS * i));
          }
          else {
            pAtomicSequentialTransition->AddTransition(new Delay(SORTING_DELAY_IN_MS * (vInitialDataElements.size() - i)));
          }

          //move each element
          if (pResultDataElement->GetLeft() != vInitialDataElements.at(i)->GetLeft()) {
            pAtomicSequentialTransition->AddTransition(new MoveTransition(vInitialDataElements.at(i),
              pResultDataElement->GetLeft() - vInitialDataElements.at(i)->GetLeft(),
              pResultDataElement->GetTop() - vInitialDataElements.at(i)->GetTop(),
              pResultDataElement->GetBottom() - vInitialDataElements.at(i)->GetBottom(), true));
          }
        }
      }

      break;
    }
    case GROUPED_BAR_CHART: {
      pResultChart = new GroupedBarChart(pInitialChart->GetDataSet());

      //parallel transition for sorting
      ParallelTransition* pParallelTransition = new ParallelTransition();
      AddTransition(pParallelTransition);

      //grouped bar sorting
      for (unsigned int i = 0; i < vInitialDataElements.size(); i++) {
        GroupedBar* pInitialGroupedBar = (GroupedBar*) vInitialDataElements.at(i);
        DataGroup* pDataGroup = pInitialGroupedBar->GetDataGroup();

        if (pDataGroup != NULL) {
          //get the matching group bar
          DataElement* pResultDataElement = ((GroupedBarChart*) pResultChart)->GetConnectedDataElementByDataGroup(pDataGroup);

          if (pResultDataElement != NULL) {
            //move all bars of the group at once
            ParallelTransition* pMoveAllBarsParallelTransition = new ParallelTransition();
            pParallelTransition->AddTransition(pMoveAllBarsParallelTransition);

            if ((pResultDataElement->GetType() == GROUPED_BAR) && (pInitialGroupedBar->GetType() == GROUPED_BAR) &&
              (pResultDataElement->GetLeft() != pInitialGroupedBar->GetLeft())) {
              std::vector<Bar*> vInitialBars = pInitialGroupedBar->GetBars();

              for (unsigned int j = 0; j < vInitialBars.size(); j++) {
                //get the matching bar
                DataElement* pInnerResultDataElement = ((GroupedBarChart*) pResultChart)->GetConnectedBar(vInitialBars.at(j)->GetDataEntry());

                pMoveAllBarsParallelTransition->AddTransition(new MoveTransition(vInitialBars.at(j),
                  pInnerResultDataElement->GetLeft() - vInitialBars.at(j)->GetLeft(),
                  pInnerResultDataElement->GetTop() - vInitialBars.at(j)->GetTop(),
                  pInnerResultDataElement->GetBottom() - vInitialBars.at(j)->GetBottom(), true));
              }
            }
          }
        }
      }

      break;
    }
    case STACKED_BAR_CHART: {
      pResultChart = new StackedBarChart(pInitialChart->GetDataSet());

      //parallel transition for sorting
      ParallelTransition* pParallelTransition = new ParallelTransition();
      AddTransition(pParallelTransition);

      //stacked bar sorting
      for (unsigned int i = 0; i < vInitialDataElements.size(); i++) {
        StackedBar* pInitialStackedBar = (StackedBar*) vInitialDataElements.at(i);
        DataGroup* pDataGroup = pInitialStackedBar->GetDataGroup();

        if (pDataGroup != NULL) {
          //get the matching stacked bar
          DataElement* pResultDataElement = ((StackedBarChart*) pResultChart)->GetConnectedDataElementByDataGroup(pDataGroup);

          if (pResultDataElement != NULL) {
            //sequential transition to delay each element according to its position
            AtomicSequentialTransition* pStackedBarAtomicSequentialTransition = new AtomicSequentialTransition();
            pParallelTransition->AddTransition(pStackedBarAtomicSequentialTransition);

            if (bDescending) {
              pStackedBarAtomicSequentialTransition->AddTransition(new Delay(SORTING_DELAY_IN_MS * i));
            }
            else {
              pStackedBarAtomicSequentialTransition->AddTransition(new Delay(SORTING_DELAY_IN_MS * (vInitialDataElements.size() - i)));
            }

            //move all bars of the group at once
            ParallelTransition* pMoveAllBarsParallelTransition = new ParallelTransition();
            pStackedBarAtomicSequentialTransition->AddTransition(pMoveAllBarsParallelTransition);

            if ((pInitialStackedBar->GetType() == STACKED_BAR) &&
              (pResultDataElement->GetLeft() != pInitialStackedBar->GetLeft())) {
              std::vector<Bar*> vInitialBars = pInitialStackedBar->GetBars();

              //move every bar of the stacked bar to the same left value
              for (unsigned int j = 0; j < vInitialBars.size(); j++) {
                pMoveAllBarsParallelTransition->AddTransition(new MoveTransition(vInitialBars.at(j),
                  pResultDataElement->GetLeft() - pInitialStackedBar->GetLeft(), 0.0f, 0.0f, true));
              }

              //move the stacked bar label with the bars
              pMoveAllBarsParallelTransition->AddTransition(new MoveTransition(pInitialStackedBar,
                pResultDataElement->GetLeft() - pInitialStackedBar->GetLeft(), 0.0f, 0.0f, true));
            }
          }
        }
      }

      break;
    }
    case PIE_CHART: {
      pResultChart = new PieChart(pInitialChart->GetDataSet());

      //get all slices in the right order
      std::vector<PieSlice*> vInitialSlices = ((PieChart*) pInitialChart)->GetAllSlicesGroupSorted();
      std::vector<DataElement*> vInitialDataElements = pInitialChart->GetDataElements();
      std::vector<DataElement*> vResultDataElements = pResultChart->GetDataElements();

      //sequential transition for sorting
      AtomicSequentialTransition* pAtomicSequentialTransition = new AtomicSequentialTransition();
      AddTransition(pAtomicSequentialTransition);

      //fade out the labels
      ParallelTransition* pFadeLabelsOutTransition = new ParallelTransition();
      pAtomicSequentialTransition->AddTransition(pFadeLabelsOutTransition);

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

      //swap the whole chart for sorting
      ParallelTransition* pPositiveScaleParallelTransition = new ParallelTransition();
      pAtomicSequentialTransition->AddTransition(pPositiveScaleParallelTransition);

      ParallelTransition* pSwapParallelTransition = new ParallelTransition();
      pAtomicSequentialTransition->AddTransition(pSwapParallelTransition);

      ParallelTransition* pNegativeScaleParallelTransition = new ParallelTransition();
      pAtomicSequentialTransition->AddTransition(pNegativeScaleParallelTransition);

      for (unsigned int i = 0; i < vInitialSlices.size(); i++) {
        PieSlice* pInitialSlice = vInitialSlices.at(i);

        pPositiveScaleParallelTransition->AddTransition(new MoveTransition(pInitialSlice, -PIE_CHART_SCALE_MAXIMUM, 0.0f, 0.0f, false, MOVE_STEPS));
        pSwapParallelTransition->AddTransition(new MoveTransition(pInitialSlice, 2.0f * (PIE_CHART_SCALE_MAXIMUM + WIDTH), 0.0f, 0.0f, false, 1));
        pNegativeScaleParallelTransition->AddTransition(new MoveTransition(pInitialSlice, -PIE_CHART_SCALE_MAXIMUM, 0.0f, 0.0f, false, MOVE_STEPS));
      }

      //fade in the new labels
      ParallelTransition* pFadeLabelsInTransition = new ParallelTransition();
      pAtomicSequentialTransition->AddTransition(pFadeLabelsInTransition);

      for (unsigned int i = 0; i < vResultDataElements.size(); i++) {
        Label* pResultLabel = ((PieSector*) vResultDataElements.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);

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

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

SortingTransition::~SortingTransition() {
  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 SortingTransition::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 SortingTransition::Draw(QPainter* pPainter) {
  pInitialChart->Draw(pPainter);
  pInitialChart->DrawLabels(pPainter);
}

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

  return true;
}

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