/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/***************************************************************************
 *            widget.cc
 *
 *  Fri Jul  4 12:31:17 CEST 2008
 *  Copyright 2008 Bent Bisballe Nyeng
 *  deva@aasimon.org
 ****************************************************************************/

/*
 *  This file is part of Pracro.
 *
 *  Pracro 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.
 *
 *  Pracro 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 Pracro; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
 */
#include "widget.h"

#include <QApplication>
#include <QDrag>

#include <QLayout>
#include <QBoxLayout>
#include <QVBoxLayout>
#include <QHBoxLayout>

#include <QPainter>

#include <QFrame>
#include <QComboBox>
#include <QLabel>
#include <QPushButton>
#include <QCheckBox>
#include <QLineEdit>
#include <QTextEdit>

#include <math.h>
#include <math.h>

#include "widgetwrapper.h"
#include "propertieseditor.h"

Widget::Widget(QDomNode node)
  : QWidget()
{
  elem = node.cloneNode(true).toElement();

  iscontainer = false;
  if(elem.hasAttribute("layout")) {
    setAcceptDrops(true);
    dragObject = NULL;
    iscontainer = true;
    widget = new QFrame();
    if(elem.attribute("layout") == "vbox") {
      orientation = Qt::Vertical;
      setLayout(new QVBoxLayout());
    } else {
      orientation = Qt::Horizontal;
      setLayout(new QHBoxLayout());
    }
  } else {
    setAcceptDrops(false);
    if(elem.hasAttribute("name")) {
      if(elem.attribute("name") == "combo") widget = new QComboBox();
      else if(elem.attribute("name") == "label") widget = new QLabel();
      else if(elem.attribute("name") == "button") widget = new QPushButton();
      else if(elem.attribute("name") == "checkbox") widget = new QCheckBox();
      else if(elem.attribute("name") == "lineedit") widget = new QLineEdit();
      else if(elem.attribute("name") == "textedit") widget = new QTextEdit();
      else widget = new QLabel();
    } else {
      widget = new QFrame();
    }
    setLayout(new QHBoxLayout());
  }
  setSizePolicy(widget->sizePolicy());
  if(widget->minimumSizeHint().isValid()) setMinimumSize(widget->minimumSizeHint());

  // Iterate and call setValue on all attributes
  QDomNamedNodeMap map = elem.attributes();
  for(size_t i = 0; i < map.length(); i++) {
    QDomAttr attr = map.item(i).toAttr();
    QString name = attr.name();
    QString value = attr.value();
    setValue(name, value);
  }
}

void Widget::paintEvent(QPaintEvent *)
{
  if(widget->minimumSizeHint().isValid()) setMinimumSize(widget->minimumSizeHint());

  widget->resize(width(), height());

  QPixmap pixmap = QPixmap::grabWidget(widget, 0, 0);

  QPainter p(this);
  int y = (height() - pixmap.height()) / 2;
  p.drawPixmap(0, y, pixmap);

  if(iscontainer) {
    
    p.setPen(Qt::blue);
    p.drawRect(0, 0, width()-1, height()-1);
    p.setPen(QColor(150,150,200));
    if(orientation == Qt::Vertical) {
      p.drawLine(4, 2, 2, 4);
      p.drawLine(4, 2, 6, 4);
      p.drawLine(4, 2, 4, 12);
      p.drawLine(4, 12, 2, 10);
      p.drawLine(4, 12, 6, 10);
    } else {
      p.drawLine(2, 4, 4, 2);
      p.drawLine(2, 4, 4, 6);
      p.drawLine(2, 4 ,12, 4);
      p.drawLine(12, 4, 10, 2);
      p.drawLine(12, 4, 10, 6);
    }
  } else {
    p.setPen(QColor(128, 128, 128, 128));
    p.drawText(width() / 2 - 20, height() / 2 + 5, elem.attribute("name", "Widget"));
  }
}

void Widget::setValue(QString name, QString value)
{
  if(iscontainer == false && elem.hasAttribute("name")) {
    if(elem.attribute("name") == "combo") {
      QComboBox *combo = ((QComboBox*)widget);
      if(name == "value") {
        while(combo->count()) combo->removeItem(0);
        combo->addItem(value);
        combo->setCurrentIndex(0);
      }
      repaint();
    } else if(elem.attribute("name") == "label") {
      QLabel *label = ((QLabel*)widget);
      if(name == "caption") label->setText(value);
      repaint();
    } else if(elem.attribute("name") == "button") {
      QPushButton *button = ((QPushButton*)widget);
      if(name == "caption") button->setText(value);
      repaint();
    } else if(elem.attribute("name") == "checkbox") {
      QCheckBox *checkbox = ((QCheckBox*)widget);
      if(name == "caption") checkbox->setText(value);
      if(name == "value") checkbox->setChecked(value == "true");
      repaint();
    } else if(elem.attribute("name") == "lineedit") {
      QLineEdit *lineedit = ((QLineEdit*)widget);
      if(name == "value") lineedit->setText(value);
      repaint();
    } else if(elem.attribute("name") == "textedit") {
      QTextEdit *textedit = ((QTextEdit*)widget);
      if(name == "value") textedit->setPlainText(value);
      repaint();
    } else {
      ((QLabel*)widget)->setText("Unknown attribute " + name + ", set to " + value);
    }
  }
  elem.attributeNode(name).setValue(value);
}

void Widget::mousePressEvent(QMouseEvent *event)
{
  if(event->button() == Qt::LeftButton) {
    dragStartPosition = event->pos();

    // Show properties
    if(!propertieseditor) propertieseditor = new PropertiesEditor();
    propertieseditor->setProperties(this);
    propertieseditor->show();
  }
}

void Widget::mouseMoveEvent(QMouseEvent *event)
{
  if (!(event->buttons() & Qt::LeftButton)) return;

  if((event->pos() - dragStartPosition).manhattanLength() 
     < QApplication::startDragDistance()) return;
  
  if(parentWidget()) {

    QDrag *drag = new QDrag(this); 
    widget->resize(width(), height());
    drag->setPixmap(QPixmap::grabWidget(widget, 0, 0));

    QMimeData *mimedata = new QMimeData();
    mimedata->setData("pracro/widget", wrapWidget(this));
    drag->setMimeData(mimedata);
    
    parentWidget()->layout()->removeWidget(this);
    setVisible(false);
  
    drag->exec();
  }
}

void Widget::dragEnterEvent(QDragEnterEvent *event)
{
  if(!iscontainer) return;

  if(event->mimeData()->hasFormat("pracro/widget")) {
    event->acceptProposedAction();

    if(dragObject) delete dragObject;
    QFrame *frame = new QFrame();

    QPalette pal;
    pal.setColor(QPalette::Foreground, Qt::red);
    frame->setPalette(pal);

    if(orientation == Qt::Horizontal) {
      frame->setFixedWidth(1);
    } else {
      frame->setFixedHeight(1);
    }

    frame->setFrameStyle(QFrame::Box | QFrame::Plain);
    frame->setContentsMargins(1,1,0,0);

    dragObject = frame;

    QWidget *w = findWidget(event->pos());
    if(w) {
      int idx = ((QBoxLayout*)layout())->indexOf(w);
      ((QBoxLayout*)layout())->insertWidget(idx, dragObject);
    } else {
      layout()->addWidget(dragObject);
    }
  }
}

void Widget::dragLeaveEvent(QDragLeaveEvent *)
{
  if(!iscontainer) return;

  if(dragObject) delete dragObject;
  dragObject = NULL;
}

void Widget::dragMoveEvent(QDragMoveEvent *event)
{
  if(!iscontainer) return;

  if(event->mimeData()->hasFormat("pracro/widget")) {
    event->acceptProposedAction();

    layout()->removeWidget(dragObject);

    QWidget *w = findWidget(event->pos());
    if(w == dragObject) return;
 
    if(w) {
      int idx = ((QBoxLayout*)layout())->indexOf(w);
      ((QBoxLayout*)layout())->insertWidget(idx, dragObject);
    } else {
      layout()->addWidget(dragObject);
    }
  }
}

void Widget::dropEvent(QDropEvent *event)
{
  if(!iscontainer) return;

  if(event->mimeData()->hasFormat("pracro/widget")) {
    int idx = layout()->indexOf(dragObject);

    /*
    QString type = event->mimeData()->data("pracro/widget").data();
    QWidget *widget;    
    if(type == "horizontal") widget = new Widget(Qt::Horizontal);
    else if(type == "vertical") widget = new Widget(Qt::Vertical);
    else widget = new Widget(type);
    */
    QWidget *widget = unwrapWidget(event->mimeData()->data("pracro/widget"));
    
    ((QBoxLayout*)layout())->insertWidget(idx, widget);
    delete dragObject;
    dragObject = NULL;
    event->acceptProposedAction();

    widget->setVisible(true);
    widget->show();
  }
}

QWidget *Widget::findWidget(QPoint pos)
{
  QPoint newpos = pos;
  QWidget *w = childAt(newpos);

  float angle = 0.0;
  float dist = 0.0;
  while((!w || QString(w->metaObject()->className()) == "QFrame") && layout()->count()) {

    angle += M_PI / 4;
    dist += 1;

    newpos = pos + QPoint(sin(angle) * dist, cos(angle) * dist);

    if(newpos.x() > height() && newpos.y() > width() && newpos.y() < 0 && newpos.x() < 0) {
      break;
    }

    //    printf("%d, %d\n", newpos.x(), newpos.y());
    w = childAt(newpos);
  }
  //  printf("Done {%p %s}\n", w, w!=NULL?w->metaObject()->className():"(null)");

  if(w) {
    int idx = layout()->indexOf(w);
    //    printf("\r%d > %d", pos.y() - w->pos().y(), w->height() / 2); fflush(stdout);
    if(orientation == Qt::Horizontal) {
      if(pos.x() - w->pos().x() > w->width() / 2) idx++;
    } else {
      if(pos.y() - w->pos().y() > w->height() / 2) idx++;
    }
 
    //    if(idx < layout()->count()) idx = layout()->count() - 1;
    if(idx >= 0 && idx < layout()->count()) w = layout()->itemAt(idx)->widget();
    else w = NULL;
  }

  return w;
}

QString Widget::toXml(QString tabs)
{
  QString xml;

  xml = tabs + "<" + elem.attribute("name");

  QDomNamedNodeMap map = elem.attributes();
  for(size_t i = 0; i < map.length(); i++) {
    QDomAttr attr = map.item(i).toAttr();
    xml += " " + attr.name() + "=\"" + attr.value() + "\"";
  }

  if(iscontainer) {
    xml += ">\n";
    for(int i = 0; i < layout()->count(); i++) {
      Widget *child = (Widget*)layout()->itemAt(i)->widget();
      xml += child->toXml("  " + tabs);
    }
    xml += tabs + "</" + elem.attribute("name") + ">\n";
  } else {
    xml += "/>\n";
  }

  return xml;
}