/***************************************************************************
                          functionsimpl.cpp - Function browser implementation 
                             -------------------
    copyright            : (C) 2004    Michal Rudolf <mrudolf@kdewebdev.org>
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or mody  *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

/* QT INCLUDES */
#include <tqcheckbox.h>
#include <tqlabel.h>
#include <tqmetaobject.h>
#include <tqstringlist.h>
#include <tqregexp.h>

/* KDE INCLUDES */
#include <kcombobox.h>
#include <tdeglobal.h>
#include <kiconloader.h>
#include <tdelistbox.h>
#include <klineedit.h>
#include <tdelocale.h>
#include <kpushbutton.h>
#include <ktextedit.h>
#include <ktextbrowser.h>
#include <kdebug.h>

/* OTHER INCLUDES */ 
#include "functionsimpl.h"
#include "kommanderwidget.h"
#include "invokeclass.h"
#include "widgetdatabase.h"

const int MaxFunctionArgs = 6;

 
FunctionsDialog::FunctionsDialog(TQWidget* a_parent, const TQDict<TQWidget>& a_widgetList, bool useInternalParser, char* a_name, 
  bool a_modal)
  : FunctionsDialogBase(a_parent, a_name, a_modal), m_widgetList(a_widgetList), m_useInternalParser(useInternalParser)
{
  clearButton->setPixmap(TDEGlobal::iconLoader()->loadIcon("locationbar_erase", TDEIcon::Toolbar));
  copyButton->setPixmap(TDEGlobal::iconLoader()->loadIcon("1downarrow", TDEIcon::Toolbar));

  groupComboBox->insertStringList(SpecialInformation::groups());
  connect(groupComboBox, TQ_SIGNAL(activated(int)), TQ_SLOT(groupChanged(int)));
  connect(widgetComboBox, TQ_SIGNAL(activated(int)), TQ_SLOT(groupChanged(int)));
  connect(functionListBox, TQ_SIGNAL(highlighted(int)), TQ_SLOT(functionChanged(int)));
  connect(copyButton, TQ_SIGNAL(clicked()), TQ_SLOT(copyText()));
  connect(clearButton, TQ_SIGNAL(clicked()), insertedText, TQ_SLOT(clear()));

  // build widget name list
  for (TQDictIterator<TQWidget> It(m_widgetList); It.current(); ++It)
    m_widgetNames.append(It.currentKey());
  m_widgetNames.sort();
  widgetComboBox->insertStringList(m_widgetNames);

  for (int j = 0; j < WidgetDatabase::count(); j++)
  {
    TQString group = WidgetDatabase::group(j);
    if (group == "Kommander" || group == "Custom")
      m_widgetTypes << WidgetDatabase::className(j);
  }
  m_widgetTypes.sort();

//TODO: Insert either the generic widget types or parse for on the fly created widget
//names. The problem is that the rest of the code depends on the fact that the widget
//from the widgetComboBox exists
//   widgetComboBox->insertStringList(m_widgetTypes);

  m_acceptedSlots = InvokeClass::acceptedSlots();
  
  m_DCOP = -1;    // Select DCOP functions by default
  m_Slots = -1;
  for (int i=0; i<groupComboBox->count(); i++)
  {
     if (groupComboBox->text(i) == i18n("Slots"))
     {
       m_Slots = i; 
       break;
     }
  }
  if (!useInternalParser)
  {
    groupComboBox->removeItem(m_Slots);
    m_Slots = -1;
  }
  for (int i=0; i<groupComboBox->count(); i++)
  {
     if (groupComboBox->text(i) == "DCOP")
     {
       m_DCOP = i; 
        break;
     }
  }
  groupComboBox->changeItem(i18n("Functions"), m_DCOP); //this is a quick way to rename this group in the UI
  groupComboBox->setCurrentItem(m_DCOP);
  groupChanged(groupComboBox->currentItem());
}

FunctionsDialog::~FunctionsDialog()
{
}

TQString FunctionsDialog::functionText() const
{
  return insertedText->text();
}

TQString FunctionsDialog::currentFunctionText()
{
  TQString prefix, function;
  int index = groupComboBox->currentItem();
  TQString s = (index != m_DCOP ) ? groupComboBox->text(index) : "DCOP";
  if (m_useInternalParser)
  {
    function = SpecialInformation::parserGroupName(s);
    if (!function.isEmpty())
      function += "_";
  }
  else 
  {
    prefix = "@";
    function = s + ".";
  }
  if (groupComboBox->currentText() == "Kommander")
    return TQString("%1%2%3").arg(prefix).arg(functionListBox->currentText()).arg(params());
  else if (groupComboBox->currentItem() == m_DCOP || groupComboBox->currentItem() == m_Slots)
    return TQString("%1%2.%3%4").arg(prefix).arg(widgetComboBox->currentText().section(' ', 0, 0))
        .arg(functionListBox->currentText().left(functionListBox->currentText().find('('))).arg(params());
  else 
    return TQString("%1%2%3%4").arg(prefix).arg(function)
        .arg(functionListBox->currentText()).arg(params());
}

void FunctionsDialog::groupChanged(int index)
{
  index = groupComboBox->currentItem();
  functionListBox->clear();
  m_slotList.clear();
  if (index == m_Slots)
  {
    KommanderWidget* a_atw = dynamic_cast<KommanderWidget *>(m_widgetList[widgetComboBox->currentText()]);
    TQStringList pFunctions = TQStringList::fromStrList(a_atw->object()->metaObject()->slotNames(true));
    for (uint i=0; i<pFunctions.count(); i++)
    {
      TQString slot = pFunctions[i];
      TQString slotArgStr = slot.section(TQRegExp("\\(|\\)"), 1);
      if (slotArgStr.isEmpty() || m_acceptedSlots.contains(slotArgStr))
      {
        TQString name = slot.remove("()");
        if (!m_slotList.contains(name))
        {
          m_slotList[name] = slot;
          functionListBox->insertItem(name);
        }
      }
    }
  } else
  {
    TQString s = (index != m_DCOP ) ? groupComboBox->text(index) : "DCOP";
    TQStringList pFunctions = SpecialInformation::functions(s);
    KommanderWidget* a_atw = 0;
    if (index == m_DCOP)
      a_atw = dynamic_cast<KommanderWidget *>(m_widgetList[widgetComboBox->currentText()]);
    int pGroup = SpecialInformation::group(s);
    SpecialFunction::ParserType pType = m_useInternalParser 
        ? SpecialFunction::InternalParser : SpecialFunction::MacroParser;
  
    for (uint i=0; i<pFunctions.count(); i++)
    {
      int pFunction = SpecialInformation::function(pGroup, pFunctions[i]);
      if (!SpecialInformation::isValid(pGroup, pFunction, pType))
        continue;
      if (a_atw && !a_atw->isFunctionSupported(pFunction) && !a_atw->isCommonFunction(pFunction))
          continue;
      functionListBox->insertItem(pFunctions[i]);
    }
  }

  functionListBox->sort();
  functionListBox->setCurrentItem(0);
  functionChanged(functionListBox->currentItem());
}

void FunctionsDialog::functionChanged(int)
{
  if (groupComboBox->currentItem() == m_Slots)
  { 
    KommanderWidget* w = dynamic_cast<KommanderWidget *>(m_widgetList[widgetComboBox->currentText()]);
    TQObject *o = w->object();
    TQString slotHelp = i18n("To learn more about the slot, look at the documentation of the base TQt/TDE class, most probably <i>%1</i>.").arg(o->metaObject()->superClassName() ? TQString(o->metaObject()->superClassName()) : TQString(o->className()) );
    TQString slotName = functionListBox->currentText();
    TQString slot = m_slotList[slotName];
    descriptionText->clear();
    descriptionText->setText(i18n("<qt><h3>%1</h3>"
      "<p><b>Description:</b> %2\n<p><b>Syntax:</b> <i>%3</i>%4</qt>")
      .arg(slotName).arg(slotHelp).arg(slot).arg(""));
  } else
  {
    int index = groupComboBox->currentItem();
    TQString s = (index != m_DCOP ) ? groupComboBox->text(index) : "DCOP";
    m_function = SpecialInformation::functionObject(s,
        functionListBox->currentText());
    TQString defArgs;
    if (m_function.minArg() < m_function.argumentCount()) 
      if (!m_function.minArg())
          defArgs = i18n("<p>Parameters are not obligatory.");
      else
          defArgs = i18n("<p>Only first argument is obligatory.", 
            "<p>Only first %n arguments are obligatory.", 
            m_function.minArg());
  
    uint pflags = SpecialFunction::ShowArgumentNames;
    if (m_function.maxArg() && m_function.argumentName(0) == "widget")
      pflags = pflags | SpecialFunction::SkipFirstArgument;
  
    descriptionText->clear();
    descriptionText->setText(i18n("<qt><h3>%1</h3>"
      "<p><b>Description:</b> %2\n<p><b>Syntax:</b> <i>%3</i>%4</qt>")
      .arg(functionListBox->currentText()).arg(m_function.description())
      .arg(m_function.prototype(pflags)).arg(defArgs));
  
  }
  showParameters();
}

void FunctionsDialog::copyText()
{
  TQString text = currentFunctionText();
  int para, i;
  insertedText->getCursorPosition(&para, &i);  
//   int cursorPos = insertedText->cursorPosition();
  insertedText->insert(text);
  insertedText->setCursorPosition(para, i + text.find('(') + 1);
}

void FunctionsDialog::showParameters()
{
  KLineEdit* edits[MaxFunctionArgs] = {arg1Edit, arg2Edit, arg3Edit, arg4Edit, arg5Edit, arg6Edit};
  TQLabel* labels[MaxFunctionArgs] = {argLabel1, argLabel2, argLabel3, argLabel4, argLabel5, argLabel6};
  KComboBox* combos[MaxFunctionArgs] = {combo1, combo2, combo3, combo4, combo5, combo6};
  TQCheckBox* quotes[MaxFunctionArgs] = {quote1, quote2, quote3, quote4, quote5, quote6};

  if (groupComboBox->currentItem() == m_Slots)
  {
    widgetComboBox->setShown(true);
    widgetLabel->setShown(true);
    TQString slot = m_slotList[functionListBox->currentText()];
    TQStringList slotArgs = TQStringList::split(',', slot.section(TQRegExp("\\(|\\)"), 1), false);
    int argsCount = slotArgs.count();
    for (int i = 0; i < MaxFunctionArgs; i++)
    {
      labels[i]->setShown(i < argsCount);
      TQString type;
      if (i < argsCount)
      {
        type = slotArgs[i].remove(TQRegExp("\\*|\\&|const\\s"));
        labels[i]->setText(TQString("%1:").arg(type));
      }
      quotes[i]->setChecked(true);
      quotes[i]->setShown(false);
      if (type == "bool")
      {
        edits[i]->setShown(false);
        combos[i]->setShown(i < argsCount);
        combos[i]->clear();
        combos[i]->setEditable(false);
        combos[i]->insertItem("true");
        combos[i]->insertItem("false");    
      } else
      {
        combos[i]->setShown(false);
        edits[i]->setShown(i < argsCount);
        edits[i]->clear();
        if (type == "TQString")
        {
          quotes[i]->setShown(i < argsCount);
        } 
      }  
    }
  } else
  {
    int start = (m_function.argumentCount() && m_function.argumentName(0) == "widget");
  
    widgetComboBox->setShown(start);
    widgetLabel->setShown(start);
    if (start)
    {
      arg1Edit->setShown(false);
      argLabel1->setShown(false);
      combo1->setShown(false);
      quote1->setShown(false);
    }
    int argsCount = m_function.argumentCount();
    for (int i=start; i<MaxFunctionArgs; i++)
    {
      labels[i]->setShown(i < argsCount);
      if (i < argsCount)
        labels[i]->setText(TQString("%1:").arg(m_function.argumentName(i)));
      quotes[i]->setChecked(true);
      quotes[i]->setShown(false);
      if (m_function.argumentType(i) == "bool")
      {
        edits[i]->setShown(false);
        combos[i]->setShown(i < argsCount);
        combos[i]->setEditable(false);
        combos[i]->clear();
        combos[i]->insertItem("true");
        combos[i]->insertItem("false");    
      } else
      {
//FIXME: big hack to show a combo for createWidgets. Good solution: extra flag for arguments telling if it is a file/widget/etc.
        if (m_function.name() == "createWidget" && ( i == 1 || i == 2))
        {
          combos[i]->clear();
          combos[i]->setEditable(true);
          if ( i == 1)
          {
            combos[i]->insertStringList(m_widgetTypes);
          } else
          {
            combos[i]->insertItem("");
            combos[i]->insertStringList(m_widgetNames);
          }
          edits[i]->setShown(false);
          combos[i]->setShown(true);
        } else
        {
          combos[i]->setShown(false);
          edits[i]->setShown(i < argsCount);
          edits[i]->clear();
          if (m_function.argumentType(i) == "TQString")
          {
            quotes[i]->setShown(i < argsCount);
          } 
        }
      }
    }
  }
}

TQString FunctionsDialog::params()
{
  TQLabel* labels[MaxFunctionArgs] = {argLabel1, argLabel2, argLabel3, argLabel4, argLabel5, argLabel6};
  KLineEdit* edits[MaxFunctionArgs] = {arg1Edit, arg2Edit, arg3Edit, arg4Edit, arg5Edit, arg6Edit};
  KComboBox* combos[MaxFunctionArgs] = {combo1, combo2, combo3, combo4, combo5, combo6};
  TQStringList pars;
  TQCheckBox* quotes[MaxFunctionArgs] = {quote1, quote2, quote3, quote4, quote5, quote6};
  bool params = false;
  bool slotsShown = (groupComboBox->currentItem() == m_Slots);
  for (int i=0; i<MaxFunctionArgs; i++)
  {
    if (edits[i]->isShown())
    {
      TQString s = edits[i]->text();
      if (!s.isEmpty() || i < m_function.minArg())
      {
        if (quotes[i]->isChecked() && ( (!slotsShown && m_function.argumentType(i) == "TQString") 
            || (slotsShown && labels[i]->text().startsWith("TQString")) ) )
          s = '"' + s + '"';
         pars.append(s);
      }
      params = true;
    } else
    if (combos[i]->isShown())
    {
      TQString s = combos[i]->currentText();
      if (!s.isEmpty() || i < m_function.minArg())
      {
        if (s != "true" &&   s !="false")
          s = '"' + s + '"';
        pars.append(s);
      }
      params = true;
    }
  }
  TQString a_param = pars.join(", ");
  if (params)
    return TQString("(%1)").arg(a_param);
  else
    return a_param;
}


#include "functionsimpl.moc"
