/***************************************************************************
 *   Copyright (C) 2007 Nicolas Hadacek <hadacek@kde.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 "cli_main.h"

#include "cli_log.h"
#include "common/global/about.h"

//-----------------------------------------------------------------------------
const CLI::CommandData *CLI::findCommandData(const TQString &command)
{
  for (uint i=0; NORMAL_COMMAND_DATA[i].name; i++)
    if ( NORMAL_COMMAND_DATA[i].name==command ) return &NORMAL_COMMAND_DATA[i];
  if ( !_isInteractive) return 0;
  for (uint i=0; INTERACTIVE_COMMAND_DATA[i].name; i++)
    if ( INTERACTIVE_COMMAND_DATA[i].name==command ) return &INTERACTIVE_COMMAND_DATA[i];
  return 0;
}

CLI::ExitCode CLI::findCommand(const TQString &s)
{
  if ( s.isEmpty() ) return errorExit(i18n("No command specified"), ARG_ERROR);
  const CommandData *data = findCommandData(s);
  if ( data==0 ) return errorExit(i18n("Unknown command: %1").arg(s), ARG_ERROR);
  return OK;
}

//-----------------------------------------------------------------------------
bool CLI::isPropertyList(const TQString &s)
{
  for (uint i=0; PROPERTY_DATA[i].name; i++)
    if ( s==PROPERTY_DATA[i].list ) return true;
  return false;
}

bool CLI::isProperty(const TQString &s)
{
  for (uint i=0; PROPERTY_DATA[i].name; i++)
    if ( s==PROPERTY_DATA[i].name ) return true;
  return false;
}

//-----------------------------------------------------------------------------
const KCmdLineOptions STANDARD_OPTIONS[] = {
  { "c",              0, 0 },
  { "command <name>", I18N_NOOP("Perform the requested command."), 0 },
  { "command-list",   I18N_NOOP("Return the list of recognized commands."), 0 },
  { "debug",          I18N_NOOP("Display debug messages."), 0 },
  { "extra-debug",    I18N_NOOP("Display extra debug messages."), 0 },
  { "max-debug",      I18N_NOOP("Display all debug messages."), 0 },
  { "lowlevel-debug", I18N_NOOP("Display low level debug messages."), 0 },
  { "quiet",          I18N_NOOP("Do not display messages."), 0 },
  KCmdLineLastOption
};

const KCmdLineOptions FORCE_OPTIONS[] = {
  { "f",              0, 0 },
  { "force",          I18N_NOOP("Overwrite files and answer \"yes\" to questions."), 0 },
  KCmdLineLastOption
};

const KCmdLineOptions INTERACTIVE_OPTIONS[] = {
  { "i",              0, 0 },
  { "cli",            I18N_NOOP("Interactive mode"), 0 },
  KCmdLineLastOption
};

CLI::OptionList::OptionList(Properties properties)
{
  init(properties);
}

CLI::OptionList::OptionList(Properties properties, const KCmdLineOptions *options)
{
  init(properties);
  for (uint i=0; options[i].name; i++) append(options[i]);
}

void CLI::OptionList::init(Properties properties)
{
  for (uint i=0; STANDARD_OPTIONS[i].name; i++) append(STANDARD_OPTIONS[i]);
  if ( properties & HasForce ) for (uint i=0; FORCE_OPTIONS[i].name; i++) append(FORCE_OPTIONS[i]);
  if ( properties & HasInteractiveMode ) for (uint i=0; INTERACTIVE_OPTIONS[i].name; i++) append(INTERACTIVE_OPTIONS[i]);
}

CLI::ExitCode CLI::commandList()
{
  Log::KeyList keys(i18n("Supported commands:"));
  for (uint i=0; NORMAL_COMMAND_DATA[i].name; i++)
    keys.append(NORMAL_COMMAND_DATA[i].name, i18n(NORMAL_COMMAND_DATA[i].help));
  if (_isInteractive) {
    for (uint i=0; INTERACTIVE_COMMAND_DATA[i].name; i++)
      keys.append(INTERACTIVE_COMMAND_DATA[i].name, i18n(INTERACTIVE_COMMAND_DATA[i].help));
  }
  keys.display(*_view);
  return OK;
}

CLI::ExitCode CLI::propertyList()
{
  Log::KeyList keys;
  for (uint i=0; PROPERTY_DATA[i].name; i++)
    keys.append(PROPERTY_DATA[i].name, i18n(PROPERTY_DATA[i].help));
  keys.display(*_view);
  return OK;
}

//-----------------------------------------------------------------------------
CLI::MainBase::MainBase(Properties properties)
  : TQObject(0, "main"), _properties(properties)
{
  Q_ASSERT( _main==0 );
  _main = this;
  _view = new View;
  setView(_view);
}

void CLI::MainBase::init()
{
  _args = KCmdLineArgs::parsedArgs();
  if ( _properties & HasInteractiveMode ) _isInteractive = _args->isSet("cli");
  if ( _properties & HasForce ) _force = _args->isSet("force");
  FOR_EACH(Log::DebugLevel, level) if ( _args->isSet(level.key()) ) _view->setDebugLevel(level);
}

CLI::OptionList CLI::MainBase::optionList(const char *fileDescription) const
{
  OptionList list(_properties, OPTIONS);
  KCmdLineOptions opt;
  for (uint i=0; PROPERTY_DATA[i].name; i++) {
    opt.description = 0;
    opt.def = 0;
    if ( PROPERTY_DATA[i].help==0 ) {
      Q_ASSERT( TQString(PROPERTY_DATA[i].name)!=PROPERTY_DATA[i].optName );
      opt.name = PROPERTY_DATA[i].name; // alias
      list.append(opt);
    } else {
      if ( PROPERTY_DATA[i].optName==0 ) continue; // interactive only
      if ( PROPERTY_DATA[i].alias ) {
        opt.name = PROPERTY_DATA[i].alias;
        list.append(opt);
      }
      opt.name = PROPERTY_DATA[i].optName;
      opt.description = PROPERTY_DATA[i].help;
      list.append(opt);
      if ( PROPERTY_DATA[i].list ) {
        opt.name = PROPERTY_DATA[i].list;
        opt.description = PROPERTY_DATA[i].listHelp;
        list.append(opt);
      }
    }
  }
  if (fileDescription) {
    opt.name = "+[file]";
    opt.description = fileDescription;
    opt.def = 0;
  }
  list.append(opt);
  return list;
}

CLI::ExitCode CLI::MainBase::list(const TQString &command)
{
  if ( command=="command-list" ) return commandList();
  if ( command=="property-list" ) return propertyList();
  return ARG_ERROR;
}

CLI::ExitCode CLI::MainBase::doRun()
{
  init();

  // process set options
  for (uint i=0; PROPERTY_DATA[i].name; i++) {
    if ( PROPERTY_DATA[i].optName==0 ) continue; // alias or interactive only
    if ( !_args->isSet(PROPERTY_DATA[i].name) ) continue;
    TQString option = _args->getOption(PROPERTY_DATA[i].name);
    ExitCode code = executeSetCommand(PROPERTY_DATA[i].name, option);
    if ( code!=OK ) return code;
    log(Log::LineType::Information, TQString("%1: %2").arg(PROPERTY_DATA[i].name).arg(executeGetCommand(PROPERTY_DATA[i].name)));
  }

  // process default lists
  if ( _args->isSet("command-list") ) return list("command-list");
  for (uint i=0; PROPERTY_DATA[i].name; i++) {
    if ( PROPERTY_DATA[i].list==0 ) continue;
    if ( _args->isSet(PROPERTY_DATA[i].list) ) return list(PROPERTY_DATA[i].list);
  }

  bool interactive;
  ExitCode code = prepareRun(interactive);
  if ( code!=OK || interactive ) return code;

  // find command
  TQString command = _args->getOption("command");
  code = findCommand(command);
  if ( code!=OK ) return code;

  // execute command
  code = prepareCommand(command);
  if ( code!=OK ) return code;
  return executeCommand(command);
}
