#include "StackedBarChart.hpp"
#include <algorithm>
#include "StackedBar.hpp"

/*
   Function: CompareLessStackedBarChart

     Global 'less' compare function for data entries by their values.

   Parameters:

     pFirstEntry - The first data entry to be compared.
     pSecondEntry - The second data entry to be compared.

   Returns:

     A boolean value indicating if the first entry's value is smaller than the second entry's.
*/
bool CompareLessStackedBarChart(DataEntry* pFirstEntry, DataEntry* pSecondEntry) {
  return pFirstEntry->GetValue() < pSecondEntry->GetValue();
}

/*
   Function: CompareGreaterStackedBarChart

     Global 'greater' compare function for data entries by their values.

   Parameters:

     pFirstEntry - The first data entry to be compared.
     pSecondEntry - The second data entry to be compared.

   Returns:

     A boolean value indicating if the first entry's value is greater than the second entry's.
*/
bool CompareGreaterStackedBarChart(DataEntry* pFirstEntry, DataEntry* pSecondEntry) {
  return pFirstEntry->GetValue() > pSecondEntry->GetValue();
}

StackedBarChart::StackedBarChart(DataSet* pDataSet) {
  iType = STACKED_BAR_CHART;
  this->pDataSet = pDataSet;

  //get data entries
  vVisibleDataEntries = pDataSet->GetAllDataEntries();
  vVisibleDataGroups = GetVisibleDataGroups(pDataSet->GetDataGroups());

  //create new temporary data groups which only hold visible entries
  std::vector<DataGroup*> vNewVisibleDataGroups;

  for (unsigned int i = 0; i < vVisibleDataGroups.size(); i++) {
    DataGroup* pCurrentDataGroup = vVisibleDataGroups.at(i);
    DataGroup* pNewDataGroup = new DataGroup(pCurrentDataGroup);
    vNewVisibleDataGroups.push_back(pNewDataGroup);
    
    std::vector<DataEntry*> vDataEntriesOfCurrentGroup = pCurrentDataGroup->GetDataEntries();

    //always sort the entries inside a stacked bar descending, i.e. small values are on top
    std::sort(vDataEntriesOfCurrentGroup.begin(), vDataEntriesOfCurrentGroup.end(), CompareGreaterStackedBarChart);

    //add the visible sorted entries to the temporary data group
    for (unsigned int j = 0; j < vDataEntriesOfCurrentGroup.size(); j++) {
      DataEntry* pDataEntry = vDataEntriesOfCurrentGroup.at(j);

      if (ContainsDataEntry(vVisibleDataEntries, pDataEntry)) {
        pNewDataGroup->AddDataEntry(pDataEntry);
      }
    }
  }

  vVisibleDataGroups = vNewVisibleDataGroups;
 
  QString sText = QObject::tr(pDataSet->GetName().c_str());

  //headline
  vLabels.push_back(new Label(sText, FONTSTYLE_HEADLINE, WIDTH / 2, 30, ORIENTATION_HORIZONTAL, Qt::AlignCenter));

  //x-axis
  vAxes.push_back(new Axis(ORIENTATION_HORIZONTAL));

  //y-axis
  vAxes.push_back(new Axis(ORIENTATION_VERTICAL, QObject::tr(pDataSet->GetUnit().c_str())));

  //grid
  double dHighestValue = GetHighestVisibleSum();
  double dHighestGridValue = 0.0;
  int iStepSize = 0;

  if (dHighestValue <= GRID_LOW_VALUE) {
    iStepSize = GRID_LOW_VALUE_STEP_SIZE;
    dHighestGridValue = GRID_LOW_VALUE;
  }
  else if (dHighestValue <= GRID_MID_VALUE) {
    iStepSize = GRID_MID_VALUE_STEP_SIZE;
    dHighestGridValue = GRID_MID_VALUE;
  }
  else if (dHighestValue <= GRID_SECOND_HIGH_VALUE) {
    iStepSize = GRID_SECOND_HIGH_VALUE_STEP_SIZE;
    dHighestGridValue = GRID_SECOND_HIGH_VALUE;
  }
  else {
    iStepSize = GRID_HIGH_VALUE_STEP_SIZE;
    dHighestGridValue = GRID_HIGH_VALUE;
  }

  for (int i = 1; i < GRID_LINES + 1; i++) {
    vGridLines.push_back(new GridLine(i * iStepSize, i));
  }

  //data elements
  float fWidth = 2.0f * (UPPER_X - ORIGIN_X - 2.0f * BIG_SPACE) / (3.0f * vVisibleDataGroups.size() - 1.0f);
  float fSpace = fWidth / 2.0f;
  float fLeft = BIG_SPACE + ORIGIN_X;

  for (unsigned int i = 0; i < vVisibleDataGroups.size(); i++) {
    vDataElements.push_back(new StackedBar(vVisibleDataGroups.at(i), fLeft + i * (fWidth + fSpace), fWidth, dHighestGridValue));
  }
}

StackedBarChart::~StackedBarChart() {
  while (vLabels.size() > 0) {
    std::vector<Label*>::iterator it = vLabels.begin();

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

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

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

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

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

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

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

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

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

void StackedBarChart::Draw(QPainter* pPainter) {
  for (std::vector<GridLine*>::iterator it = vGridLines.begin(); it != vGridLines.end(); it++) {
    (*it)->Draw(pPainter);
  }

  if (pDataSet->GetSortingOrder()) {
    for (int i = (int) vDataElements.size() - 1; i >= 0; i--) {
      vDataElements.at(i)->Draw(pPainter);
    }
  }
  else {
    for (unsigned int i = 0; i < vDataElements.size(); i++) {
      vDataElements.at(i)->Draw(pPainter);
    }
  }

  for (std::vector<Axis*>::iterator it = vAxes.begin(); it != vAxes.end(); it++) {
    (*it)->Draw(pPainter);
  }
}

void StackedBarChart::DrawLabels(QPainter* pPainter) {
  for (std::vector<GridLine*>::iterator it = vGridLines.begin(); it != vGridLines.end(); it++) {
    (*it)->DrawLabels(pPainter);
  }
  
  for (std::vector<Label*>::iterator it = vLabels.begin(); it != vLabels.end(); it++) {
    (*it)->Draw(pPainter);
  }

  for (std::vector<DataElement*>::iterator it = vDataElements.begin(); it != vDataElements.end(); it++) {
    (*it)->DrawLabels(pPainter);
  }

  for (std::vector<Axis*>::iterator it = vAxes.begin(); it != vAxes.end(); it++) {
    (*it)->DrawLabels(pPainter);
  }
}

void StackedBarChart::ChangePosition(float fDeltaLeft, float fDeltaTop, float fDeltaBottom) {
  UNREFERENCED(fDeltaLeft);
  UNREFERENCED(fDeltaTop);
  UNREFERENCED(fDeltaBottom);
}

void StackedBarChart::ChangeAlpha(float fDeltaAlpha) {
  UNREFERENCED(fDeltaAlpha);
}

std::vector<DataGroup*> StackedBarChart::GetVisibleDataGroups(std::vector<DataGroup*> vAllDataGroups) {
  std::vector<DataGroup*> vDataGroups;

  for (unsigned int i = 0; i < vAllDataGroups.size(); i++) {
    DataGroup* pDataGroup = vAllDataGroups.at(i);
    std::vector<DataEntry*> vDataEntriesOfGroup = pDataGroup->GetDataEntries();

    for (unsigned int j = 0; j < vDataEntriesOfGroup.size(); j++) {
      DataEntry* pDataEntryOfGroup = vDataEntriesOfGroup.at(j);

      if (ContainsDataEntry(vVisibleDataEntries, pDataEntryOfGroup)) {
        vDataGroups.push_back(pDataGroup);

        break;
      }
    }
  }

  return vDataGroups;
}

bool StackedBarChart::ContainsDataEntry(std::vector<DataEntry*> vDataEntries, DataEntry* pDataEntry) {
  for (unsigned int i = 0; i < vDataEntries.size(); i++) {
    DataEntry* pCurrentDataEntry = vDataEntries.at(i);

    if (pCurrentDataEntry == pDataEntry) {
      return true;
    }
  }

  return false;
}

double StackedBarChart::GetHighestVisibleSum() {
  double dHighestSum = 0.0;

  for (unsigned int i = 0; i < vVisibleDataGroups.size(); i++) {
    double dSum = vVisibleDataGroups.at(i)->SumValues();

    if (dSum > dHighestSum) {
      dHighestSum = dSum;
    }
  }

  return dHighestSum;
}

DataElement* StackedBarChart::GetConnectedDataElementByDataGroup(DataGroup* pDataGroup) {
  for (unsigned int i = 0; i < vDataElements.size(); i++) {
    DataElement* pCurrentDataElement = vDataElements.at(i);

    if (pCurrentDataElement->GetType() == STACKED_BAR) {
      StackedBar* pStackedBar = (StackedBar*) pCurrentDataElement;

      DataGroup* pFirstDataGroup = pDataGroup;

      if (pFirstDataGroup->IsCopy()) {
        pFirstDataGroup = pFirstDataGroup->GetReferenceDataGroup();
      }

      DataGroup* pSecondDataGroup = pStackedBar->GetDataGroup();

      if (pSecondDataGroup->IsCopy()) {
        pSecondDataGroup = pSecondDataGroup->GetReferenceDataGroup();
      }

      if (pFirstDataGroup == pSecondDataGroup) {
        return pCurrentDataElement;
      }
    }
  }

  return NULL;
}

DataElement* StackedBarChart::GetConnectedBar(DataEntry* pDataEntry) {
  for (unsigned int i = 0; i < vDataElements.size(); i++) {
    DataElement* pCurrentDataElement = vDataElements.at(i);

    if (pCurrentDataElement->GetType() == STACKED_BAR) {
      StackedBar* pStackedBar = (StackedBar*) pCurrentDataElement;
      std::vector<Bar*> vBars = pStackedBar->GetBars();
      
      for (unsigned int j = 0; j < vBars.size(); j++) {
        if (vBars.at(j)->GetDataEntry() == pDataEntry) {
          return vBars.at(j);
        }
      }
    }
  }

  return NULL;
}

std::vector<DataElement*> StackedBarChart::GetAllBars() {
  std::vector<DataElement*> vAllBars;

  for (unsigned int i = 0; i < vVisibleDataEntries.size(); i++) {
    DataEntry* pCurrentDataEntry = vVisibleDataEntries.at(i);
    DataElement* pCurrentDataElement = GetConnectedBar(pCurrentDataEntry);

    if (pCurrentDataElement != NULL) {
      vAllBars.push_back(pCurrentDataElement);
    }
  }

  return vAllBars;
}

unsigned int StackedBarChart::GetIndexOfBar(DataElement* pDataElement) {
  for (unsigned int i = 0; i < vDataElements.size(); i++) {
    DataElement* pCurrentDataElement = vDataElements.at(i);

    if (pCurrentDataElement->GetType() == STACKED_BAR) {
      StackedBar* pStackedBar = (StackedBar*) pCurrentDataElement;
      std::vector<Bar*> vBars = pStackedBar->GetBars();
      
      for (unsigned int j = 0; j < vBars.size(); j++) {
        if (vBars.at(j) == pDataElement) {
          return j;
        }
      }
    }
  }

  return -1;
}