/*! @file

    @id $Id$
*/
//       1         2         3         4         5         6         7         8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890

#ifndef EXCEPTIONS_HXX
#define EXCEPTIONS_HXX
#include <stdexcept>
#include <queue>
#include <QDir>

class Exception: public std::exception {
  public:
    Exception(QString w): _what(w) {}
    ~Exception() throw() {}
    const char* what() const throw() {
      return _what.toStdString().c_str();
    }
    void line(int linenr) {
      if (linenr>0) _what=QString::number(linenr)+" "+_what;
    }
    void file(QString filename) {
      if (filename.size()) _what=filename+":"+_what;
    }
  protected:
    QString _what;
};

class ParseError: public Exception {
  public:
    ParseError(QString w): Exception("parse error: "+w) {}
};

class UnknownCommand: public ParseError {
  public:
    UnknownCommand(QString line): ParseError("unknown command: \""+line+"\"") {}
};

class BadArgument: public ParseError {
  public:
    BadArgument(QString arg): ParseError("bad argument: "+arg) {}
};

class MissingArguments: public ParseError {
  public:
    MissingArguments(QString args, QString req):
        ParseError("missing arguments, requires "+req+", got: "+args) {}
};

class MissingLine: public ParseError {
  public:
    MissingLine():
        ParseError("line expectet at end of file") {}
};

class TestFailed: public Exception {
  public:
    TestFailed(QString why): Exception("Test Failed: "+why) {}
};

class PossibleRetryLoad: public TestFailed {
  public:
    PossibleRetryLoad(QString txt): TestFailed(txt) {}
};

class WrongSignal: public PossibleRetryLoad {
  public:
    WrongSignal(QString signal, QStringList args,
                std::pair<QString, QStringList> received):
        PossibleRetryLoad
        ("expected: \""+signal+" "+args.join(' ')+"\"; "
         "received: \""+received.first+" "+received.second.join(' ')+"\"") {
    }
};

class UnhandledSignals: public TestFailed {
  public:
    typedef std::pair<QString, QStringList> Signal;
    UnhandledSignals(std::queue<Signal> sigs):
        TestFailed("unhandled signals:") {
      while (!sigs.empty()) {
        Signal res(sigs.front());
        _what += "\n"+res.first+" "+res.second.join(' ');
        sigs.pop();
      }
    }
};

class TimeOut: public PossibleRetryLoad {
  public:
    TimeOut(): PossibleRetryLoad("command timeout") {}
};

class ElementNotFound: public TestFailed {
  public:
    ElementNotFound(QString selector):
        TestFailed("element not found: "+selector) {}
};

class DirectoryCannotBeCreated: public TestFailed {
  public:
    DirectoryCannotBeCreated(QString name):
        TestFailed("cannot create directory: "+name) {}
};

class CannotWriteScreenshot: public TestFailed {
  public:
    CannotWriteScreenshot(QString name):
        TestFailed("cannot write screenshot: "+name) {}
};

class CannotWriteSouceHTML: public TestFailed {
  public:
    CannotWriteSouceHTML(QString name):
        TestFailed("cannot write html source code: "+name) {}
};

class FileNotFound: public TestFailed {
  public:
    FileNotFound(QString arg): TestFailed("file not found: "+arg) {}
};

class NotACertificate: public TestFailed {
  public:
    NotACertificate(QString arg):
        TestFailed("file is not a certificate: "+arg) {
    }
};

class KeyNotReadable: public TestFailed {
  public:
    KeyNotReadable(QString arg):
        TestFailed("key file is not readable (password?): "+arg) {
    }
};

class NotUnattended: public TestFailed {
  public:
    NotUnattended(): TestFailed("web page is not in unattended test mode") {}
};

class LastFileNotUploaded: public TestFailed {
  public:
    LastFileNotUploaded(QString arg):
        TestFailed("last specified upload file has not been uploaded: "+arg) {
    }
};

class EmptyUploadFile: public TestFailed {
  public:
    EmptyUploadFile(): TestFailed("specified upload file is empty string") {}
};

class NoUploadFile: public TestFailed {
  public:
    NoUploadFile(): TestFailed("no upload file specified") {}
};

class SetFileUploadFailed: public TestFailed {
  public:
    SetFileUploadFailed(QString selector, QString filename):
        TestFailed("set file upload failed for selector "+selector
                   +" and file "+filename) {
    }
};

class AssertionFailed: public TestFailed {
  public:
    AssertionFailed(QString text):
        TestFailed("assertion failed: "+text) {
    }
};

class DownloadFailed: public TestFailed {
  public:
    DownloadFailed(QString file):
        TestFailed("download failed of file \""+file+"\"") {
    }
};

class WriteFileFailed: public TestFailed {
  public:
    WriteFileFailed(QString file):
        TestFailed("write failed of file \""+QDir(file).absolutePath()+"\"") {
    }
};

class ScriptFailed: public TestFailed {
  public:
    ScriptFailed(QString dsc, QString cmd, QStringList args, QString script):
        TestFailed(dsc+"; command: "+cmd
                   +(args.size()?" "+args.join(' '):QString())
                   +(script.size()?"\n"+script:QString())) {
    }
};

class CannotStartScript: public ScriptFailed {
  public:
    CannotStartScript(QString command, QStringList args, QString script):
        ScriptFailed("command not found", command, args, script) {
    }
};

class CannotLoadScript: public ScriptFailed {
  public:
    CannotLoadScript(QString command, QStringList args, QString script):
        ScriptFailed("cannot load script data", command, args, script) {
    }
};

class ScriptNotFinished: public ScriptFailed {
  public:
    ScriptNotFinished(QString command, QStringList args, QString script):
        ScriptFailed("command hangs, timeout", command, args, script) {
    }
};

class ScriptExecutionFailed: public ScriptFailed {
  public:
    ScriptExecutionFailed(QString command, QStringList args, QString script,
                          int code, QString stdout, QString stderr):
        ScriptFailed("failed with exit code "+QString::number(code)
                     +(stdout.size()?"; stdout=\""+stdout+"\"":"")
                     +(stderr.size()?"; stderr=\""+stderr+"\"":""),
                     command, args, script) {
    }
};

class WrongNumberOfArguments: public TestFailed {
  public:
    WrongNumberOfArguments(QStringList vars, QStringList args):
        TestFailed(QString("function has %1 arguments, but %2 were given")
                   .arg(vars.size()).arg(args.size())) {
    }
};

class FunctionCallFailed: public TestFailed {
  public:
    FunctionCallFailed(QString name, QStringList vars, QStringList args,
                       const std::exception& x):
        TestFailed("function call failed: "+name) {
        for (QStringList::iterator var(vars.begin()), arg(args.begin());
             var<vars.end() && arg<args.end(); ++var, ++arg)
          _what += " "+*var+"="+*arg;
        _what += QString("; reason: ")+x.what();
    }
};

class FunctionNotFound: public TestFailed {
  public:
    FunctionNotFound(QString name): TestFailed("function not found: "+name) {
    }
};

class VariableNotFound: public TestFailed {
  public:
    VariableNotFound(QString name):
        TestFailed("variable not found: "+name) {
    }
};

class CheckFailed: public TestFailed {
  public:
    CheckFailed(QString value1, char cmp, QString value2):
        TestFailed(QString("check failed: %1 %2 %3")
                   .arg(value1).arg(cmp).arg(value1)) {
    }
};

class OpenIncludeFailed: public TestFailed {
  public:
    OpenIncludeFailed(QString file):
        TestFailed(QString("open include file %1 failed").arg(file)) {
    }
};

class ParseIncludeFailed: public TestFailed {
  public:
    ParseIncludeFailed(QString file, QString msg):
        TestFailed(QString("parse include file %1 failed with: %2").arg(file).arg(msg)) {
    }
};

class ExecuteIncludeFailed: public TestFailed {
  public:
    ExecuteIncludeFailed(QString file, QString msg):
        TestFailed(QString("error in included file %1: %2").arg(file).arg(msg)) {
    }
};

#endif
