/***************************************************************************
                    kommanderfunctions.cpp - Text widget core functionality 
                             -------------------
    copyright          : (C) 2004      Michal Rudolf <mrudolf@kdewebdwev.org>

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

/***************************************************************************
 *                                                                         *
 *   This program 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.                                   *
 *                                                                         *
 ***************************************************************************/

#include <iostream>
#include <stdlib.h> 

#include <tqfile.h>
#include <tqregexp.h>
#include <tqtextstream.h>
 
#include <dcopclient.h>
#include <tdeapplication.h>
#include <tdeconfig.h>
#include <tdelocale.h>
#include <tdeglobal.h>

#include "kommanderwidget.h"
#include "specials.h"
#include "specialinformation.h"
#include "expression.h"
#include "parser.h"

TQString KommanderWidget::evalFunction(const TQString& function, const TQStringList& args)
{ 
  switch (SpecialInformation::function(Group::Kommander, function)) {
    case Kommander::widgetText:
      return handleDCOP(DCOP::text);
    case Kommander::selectedWidgetText:
      return handleDCOP(DCOP::selection);
    case Kommander::dcopid:
      return kapp->dcopClient()->appId();
    case Kommander::pid:
      return TQString().setNum(getpid());
    case Kommander::null:
      return TQString();
    case Kommander::comment:
      return TQString("#");
    case Kommander::exec:
      return execCommand(args[0]);
    case Kommander::dcop:
      return DCOPQuery(args);
    case Kommander::parentPid:
      return global("_PARENTPID").isEmpty() ? TQString().setNum(getppid()) : global("_PARENTPID");
    case Kommander::env:
      return TQString(getenv(args[0].latin1())); 
    case Kommander::i18n:
      return TDEGlobal::locale()->translate(args[0].utf8());
    case Kommander::global:
      return global(args[0]);
    case Kommander::setGlobal:
      setGlobal(args[0], args[1]); 
      return TQString();
    case Kommander::debug:
      tqDebug("%s", args[0].latin1());
      fflush(stderr);
      return TQString();
    case Kommander::echo:
      for (uint i=0; i<args.count(); i++)
         std::cout << args[i].latin1();
      fflush(stdout);
      return TQString();
    case Kommander::readSetting:
    {
      TQString fname = fileName();
      if (!fname.isEmpty())
      {
        TDEConfig cfg("kommanderrc", true);
        cfg.setGroup(fname);
        return cfg.readEntry(args[0], args[1]);
      }
      return TQString();
    }
    case Kommander::writeSetting:
    {
      TQString fname = fileName();
      if (!fname.isEmpty())
      {
        TDEConfig cfg("kommanderrc", false);
        cfg.setGroup(fname);
        cfg.writeEntry(args[0], args[1]);
      }
      return TQString();
    }
    case Kommander::dialog:
      if (args.count() > 1)
        return runDialog(args[0], args[1]); 
      else
        return runDialog(args[0]); 
    case Kommander::expr:
    {
      Expression expr(args[0]);
      bool ok;
      TQVariant value = expr.value(&ok);
      return ok ? value.toString() : TQString();
    }
    default:
      return TQString();
  }
}


TQString KommanderWidget::evalExecBlock(const TQStringList& args, const TQString& s, int& pos) 
{
  int f = s.find("@execEnd", pos);  
  if (f == -1)
  {
    printError(i18n("Unterminated @execBegin ... @execEnd block."));
    return TQString();
  } 
  else
  {
    TQString shell = args.count() ? args[0] : TQString();
    int start = pos;
    pos = f + TQString("@execEnd").length()+1;
    return execCommand(evalAssociatedText(s.mid(start, f - start)), shell);
  }
}
 
TQString KommanderWidget::evalForEachBlock(const TQStringList& args, const TQString& s, int& pos) 
{
  int f = s.find("@end", pos);  
//FIXME: better detection of block boundaries  
  if (f == -1)
  {
    printError(i18n("Unterminated @forEach ... @end block."));
    return TQString();
  }
  else
  {
    int start = pos;
    pos = f + TQString("@end").length()+1;
    TQString var = args[0];
    TQStringList loop = TQStringList::split("\n", args[1]);
    TQString output;
    TQString block = substituteVariable(s.mid(start, f - start), TQString("%1_count").arg(var),
      TQString::number(loop.count()));
    TQString varidx = TQString("%1_index").arg(var);
    for (uint i=0; i<loop.count(); i++)
      output += evalAssociatedText(substituteVariable(substituteVariable(block, varidx, TQString::number(i+1)),
        var, loop[i]));
    return output;
  }
}

TQString KommanderWidget::evalForBlock(const TQStringList& args, const TQString& s, int& pos) 
{
  int f = s.find("@end", pos);  
//FIXME: better detection of block boundaries  
  if (f == -1)
  {
    printError(i18n("Unterminated @forEach ... @end block."));
    return TQString();
  } 
  else
  {
    int start = pos;
    pos = f + TQString("@end").length()+1;
    TQString block = s.mid(start, f - start);
    TQString variable = args[0];

    Expression expr;
    int loopstart = expr.value(args[1]).toInt();
    int loopend = expr.value(args[2]).toInt();
    int loopstep = 1;
    if (args.count() > 3)
    {
      loopstep = expr.value(args[3]).toInt();  
      if (!loopstep)
        loopstep = 1;
    }

    TQString output;
    for (int i=loopstart; i<=loopend; i+=loopstep)
    {
      output += evalAssociatedText(substituteVariable(block, variable, TQString::number(i)));
    }
    return output;
  }
}

TQString KommanderWidget::evalIfBlock(const TQStringList& args, const TQString& s, int& pos) 
{
  int f = s.find("@endif", pos);
//FIXME: better detection of block boundaries; add error message
  if (f == -1)
  {
    pos = s.length()+1;
    printError(i18n("Unterminated @if ... @endif block."));
    return TQString();
  }
  else
  {
    TQString block = s.mid(pos, f - pos);
    pos = f + TQString("@endif").length()+1;
    Expression expr;
    if (expr.isTrue(args[0]))
      return evalAssociatedText(block);
    return TQString();
  }
}

TQString KommanderWidget::evalSwitchBlock(const TQStringList& args, const TQString& s, int& pos) 
{
  int f = s.find("@end", pos);
//FIXME: better detection of block boundaries; add error message
  if (f == -1)
  {
    printError(i18n("Unterminated @switch ... @end block."));
    return TQString();
  }
  else
  {
    TQString block = s.mid(pos, f - pos);
    pos = f + TQString("@end").length()+1;
    f = parseBlockBoundary(block, 0, "@case");
    bool finished = f == -1;
    while (!finished)
    {
      f += 5;
      int end = parseBlockBoundary(block, f, "@case");
      if (end == -1) 
      {
        end = block.length();
        finished = true;
      }
      bool ok;
      TQString value = parseBrackets(block, f, ok);
      if (!ok) 
        break;
      if (value == args[0] || value == "*")
        return evalAssociatedText(block.mid(f, end-f));
      f = end;
    }
    return TQString();
  }
}



TQString KommanderWidget::evalArrayFunction(const TQString& function, const TQStringList& args)
{
  Parser parser(internalParserData());
  int fname = SpecialInformation::function(Group::Array, function);
  TQString array = args[0].startsWith("_") ? args[0] : TQString("_")+ args[0];

  if (fname == Array::setValue)
    parser.setArray(array, args[1], args[2]);
  else if (fname == Array::fromString)
  {
    TQStringList lines = TQStringList::split("\n", args[1]);
    for (TQStringList::Iterator it = lines.begin(); it != lines.end(); ++it)
    {
      TQString key = (*it).section('\t', 0, 0).stripWhiteSpace();
      if (!key.isEmpty())
        parser.setArray(array, key, (*it).section('\t', 1));
    }
  }
  else if (!parser.isArray(array))
    return TQString();
  else switch (fname) {
    case Array::value:
      return parser.arrayValue(array, args[1]).toString();
    case Array::keys:
    {
      const TQMap<TQString, ParseNode> map = parser.array(array);
      TQStringList keys;
      for (TQMap<TQString, ParseNode>::ConstIterator it = map.begin(); it != map.end(); ++it)
        keys.append(it.key());
      return keys.join("\n");
    }
    case Array::values:
    {
      const TQMap<TQString, ParseNode> map = parser.array(array);
      TQStringList values;
      for (TQMap<TQString, ParseNode>::ConstIterator it = map.begin(); it != map.end(); ++it)
        values.append(it.data().toString());
      return values.join("\n");
    }
    case Array::clear:
      parser.unsetArray(array);
      return TQString();
    case Array::remove:
      parser.unsetArray(array, args[1]);
      return TQString();
    case Array::count:
      return TQString::number(parser.array(array).count());
    case Array::toString:
    {
      const TQMap<TQString, ParseNode> map = parser.array(array);
      TQString arraystring;
      for (TQMap<TQString, ParseNode>::ConstIterator it = map.begin(); it != map.end(); ++it)
        arraystring += TQString("%1\t%2\n").arg(it.key()).arg(it.data().toString());
      return arraystring;
    }
    default:
      return TQString();
  }
  return TQString();
}


TQString KommanderWidget::evalWidgetFunction(const TQString& identifier, const TQString& s, int& pos)
{
  KommanderWidget* pWidget = parseWidget(identifier);
  if (!pWidget) 
  {
    printError(i18n("Unknown widget: @%1.").arg(identifier));
    return TQString();
  }
  if (s[pos] == '.')
  {
    pos++;
    bool ok = true;
    TQString function = parseIdentifier(s, pos);
    TQStringList args = parseFunction("DCOP", function, s, pos, ok);
    if (!ok)
      return TQString();
    args.prepend(pWidget->widgetName());
    TQString prototype = SpecialInformation::prototype(Group::DCOP,
      SpecialInformation::function(Group::DCOP, function));
    return localDCOPQuery(prototype, args);
  }
  else if (pWidget == this)
  {
    printError(i18n("Infinite loop: @%1 called inside @%2.").arg(pWidget->widgetName())
        .arg(pWidget->widgetName()));
    return TQString();
  }
  else if (!pWidget->hasAssociatedText())
  {
    printError(i18n("Script for @%1 is empty.").arg(pWidget->widgetName()));
    return TQString();
  }
  return pWidget->evalAssociatedText();
}

