/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/***************************************************************************
 *            macrowindow.cc
 *
 *  Fri Aug 31 12:27:45 CEST 2007
 *  Copyright 2007 Lars Bisballe Jensen and Bent Bisballe Nyeng
 *  elsenator@gmail.com and 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 "macrowindow.h"

#include <QVBoxLayout>
#include <QDomDocument>
#include <QDomElement>
#include <QDomNode>
#include <QByteArray>

#include "messagebox.h"

#include "widgets/widget.h"
#include "widgets/window.h"
#include "widgetbuilder.h"
#include "lua.h"

extern QString cpr;
extern QString user;
extern QString host;
extern quint16 port;

MacroWindow::MacroWindow(NetCom &n, QDomNode &xml_doc, QString templ,
                         bool collapsed, bool compact)
  : Collapser(), netcom(n)
{
  waschanged = false;

  this->templ = templ;

  setCollapsedWidget(new ResumeWidget(compact));

  this->lua = new LUA(&this->widgets, &this->auxwidgets);

  update(xml_doc);

  setCollapsed(collapsed);
  active = true;
}

MacroWindow::~MacroWindow()
{
  delete lua;
}

void MacroWindow::update(QDomNode &node)
{
  mainwidget = NULL;

  widgets.clear();
  auxwidgets.clear();
  luaprograms.clear();

  initMacro(node);

  if(mainwidget) setExpandedWidget(mainwidget);
}

void MacroWindow::initMacro(QDomNode &node)
{
  QDomElement xml_elem = node.toElement();

  if(xml_elem.tagName() == "macro") {
    // Assign the macro name and version to QStrings for use when comitting
    if(xml_elem.hasAttribute("name")) macro = xml_elem.attribute("name");
    if(xml_elem.hasAttribute("version")) version = xml_elem.attribute("version");

  } else if(xml_elem.tagName() == "scripts") {
    // Nothing to do here
  } else if(xml_elem.tagName() == "resume") {
    QString resume = xml_elem.text();
    ((ResumeWidget*)collapsedWidget())->setText(resume);
  } else if(xml_elem.tagName() == "script") {

    if(xml_elem.hasAttribute("language") &&
       xml_elem.attribute("language") == "lua") {
      if(xml_elem.hasAttribute("name")) {
        luaprograms[xml_elem.attribute("name")] = xml_elem.text();
      }
    } else {
      printf("Unknown script type %s\n", xml_elem.attribute("language").toStdString().c_str());
    }

  } else if(xml_elem.tagName() == "widgets") {
    Window *window = new Window(xml_elem, this);
    macrotitle = xml_elem.attribute("caption");
    mainwidget = window;

    QDomNodeList children = node.childNodes();

    // Build widgets
    for (int i=0; i<children.count();i++) {
      QDomNode child = children.at(i);
      widgets += widgetBuilder(child, mainwidget, this);
    }

    // Insert their values (this must be done last for scripts to work properly)
    for (int i=0; i<children.count();i++) {
      QDomNode child = children.at(i);
      setValues(child, this);
    }

    if(waschanged == true) macroChanged();

    return;
  }

  QDomNodeList children = node.childNodes();

  for (int i=0; i<children.count();i++) {
    QDomNode child = children.at(i);
    initMacro(child);
  }
}

bool MacroWindow::doCommit()
{
  // Check for, and count, errors on all entries before comitting
  int faulty = 0; // 0 initial errors

  QVector< Widget* >::iterator i = widgets.begin();
  while (i != widgets.end()) {
    Widget* w = *i;
    if(!w->isDisabled() && !w->isValid()) faulty++; // Regexp check, returns valid if entry passed
    i++;
  }

  // If all entries passed validation, continue commit
  if(faulty == 0) {
    netcom.send(widgets, templ, macro, version);
    emit updateOnCommit();
    setCollapsed(true);
    return true;
  } else {
    return false;
  }
}

void MacroWindow::close()
{
  //  mainwidget->close();
  isclosed = true;
}

void MacroWindow::commit()
{
  if(doCommit()) {
    //    close();
  } else {
   MessageBox::critical(NULL, "Fejl",
                        "Makroen " + macrotitle + " er ikke udfyldt korrekt, pr�v igen.\n",
                        MessageBox::Ok);
  }
}

void MacroWindow::reset()
{
  /*
  MessageBox::warning(NULL, tr("Reset"),
                   tr("Du har valgt at nulstille de indtastede data.\n"
                      "Er du sikker?"),
                   MessageBox::Yes | MessageBox::Cancel);
  printf("MacroWindow -> resetting...\n");
  */
  QVector< Widget* >::iterator i = widgets.begin();
  while (i != widgets.end()) {
    Widget* w = *i;
    w->reset();
    i++;
  }

  QVector< Widget* >::iterator j = auxwidgets.begin();
  while (j != auxwidgets.end()) {
    Widget* w = *j;
    w->reset();
    j++;
  }

  waschanged = false;
}

void MacroWindow::cancel()
{
  collapseWrapper();
}

void MacroWindow::cont(QString name)
{
  QString macro;
  QVector< Widget* >::iterator i=widgets.begin();
  while (i != widgets.end()) {
    Widget* w = *i;
    if(w->getName() == name) {
      macro = w->getValue();
    }
    i++;
  }
  if(doCommit()) {

    // FIXME: Hack to prevent XML clotching.
    // The server could not differentiate the commit and the request.

    // TODO: Where to get the template var??
    //    new_macro("example", macro);
    //    close();
  } else {
   MessageBox::critical(NULL, "Fejl",
			 "Makroen " + macrotitle + " er ikke udfyldt korrekt, pr�v igen.\n",
			 MessageBox::Ok);
  }
  printf("%s : MacroWindow -> continuing...\n", macro.toStdString().c_str());
}

void MacroWindow::cont_nocommit(QString name)
{
  QString macro;
  QVector< Widget* >::iterator i=widgets.begin();
  while (i != widgets.end()) {
    Widget* w = *i;
    if(w->getName() == name) {
      macro = w->getValue();
    }
    i++;
  }
  if(true/*doCommit()*/) {

    // FIXME: Hack to prevent XML clotching.
    // The server could not differentiate the commit and the request.

    // TODO: Where to get the template var??
    //    new_macro("example", macro);
    //    close();
  } else {
   MessageBox::critical(NULL, "Fejl",
			 "Makroen " + macrotitle + " er ikke udfyldt korrekt, pr�v igen.\n",
			 MessageBox::Ok);
  }
  printf("%s : MacroWindow -> continuing...\n", macro.toStdString().c_str());
}

bool MacroWindow::isClosed()
{
  return isclosed || mainwidget->isVisible() == false;
}

Widget *MacroWindow::getWidget(QString name)
{
  QVector< Widget* >::iterator i = widgets.begin();
  while (i != widgets.end()) {
    Widget* w = *i;
    if(name == w->getName()) return w;
    i++;
  }

  QVector< Widget* >::iterator j = auxwidgets.begin();
  while (j != auxwidgets.end()) {
    Widget* w = *j;
    if(name == w->getName()) return w;
    j++;
  }
  
  printf("WARNING: Widget %s not found\n", name.toStdString().c_str());

  return NULL;
}

void MacroWindow::addAuxWidgets(QVector< Widget* > ws)
{
  auxwidgets += ws;
}

void MacroWindow::addWidgets(QVector< Widget* > ws)
{
  widgets += ws;
}

void MacroWindow::expandWrapper()
{
  if(!isCollapsed()) return;

  widgets.clear();
  auxwidgets.clear();
  luaprograms.clear();
  waschanged = false;
  
  QDomDocument xml_doc = netcom.send(templ, macro);
  
  //
  // TODO: This is where the dependency checking should occur.
  //
  
  // Initiate the new macro window with the xml document and push
  //  it to the window list
  QDomNodeList templates = xml_doc.documentElement().childNodes();
  QDomNode templatenode = templates.at(0); // There can be only one! (Swush, flomp)
  QDomNodeList macronodes = templatenode.childNodes();
  for(int j = 0; j < macronodes.count(); j++) {
    QDomNode macronode = macronodes.at(j);
    
    if(true || macronode.childNodes().count()) {
      // macrowindows.push_back( new MacroWindow( netcom, macronode ) );
      QDomElement xml_elem = macronode.toElement();
      
      if(xml_elem.tagName() == "macro") {
        
        // Assign the macro name and version to QStrings for use when comitting
        QString macroname;
        if(xml_elem.hasAttribute("name")) {
          if(xml_elem.attribute("name") == macro) {
            // update me!
            initMacro(macronode);
          }
        }
      }
    }
  }
  setExpandedWidget(mainwidget);
  expand();

  QVector< Widget* >::iterator i = widgets.begin();
  while (i != widgets.end()) {
    Widget* w = *i;
    if(w->setKeyboardFocus()) break;
    i++;
  }
}

void MacroWindow::collapseWrapper()
{
  if(isCollapsed()) return;

  if(waschanged) {
    switch(MessageBox::warning(NULL,
                                "Gem �ndringerne i makroen?",
                                "Du har valgt at lukke makroen " + macrotitle + ".\n"
                                "�nsker du at gemme inden du lukker?",
                                MessageBox::Save | MessageBox::Close | MessageBox::Cancel)) {
    case MessageBox::Save:
      if(doCommit()) setCollapsed(true);
      else MessageBox::critical(NULL,
                                 "Fejl",
                                 "Makroen " + macrotitle + "  er ikke udfyldt korrekt, pr�v igen.\n",
                                 MessageBox::Ok);
      
      break;
    case MessageBox::Close:
      collapse();
      break;
    case MessageBox::Cancel:
    default:
      //      emit expanding(); // signal the other to close again (if any)
      break;
    }
  } else {
    collapse();
  }
}

void MacroWindow::toggleMacro()
{
  if(!active) return;
  if(isCollapsed()) {
    expandWrapper();
  } else {
    collapseWrapper();
  }
}

void MacroWindow::macroChanged()
{
  printf("This macro was changed!\n");
  emit macroHasChanged();
  waschanged = true;
}

void MacroWindow::setActive(bool active)
{
  if(this->active == active) return;

  this->active = active;
  setEnabled(active);

  emit activationChanged(active);
}