#include "GraphicTidListBar.hpp"

//////////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS & DESTRUCTOR

GraphicTidListBar::GraphicTidListBar(QGraphicsItem * parent) :
    QGraphicsItem(parent),
    pInputDagsMap(0),
    pTidList(0),
    size(1000, 40),
    barHeight(20),
    tickHeight(5),
    fontSize(10),
    colorMode(GraphicTidListBar::LINEAR_GRAYSCALE)
{

}


GraphicTidListBar::~GraphicTidListBar() {

}


//////////////////////////////////////////////////////////////////////////////
// METHODS

/**
 * @reimp
 */
QRectF GraphicTidListBar::boundingRect() const {
  return QRectF(0, 0, size.width(), size.height());
}


/**
 * @reimp
 */
void GraphicTidListBar::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *widget) {

  Q_UNUSED(option);
  Q_UNUSED(widget);

  if (isReady()) {

    // Draw bar content
    //qDebug() << "rate 0 :" << ratesList[0];
    for (int i = 0; i < ratesList.size(); i++) {
      painter->setPen(calculateColor(ratesList[i]));
      painter->drawLine(i, 0, i, barHeight);
    }

    // Draw ruler base
    painter->setPen(Qt::black);
    painter->drawLine(0, barHeight + 2, ratesList.size(), barHeight + 2);

    // Draw ruler ticks
    int numberOfInputDags = boolMap.size();
    int rulerSteps = 10;
    int stepSize = numberOfInputDags / rulerSteps;
    int stepPixelSize = stepSize / dagsPerPixel;

    for (int i = 0; i < ratesList.size(); i++) {
      if (i % stepPixelSize == 0) {
        painter->setPen(Qt::black);
        painter->drawLine(i, barHeight + 2, i, barHeight + 2 + tickHeight);
        painter->drawText(i + 2, barHeight + 12, QString::number(i * dagsPerPixel));
      }
    }


  }

  //  painter->setPen(Qt::red);
  //  painter->drawRect(boundingRect());
}


/**
 * Resets the current boolean map with false values for every key.
 */
void GraphicTidListBar::resetBoolMap() {

  for (int i = 0; i < boolMap.size(); i++)
    boolMap[i] = false;

}


/**
 * Builds a list of N double values, where N is the width of the display area.
 * Each value represents the intensity of a pixel and the amount of inputs DAGs
 * that belong to the pattern's TID list.
 * If display width is 0 the display is ignored.
 */
void GraphicTidListBar::updateRatesList() {

  if (size.width() > 0) {

    // Clear all previous display values (empty list)
    ratesList.clear();

    // DAGs per pixel (rounded up)
    dagsPerPixel = boolMap.size() / size.width() + 1;
    //qDebug() << "dags per pixel :" << dagsPerPixel;

    double inputsRate = 0;
    int inputsRead = 0;

    for (int i = 0; i < boolMap.size(); i++, inputsRead++) {

      if (boolMap[i]) {
        inputsRate++;
      }

      if (inputsRead % dagsPerPixel == 0) {
        ratesList.append(inputsRate / inputsRead);
        inputsRate = 0;
        inputsRead = 0;
      }

    }

    // Repaint object
    update();

  }

}


/**
 * Calculates a color depending on the color mode and the parameter value.
 * Depending on color mode, the color can be a grayscale, or a colorscale.
 *
 * @param value A double value between 0 and 1.
 * @return A color
 */
QColor GraphicTidListBar::calculateColor(double value) {

  QColor color;

  switch(colorMode) {

    // Linear 5 colors gradient :
    // red 100% -> yellow 75% -> green 50% -> cyan 25% -> blue 0%
  case LINEAR_5_COLORS :
    if (value <= 1 && value >= 0.75)
      // yellow -> red
      color = colorLinearInterpolation(Qt::yellow, Qt::red, value);
    else if (value < 0.75 && value >= 0.5)
      // green -> yellow
      color = colorLinearInterpolation(Qt::green, Qt::yellow, value);
    else if (value < 0.5 && value >= 0.25)
      // cyan -> green
      color = colorLinearInterpolation(Qt::cyan, Qt::green, value);
    else if (value < 0.25 && value >= 0 )
      // blue -> cyan
      color = colorLinearInterpolation(Qt::blue, Qt::cyan, value);
    break;

    // Non-linear 5 colors gradient :
    // red 100% -> yellow 87.5% -> green 75% -> cyan 50% -> blue 0%
  case NONLINEAR_5_COLORS :
    if (value <= 1 && value >= 0.875)
      // yellow -> red
      color = colorLinearInterpolation(Qt::yellow, Qt::red, value);
    else if (value < 0.875 && value >= 0.75)
      // green -> yellow
      color = colorLinearInterpolation(Qt::green, Qt::yellow, value);
    else if (value < 0.75 && value >= 0.5)
      // cyan -> green
      color = colorLinearInterpolation(Qt::cyan, Qt::green, value);
    else if (value < 0.5 && value >= 0 )
      // blue -> cyan
      color = colorLinearInterpolation(Qt::blue, Qt::cyan, value);
    break;

    // Linear 6 colors gradient :
    // red 100% -> yellow 75% -> green 50% -> cyan 25% -> blue 0%
  case LINEAR_6_COLORS :
    if (value <= 1 && value >= 0.9375)
      color = colorLinearInterpolation(Qt::yellow, Qt::red, value);
    else if (value < 0.9375 && value >= 0.875)
      color = colorLinearInterpolation(Qt::green, Qt::yellow, value);
    else if (value < 0.875 && value >= 0.75)
      color = colorLinearInterpolation(Qt::cyan, Qt::green, value);
    else if (value < 0.75 && value >= 0.5 )
      color = colorLinearInterpolation(Qt::blue, Qt::cyan, value);
    else if (value < 0.5 && value >= 0 )
      color = colorLinearInterpolation(Qt::black, Qt::blue, value);
    break;

    // Non-linear 5 colors gradient :
    // red 100% -> yellow 87.5% -> green 75% -> cyan 50% -> blue 0%
  case NONLINEAR_5_COLORS_2 :
    if (value <= 1 && value >= 0.9375)
      color = colorLinearInterpolation(Qt::yellow, Qt::green, value);
    else if (value < 0.9375 && value >= 0.875)
      color = colorLinearInterpolation(Qt::red, Qt::yellow, value);
    else if (value < 0.875 && value >= 0.75)
      color = colorLinearInterpolation(Qt::darkMagenta, Qt::red, value);
    else if (value < 0.75 && value >= 0.5 )
      color = colorLinearInterpolation(Qt::blue, Qt::darkMagenta, value);
    else if (value < 0.5 && value >= 0 )
      color = colorLinearInterpolation(Qt::black, Qt::blue, value);
    break;

    // Linear 3 colors gradient :
    // red 100% -> yellow 50% -> green 0%
  case LINEAR_3_COLORS :
    if (value <= 1 && value > 0.67)
      color = colorLinearInterpolation(Qt::cyan, Qt::black, value);
    else if (value <= 0.67 && value >= 0)
      color = colorLinearInterpolation(Qt::white, Qt::cyan, value);
    break;



    // Linear 2 colors gradient :
    // green 100% -> black 0%
  case LINEAR_1 :
    color = colorLinearInterpolation(Qt::black, Qt::green, value);
    break;

  case LINEAR_2 :
    color = colorLinearInterpolation(Qt::white, Qt::darkGreen, value);
    break;

  default:
    // fall trough

    // Linear graysale gradient : white 0% -> black 100%
  case LINEAR_GRAYSCALE:
    color = colorLinearInterpolation(Qt::white, Qt::black, value);
    break;
  }

  return color;

}


/**
 * Calculates a linear interpolation between two colors, given an alpha value.
 *
 * @param c1 Color 1 (alpha = 0)
 * @param c2 Color 2 (alpha = 1)
 * @param alpha Double value between 0 and 1
 * @return A color between c1 and c2
 */
QColor GraphicTidListBar::colorLinearInterpolation(QColor c1, QColor c2, double alpha) {

  int red, green, blue;

  red = (1.0f - alpha) * c1.red() + alpha * c2.red();
  if (red > 255)
    red = 255;
  else if (red < 0)
    red = 0;

  green = (1.0f - alpha) * c1.green() + alpha * c2.green();
  if (green > 255)
    green = 255;
  else if (green < 0)
    green = 0;

  blue = (1.0f - alpha) * c1.blue() + alpha * c2.blue();
  if (blue > 255)
    blue = 255;
  else if (blue < 0)
    blue = 0;

  return QColor(red, green, blue);

}


//////////////////////////////////////////////////////////////////////////////
// GETTERS & SETTERS

bool GraphicTidListBar::isReady() const {
  return (pInputDagsMap != NULL && pTidList != NULL && ratesList.size() > 0);
}

void GraphicTidListBar::setSize(QSize _size) {
  size = _size;
  updateRatesList();
}

void GraphicTidListBar::setWidth(int _width) {
  setSize(QSize(_width, size.height()));
}

void GraphicTidListBar::setHeight(int _height) {
  size.setHeight(_height);
}


/**
 * Sets the Input DAGs map for this TID list bar view.
 * The boolean map is reset to false. If there was already a map it is cleared.
 * Each key <int i, bool b> of the boolean map tells whether the input DAG i
 * belongs to the current DAG pattern's TID list (b=true) or not (b=false).
 *
 * @param _inputDagsMap The pointer to the data model's input DAGs map
 */
void GraphicTidListBar::setInputDagsMap(QMap<int, QString> *_inputDagsMap) {

  // Store input dags map pointer
  pInputDagsMap = _inputDagsMap;
  // Empty map
  boolMap.clear();
  // Fill map with false values
  for (int i = 0; i < pInputDagsMap->size(); i++)
    boolMap.insert(i, false);

}


/**
 * Sets the current TID list to be displayed, based on the current input DAGs map.
 *
 * @param _tidList The pointer to the pattern DAG's TID list
 */
void GraphicTidListBar::setTidList(QList<int> *_tidList) {

  pTidList = _tidList;

  resetBoolMap();

  /*
   Initializes the input boolean map from current TID list data :
   - true if the current input DAG belongs to the pattern TID list
   - false if not (value already initialized)
   */
  for (int i = 0; i < pTidList->size(); i++) {
    boolMap[pTidList->at(i)] = true;
  }

}


/**
 * Changes color mode and updates display to redraw the tid list bar.
 *
 * @param _mode The new color mode to be set
 */
void GraphicTidListBar::setColorMode(int _mode) {

  colorMode = _mode;

  // Update display after changing color mode
  update();
}

