/**********************************************************************
 PeriodicTableView - Periodic Table Graphics View for Avogadro

 Copyright (C) 2007-2008 by Marcus D. Hanwell

 This file is part of the Avogadro molecular editor project.
 For more information, see <http://avogadro.sourceforge.net/>

 Avogadro is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 Avogadro is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 02110-1301, USA.

 **********************************************************************/

#include "periodictableview.h"

#include <avogadro/elementtranslate.h>

#include <openbabel/data.h>

#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsView>
#include <QPainter>
#include <QStyleOption>
#include <QFont>
#include <QFontMetrics>
#include <QDebug>

namespace OpenBabel{
  // EXTERN comes from babelconfig.h and is more than just 'extern' on windows
  EXTERN OBElementTable etab;
}

namespace Avogadro {

  ElementItem::ElementItem(int elementNumber) : m_width(26), m_height(26)
  {
    // Set up some reasonable defaults for our item
    m_element = elementNumber;

    // Want these items to be selectable
    setFlags(QGraphicsItem::ItemIsSelectable);

    m_symbol = OpenBabel::etab.GetSymbol(m_element);
    std::vector<double> color = OpenBabel::etab.GetRGB(m_element);
    m_color = new QColor();
    m_color->setRgbF(color[0], color[1], color[2]);
    // Set some custom data to make it easy to figure out which element we are
    setData(0, m_element);
    setData(1, 8 + m_element);
  }

  ElementItem::~ElementItem()
  {
     delete m_color;
  }

  QRectF ElementItem::boundingRect() const
  {
    return QRectF(-m_width/2, -m_height/2, m_width, m_height);
  }

  QPainterPath ElementItem::shape() const
  {
    QPainterPath path;
    path.addRect(-m_width/2, -m_height/2, m_width, m_height);
    return path;
  }

  void ElementItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *,
                          QWidget *)
  {
    // Fill the rectangle with the element colour
    painter->setBrush(*m_color);

    // Handle the case where the item is selected
//    if (m_color->value() > 240) {
    if (m_element == 1 || m_element == 2 || m_element == 9 || m_element == 47 || m_element == 78) {
      if (isSelected())
        painter->setPen(Qt::darkGray);
      else
        painter->setPen(Qt::black);
    }
    else {
      if (isSelected())
        painter->setPen(Qt::white);
      else
        painter->setPen(Qt::black);
    }

    QRectF rect(-m_width/2, -m_height/2, m_width, m_height);
    painter->drawRect(rect);
    painter->drawText(rect, Qt::AlignCenter, m_symbol);
  }

  ElementDetail::ElementDetail(int elementNumber) : m_width(100), m_height(70)
  {
    // Set up some reasonable defaults for our item
    m_element = elementNumber;
  }

  QRectF ElementDetail::boundingRect() const
  {
    return QRectF(-m_width/2, -m_height/2, m_width, m_height);
  }

  QPainterPath ElementDetail::shape() const
  {
    QPainterPath path;
    path.addRect(-m_width/2, -m_height/2, m_width, m_height);
    return path;
  }

  void ElementDetail::paint(QPainter *painter, const QStyleOptionGraphicsItem *,
                          QWidget *)
  {
    // Set up a font object and get its height
    QFont font("sans-serif");
    font.setPixelSize(12);
    painter->setFont(font);
    QFontMetrics fm(font);
    int pixelHeight = fm.height();

    QString symbol = OpenBabel::etab.GetSymbol(m_element);
    QString name(elementTranslator.name(m_element));
    QString mass = QString::number(OpenBabel::etab.GetMass(m_element), 'f', 3);

    std::vector<double> color = OpenBabel::etab.GetRGB(m_element);
    QColor m_color;
    m_color.setRgbF(color[0], color[1], color[2]);

    // Draw the element detail border and fill with the element colour
    painter->setBrush(m_color);
    painter->setPen(Qt::black);
    QRectF rect(-m_width/2, -m_height/2, m_width, m_height);
    painter->drawRect(rect);

    // Draw the element symbol bigger than everything else
    font.setPixelSize(24);
    QFontMetrics fm2(font);
    pixelHeight = fm2.height();
    int pixelWidth = fm2.width(symbol);
    painter->setFont(font);
    QRectF symbolRect(-10, -m_height/2 + 8, pixelWidth, pixelHeight);
    painter->drawText(symbolRect, Qt::AlignCenter, symbol);

    // Reduce the font size to draw the other parts
    font.setPixelSize(12);
    int pixelHeight2 = fm.height();
    painter->setFont(font);

// I don't seem to be able to get a nice, cross platform layout working here
// I would really like to figure out how to make this more portable - ideas?
#ifdef Q_WS_MAC
    // Draw the proton number
    QRectF protonNumberRect(-m_width/2 - 10, -m_height/2 + 8, m_width/2, pixelHeight2);
    painter->drawText(protonNumberRect, Qt::AlignRight, QString::number(m_element));

    // Draw the mass
    QRectF massNumberRect(-m_width/2, -m_height/2 + 8 + pixelHeight*1.1, m_width, pixelHeight2);
    painter->drawText(massNumberRect, Qt::AlignCenter, mass);

    // Finally the full element name
    QRectF nameRect(-m_width/2, -m_height/2 + 4 + pixelHeight*1.1 + pixelHeight2, m_width, pixelHeight);
    painter->drawText(nameRect, Qt::AlignCenter, name);
#else
    // Draw the proton number
    QRectF protonNumberRect(-m_width/2 - 10, -m_height/2 + 16, m_width/2, pixelHeight2);
    painter->drawText(protonNumberRect, Qt::AlignRight, QString::number(m_element));

    // Draw the mass
    QRectF massNumberRect(-m_width/2, -m_height/2 + 4  + pixelHeight, m_width,
                          pixelHeight2);
    painter->drawText(massNumberRect, Qt::AlignCenter, mass);

    // Finally the full element name
    QRectF nameRect(-m_width/2, -m_height/2 + pixelHeight + 0.8 * pixelHeight2,
                    m_width, pixelHeight);
    painter->drawText(nameRect, Qt::AlignCenter, name);
#endif
  }

  void ElementDetail::elementChanged(int element)
  {
    m_element = element;
    emit update();
  }

  PeriodicTableScene::PeriodicTableScene(QObject *parent)
    : QGraphicsScene(parent)
  {
    int width = 26;
    int height = 26;

    ElementDetail *detail = new ElementDetail(1);
    detail->setPos(6.5 * width, 0.75 * height);
    addItem(detail);

    // Connect the slot and the signal...
    connect(this, SIGNAL(elementChanged(int)),
            detail, SLOT(elementChanged(int)));

    ElementItem *item = new ElementItem(1);
    item->setPos( 0 * width, 0 * height);
    addItem(item);

    item = new ElementItem(2);
    item->setPos(17 * width, 0 * height);
    addItem(item);

    item = new ElementItem(3);
    item->setPos( 0 * width, 1 * height);
    addItem(item);

    item = new ElementItem(4);
    item->setPos( 1 * width, 1 * height);
    addItem(item);

    item = new ElementItem(5);
    item->setPos(12 * width, 1 * height);
    addItem(item);

    item = new ElementItem(6);
    item->setPos(13 * width, 1 * height);
    addItem(item);

    item = new ElementItem(7);
    item->setPos(14 * width, 1 * height);
    addItem(item);

    item = new ElementItem(8);
    item->setPos(15 * width, 1 * height);
    addItem(item);

    item = new ElementItem(9);
    item->setPos(16 * width, 1 * height);
    addItem(item);

    item = new ElementItem(10);
    item->setPos(17 * width, 1 * height);
    addItem(item);

    item = new ElementItem(11);
    item->setPos( 0 * width, 2 * height);
    addItem(item);

    item = new ElementItem(12);
    item->setPos( 1 * width, 2 * height);
    addItem(item);

    item = new ElementItem(13);
    item->setPos(12 * width, 2 * height);
    addItem(item);

    item = new ElementItem(14);
    item->setPos(13 * width, 2 * height);
    addItem(item);

    item = new ElementItem(15);
    item->setPos(14 * width, 2 * height);
    addItem(item);

    item = new ElementItem(16);
    item->setPos(15 * width, 2 * height);
    addItem(item);

    item = new ElementItem(17);
    item->setPos(16 * width, 2 * height);
    addItem(item);

    item = new ElementItem(18);
    item->setPos(17 * width, 2 * height);
    addItem(item);

    int element = 19;
    for (int i = 3; i < 5; i++) {
      for (int j = 0; j < 18; j++) {
        item = new ElementItem(element++);
        item->setPos(j * width, i * height);
        addItem(item);
      }
    }

    item = new ElementItem(element++);
    item->setPos( 0 * width, 5 * height);
    addItem(item);

    item = new ElementItem(element++);
    item->setPos( 1 * width, 5 * height);
    addItem(item);

    element = 71;

    for (int i = 2; i < 18; i++) {
      item = new ElementItem(element++);
      item->setPos(i * width, 5 * height);
      addItem(item);
    }

    item = new ElementItem(element++);
    item->setPos( 0 * width, 6 * height);
    addItem(item);

    item = new ElementItem(element++);
    item->setPos( 1 * width, 6 * height);
    addItem(item);

    element = 103;

    for (int i = 2; i < 16; i++) {
      item = new ElementItem(element++);
      item->setPos(i * width, 6 * height);
      addItem(item);
    }

    // Now for the weird ones at the bottom...
    element = 57;
    for (int i = 2; i < 16; i++) {
      item = new ElementItem(element++);
      item->setPos(i * width, 7.5 * height);
      addItem(item);
    }
    element = 89;
    for (int i = 2; i < 16; i++) {
      item = new ElementItem(element++);
      item->setPos(i * width, 8.5 * height);
      addItem(item);
    }
  }

  void PeriodicTableScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
  {
    if (event->button() != Qt::LeftButton)
      return;

    QGraphicsItem *item = QGraphicsScene::itemAt(event->scenePos());
    if (item->data(0).toInt() > 0 && item->data(0).toInt() < 119)
      emit(elementChanged(item->data(0).toInt()));

    QGraphicsScene::mousePressEvent(event);
  }

  void PeriodicTableScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
  {
    QGraphicsScene::mouseMoveEvent(event);
  }

  void PeriodicTableScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
  {
    QGraphicsScene::mouseReleaseEvent(event);
  }

  PeriodicTableView::PeriodicTableView(QWidget *parent) : QGraphicsView(parent)
  {
    // Use a small title bar (Qt::Tool) with no minimize or maximise buttons
    setWindowFlags(Qt::Dialog | Qt::Tool);

    PeriodicTableScene *table = new PeriodicTableScene;
    table->setSceneRect(-20, -20, 480, 260);
    table->setItemIndexMethod(QGraphicsScene::NoIndex);
    table->setBackgroundBrush(Qt::white);

    setScene(table);
    setRenderHint(QPainter::Antialiasing);
    setWindowTitle(tr("Periodic Table"));
    resize(490, 270);
    setFixedSize(490, 270);
    connect(table, SIGNAL(elementChanged(int)),
            this, SLOT(elementClicked(int)));
  }

  PeriodicTableView::PeriodicTableView(QGraphicsScene *scene, QWidget *parent) :
    QGraphicsView(scene, parent)
  {
    connect(scene, SIGNAL(elementChanged(int)),
            this, SLOT(elementClicked(int)));
  }

  void PeriodicTableView::setSelectedElement(int)
  {

  }

  void PeriodicTableView::elementClicked(int id)
  {
    emit(elementChanged(id));
  }

  void PeriodicTableView::mouseDoubleClickEvent(QMouseEvent *)
  {
    close();
  }
} // End namespace Avogadro

#include "periodictableview.moc"
