// SPDX-FileCopyrightText: 2021 Ernie Pasveer <epasveer@att.net>
//
// SPDX-License-Identifier: GPL-3.0-or-later

#include "SeerMainWindow.h"
#include "SeerUtl.h"
#include "QProcessInfo.h"
#include <QtWidgets/QApplication>
#include <QtWidgets/QStyleFactory>
#include <QtGui/QIcon>
#include <QtCore/QCommandLineParser>
#include <QtCore/QCommandLineOption>
#include <QtCore/QStringList>
#include <QtCore/QString>
#include <QtCore/QTextStream>
#include <QtCore/QObject>
#include <QtCore/QDebug>
#include <QtCore/QDirIterator>
#include <iostream>

static void seerhelp() {

    QString text;
    QString fileName(":/seer/resources/help/seergdb.hlp");

    QFile file(fileName);

    if (file.open(QIODevice::ReadOnly) == false) {
        text = "seer: no help available.\n";
    }else{
        text = file.readAll();
    }

    file.close();

    QTextStream(stdout) << text;

    exit(0);
}

int main (int argc, char* argv[]) {

    //
    // Set up logging and message formats.
    //
    QLoggingCategory::setFilterRules("*.debug=false\n"
                                     "*.info=false\n"
                                     "*.warning=false\n"
                                     "*.critical=true");

    qSetMessagePattern("[%{time hh:mm:ss}][%{function}:%{line}][%{category}] %{message}");

    //
    // Create the app.
    //
    QApplication app(argc, argv);

    QCoreApplication::setApplicationName("seergdb");
    QCoreApplication::setOrganizationName("seergdb");
    QCoreApplication::setApplicationVersion(Seer::version() + " - Ernie Pasveer (c)2021 - 2025");

    //
    // Parse arguments.
    //
    QCommandLineParser parser;
    parser.setApplicationDescription("\nSeer - A gui frontend for gdb.");
    parser.setOptionsAfterPositionalArgumentsMode(QCommandLineParser::ParseAsPositionalArguments); // Treat all arguments after the executable name as positional arguments too.
    parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); // Treat all arguments long options.
    parser.addVersionOption();

    // Run or start options.
    QCommandLineOption runOption(QStringList() << "r" << "run");
    parser.addOption(runOption);

    QCommandLineOption startOption(QStringList() << "s" << "start");
    parser.addOption(startOption);

    QCommandLineOption attachOption(QStringList() << "attach", "", "pid");
    parser.addOption(attachOption);

    QCommandLineOption connectOption(QStringList() << "connect", "", "medium");
    parser.addOption(connectOption);

    QCommandLineOption rrOption(QStringList() << "rr", "", "hostport");
    parser.addOption(rrOption);

    QCommandLineOption corefileOption(QStringList() << "core", "", "corefile");
    parser.addOption(corefileOption);

    QCommandLineOption projectOption(QStringList() << "project", "", "project");
    parser.addOption(projectOption);

    QCommandLineOption configOption(QStringList() << "config");
    parser.addOption(configOption);

    QCommandLineOption workingdirOption(QStringList() << "cwd" << "working-dir", "", "workingdirectory");
    parser.addOption(workingdirOption);

    QCommandLineOption symbolfileOption(QStringList() << "sym" << "symbol-file", "", "symbolfile");
    parser.addOption(symbolfileOption);

    QCommandLineOption breakfileOption(QStringList() << "bl" << "break-load", "", "breakpointfile");
    parser.addOption(breakfileOption);

    QCommandLineOption breakfunctionOption(QStringList() << "bf" << "break-function", "", "breakpointfunction");
    parser.addOption(breakfunctionOption);

    QCommandLineOption breaksourceOption(QStringList() << "bs" << "break-source", "", "breakpointsource");
    parser.addOption(breaksourceOption);

    QCommandLineOption showAssemblyTabOption(QStringList() << "sat" << "show-assembly-tab", "", "showassemblytab");
    parser.addOption(showAssemblyTabOption);

    QCommandLineOption startAddressRandomizeOption(QStringList() << "sar" << "start-address-randomize", "", "startaddressrandomize");
    parser.addOption(startAddressRandomizeOption);

    QCommandLineOption nonStopModeOption(QStringList() << "nsm" << "non-stop-mode", "", "nonstopmode");
    parser.addOption(nonStopModeOption);

    QCommandLineOption gdbProgramOption(QStringList() << "gdb-program", "", "gdbprogram");
    parser.addOption(gdbProgramOption);

    QCommandLineOption gdbArgumentsOption(QStringList() << "gdb-arguments", "", "gdbarguments");
    parser.addOption(gdbArgumentsOption);

    QCommandLineOption xxxdebugOption(QStringList() << "xxx-debug");
    parser.addOption(xxxdebugOption);

    QCommandLineOption xxxresourcesOption(QStringList() << "xxx-resources");
    parser.addOption(xxxresourcesOption);

    QCommandLineOption xxxstylesOption(QStringList() << "xxx-styles");
    parser.addOption(xxxstylesOption);

    QCommandLineOption helpOption(QStringList() << "h" << "help");
    parser.addOption(helpOption);

    // A positional argument for executable name.
    // All other arguments after that are treated as positional arguments for the executable.
    parser.addPositionalArgument("executableandarguments", "");

    // Process the arguments.
    parser.process(app);

    if (parser.isSet(helpOption)) {
        seerhelp();
    }

    if (parser.isSet(xxxdebugOption)) {
        QLoggingCategory::setFilterRules("*.debug=false\n"
                                         "*.info=false\n"
                                         "*.warning=false\n"
                                         "*.critical=true\n"
                                         "default.debug=true");

        QSettings settings;
        qDebug() << "SETTINGS"    << settings.fileName();
    }

    if (parser.isSet(xxxresourcesOption)) {
        QTextStream(stdout) << "RESOURCES" << "\n";
        QDirIterator it(":", QDirIterator::Subdirectories);
        while (it.hasNext()) {
            QTextStream(stdout) << "   " << it.next() << "\n";
        }
        QTextStream(stdout) << "ENDRESOURCES" << "\n";
        return 0;
    }

    if (parser.isSet(xxxstylesOption)) {
        QTextStream(stdout) << "STYLES" << "\n";
        QStringList styles = QStyleFactory::keys();
        for (auto style : styles) {
            QTextStream(stdout) << "   " << style << "\n";
        }
        QTextStream(stdout) << "ENDSTYLES" << "\n";
        return 0;
    }

    // Get the positional arguments. (The ones at the end of the line - executable name and its arguments.
    QStringList positionalArguments = parser.positionalArguments();

    // Get the executable name from the arguments;
    QString executableName;

    if (positionalArguments.size() > 0) {
        executableName = positionalArguments.takeFirst();
    }

    // Get launch mode.
    QString launchMode    = "none";
    QString breakMode     = "none";
    int     executablePid = -1;
    QString executableConnectHostPort;
    QString executableRRTraceDirectory;
    QString executableWorkingDirectory;
    QString executableSymbolFilename;
    QString executableBreakpointsFilename;
    QString executableBreakpointFunctionName;
    QString executableBreakpointSourceFilenameAndLineno;
    QString executableShowAssemblyTab;
    QString executableStartAddressRandomize;
    QString executableNonStopMode;
    QString executableCoreFilename;
    QString projectFilename;
    QString gdbProgram;
    QString gdbArguments;

    if (parser.isSet(runOption)) {
        launchMode = "run";
        breakMode  = "none";
    }

    if (parser.isSet(startOption)) {
        launchMode = "run";
        breakMode  = "inmain";
    }

    if (parser.isSet(workingdirOption)) {
        executableWorkingDirectory = parser.value(workingdirOption);
    }

    if (parser.isSet(symbolfileOption)) {
        executableSymbolFilename = parser.value(symbolfileOption);
    }

    if (parser.isSet(breakfileOption)) {
        executableBreakpointsFilename = parser.value(breakfileOption);
    }

    if (parser.isSet(breakfunctionOption)) {
        executableBreakpointFunctionName = parser.value(breakfunctionOption);
        breakMode  = "infunction";
    }

    if (parser.isSet(breaksourceOption)) {
        executableBreakpointSourceFilenameAndLineno = parser.value(breaksourceOption);
        breakMode  = "insource";
    }

    if (parser.isSet(showAssemblyTabOption)) {
        executableShowAssemblyTab = parser.value(showAssemblyTabOption);
    }

    if (parser.isSet(startAddressRandomizeOption)) {
        executableStartAddressRandomize = parser.value(startAddressRandomizeOption);
    }

    if (parser.isSet(nonStopModeOption)) {
        executableNonStopMode = parser.value(nonStopModeOption);
    }

    if (parser.isSet(attachOption)) {
        launchMode = "attach";

        QString pid = parser.value(attachOption);

        executablePid = pid.toInt();

        if (executableName == "") {

            QProcessInfo info = QProcessInfo::populate(executablePid);

            executableName = info.path() + "/" + info.name();
        }
    }

    if (parser.isSet(connectOption)) {
        launchMode = "connect";

        executableConnectHostPort = parser.value(connectOption);
    }

    if (parser.isSet(rrOption)) {
        launchMode = "rr";

        executableRRTraceDirectory = parser.value(rrOption);
    }

    if (parser.isSet(corefileOption)) {
        launchMode = "corefile";

        executableCoreFilename = parser.value(corefileOption);
    }

    if (parser.isSet(projectOption)) {
        launchMode = "project";

        projectFilename = parser.value(projectOption);
    }

    if (parser.isSet(configOption)) {
        launchMode = "configdialog";
    }

    if (parser.isSet(gdbProgramOption)) {
        gdbProgram = parser.value(gdbProgramOption);
    }

    if (parser.isSet(gdbArgumentsOption)) {
        gdbArguments = parser.value(gdbArgumentsOption);
    }


    //
    // Start Seer
    //
    SeerMainWindow seer;

    seer.setWindowIcon(QIcon(":/seer/resources/seergdb_64x64.png"));
    seer.setExecutableName(executableName);
    seer.setExecutableWorkingDirectory(executableWorkingDirectory);
    seer.setExecutableSymbolName(executableSymbolFilename);
    seer.setExecutableArguments(positionalArguments);

    if (executableBreakpointsFilename != "") {
        seer.setExecutableBreakpointsFilename(executableBreakpointsFilename);
    }

    if (executableBreakpointFunctionName != "") {
        seer.setExecutableBreakpointFunctionName(executableBreakpointFunctionName);
    }

    if (executableBreakpointSourceFilenameAndLineno != "") {
        seer.setExecutableBreakpointSourceName(executableBreakpointSourceFilenameAndLineno);
    }

    if (executableShowAssemblyTab != "") {
        if (executableShowAssemblyTab == "yes") {
            seer.setExecutableShowAssemblyTab(true);

        }else if (executableShowAssemblyTab == "no") {
            seer.setExecutableShowAssemblyTab(false);

        }else{
            printf("%s: Unknown --show-assembly-tab option '%s'\n", qPrintable(QCoreApplication::applicationName()), qPrintable(executableShowAssemblyTab));
            return 1;
        }
    }

    if (executableStartAddressRandomize != "") {
        if (executableStartAddressRandomize == "yes") {
            seer.setExecutableRandomizeStartAddress(true);

        }else if (executableStartAddressRandomize == "no") {
            seer.setExecutableRandomizeStartAddress(false);

        }else{
            printf("%s: Unknown --start-address-randomize option '%s'\n", qPrintable(QCoreApplication::applicationName()), qPrintable(executableStartAddressRandomize));
            return 1;
        }
    }

    if (executableNonStopMode != "") {
        if (executableNonStopMode == "yes") {
            seer.setExecutableNonStopMode(true);

        }else if (executableNonStopMode == "no") {
            seer.setExecutableNonStopMode(false);

        }else{
            printf("%s: Unknown --non-stop-mode option '%s'\n", qPrintable(QCoreApplication::applicationName()), qPrintable(executableNonStopMode));
            return 1;
        }
    }

    if (gdbProgram != "") {
        seer.setGdbProgramOverride(gdbProgram);
    }

    if (gdbArguments != "") {
        seer.setGdbArgumentsOverride(gdbArguments);
    }

    qDebug() << "EXECUTABLENAME"    << executableName;
    qDebug() << "WORKINGDIRECTORY"  << executableWorkingDirectory;
    qDebug() << "SYMBOLNAME"        << executableSymbolFilename;
    qDebug() << "PID"               << executablePid;
    qDebug() << "CONNECTHOST"       << executableConnectHostPort;
    qDebug() << "RRTRACEDIRECTORY"  << executableRRTraceDirectory;
    qDebug() << "COREFILENAME"      << executableCoreFilename;
    qDebug() << "PROJECTFILE"       << projectFilename;
    qDebug() << "ARGUMENTS"         << positionalArguments;

    seer.setExecutablePid(executablePid);
    seer.setExecutableConnectHostPort(executableConnectHostPort);
    seer.setExecutableRRTraceDirectory(executableRRTraceDirectory);
    seer.setExecutableCoreFilename(executableCoreFilename);
    seer.setProjectFilename(projectFilename);

    seer.launchExecutable(launchMode, breakMode);

    seer.show();
    seer.activateWindow();

    return app.exec();
}

