/*
 * Copyright (c) 2015-2018 TRUSTONIC LIMITED
 * All rights reserved
 *
 * The present software is the confidential and proprietary information of
 * TRUSTONIC LIMITED. You shall not disclose the present software and shall
 * use it only in accordance with the terms of the license agreement you
 * entered into with TRUSTONIC LIMITED. This software may be subject to
 * export or import laws in certain countries.
 */
#include <stdlib.h>
#include <string.h>     // memset
#include <stdio.h>      // fopen
#include <libgen.h>     // basename
#include <vector>
#include <ctype.h>
#include <algorithm>
#include <getopt.h>

#include "buildTag.h"
#include "log.h"
#include "DebugInfo.h"

static void printUsage(const char *path) {
    fprintf(stderr, "\nUsage: %s [-hit]\n", path);
    fprintf(stderr, "Copyright (c) Trustonic Limited 2015-2018.\n");
    fprintf(stderr, MOBICORE_COMPONENT_BUILD_TAG "\n");
    fprintf(stderr, "%-22s Dump file to read\n",                                                    "--input-file, -i file");
    fprintf(stderr, "%-22s Print all available information\n",                                      "--all");
    fprintf(stderr, "%-22s Print all dbgfs information\n",                                          "--all_dbgfs");
    fprintf(stderr, "%-22s Print kernel information\n",                                             "--kernel");
    fprintf(stderr, "%-22s Print kernel information with threads ordered by last modification\n",   "--kernel_ordered");
    fprintf(stderr, "%-22s Print kernel information with threads grouped by session\n",             "--kernel_grouped");
    fprintf(stderr, "%-22s Print last mcp commands (dbgfs file)\n",                                 "--last_mcp_cmd");
    fprintf(stderr, "%-22s Print last iwp commands (dbgfs file)\n",                                 "--last_iwp_cmd");
    fprintf(stderr, "%-22s Print structs and sessions (dbgfs file)\n",                              "--sessions");
    fprintf(stderr, "%-22s Print RTM information\n",                                                "--rtm");
    fprintf(stderr, "%-22s Print syscall events per thread\n",                                      "--thread_event");
    fprintf(stderr, "-v/V\t\tverbose mode (more data, need a bigger screen !)\n");

    fprintf(stderr, "%-22s Execute a 'Linux top' like action on the corresponding command, \n"
                    "\t\t       only the first argument is taken in account\n",                     "--top, -t");
    fprintf(stderr, "\t\t\t-> eg : ./tee-ps -t --rtm  (RTM info dynamically)\n");
    fprintf(stderr, "\t\t\t-> eg : ./tee-ps [-t --rtm --fastcall | --rtm --fastcall --top] (only displays RTM info)\n");

    fprintf(stderr, "%-22s Print Product ID and RTM information\n", "no option");
}

extern int optopt, opterr;
int main(int argc, char *args[]) {
    bool do_top = false;
    bool do_verbose = false;
    // Get the actions
    std::vector<DebugInfo::Action> actions;

    static struct option long_options[] = {
        /* These options set a flag. */
        {"all",             no_argument,        0, 0},
        {"all_dbgfs",       no_argument,        0, 0},
        {"help",            no_argument,        0, 'h'},
        {"kernel",          no_argument,        0, 0},
        {"kernel_ordered",  no_argument,        0, 0},
        {"kernel_grouped",  no_argument,        0, 0},
        {"last_mcp_cmd",    no_argument,        0, 0},
        {"last_iwp_cmd",    no_argument,        0, 0},
        {"rtm",             no_argument,        0, 0},
        {"sessions",        no_argument,        0, 0},
        {"thread_event",    no_argument,        0, 0},
        {"top",             no_argument,        0, 't'},
        {"input-file",      required_argument,  0, 'i'},
        {"verbose",         no_argument,        0, 'v'},
        {0, 0, 0, 0}
    };
    int opt;
    // Prevent the error message
    opterr = 0;
    int option_index = 0;
    bool failed = 0;
    const char* base_name = basename(args[0]);
    while ((opt = ::getopt_long(argc, args, "hi:tv", long_options, &option_index)) != -1) {
        switch (opt) {
            // Default options
            case 'h':
                printUsage(base_name);
                return EXIT_SUCCESS;

            case 'i':
            {
                DebugInfo debug_info = DebugInfo(optarg);
                return EXIT_SUCCESS;
            }

            case 't':
            {
                do_top = true;
                break;
            }

            case 'v':
            {
                do_verbose = true;
                break;
            }

            case 0 :
            {
                DebugInfo::Action action = DebugInfo::Action::UNKNOWN;
                if (std::string(long_options[option_index].name) == "all") {
                    action = DebugInfo::Action::ALL_INFO;
                } else if (std::string(long_options[option_index].name) == "all_dbgfs") {
                    action = DebugInfo::Action::DBGFS_ALL_INFO;
                } else if (std::string(long_options[option_index].name) == "sessions") {
                    action = DebugInfo::Action::DBGFS_SESSIONS_INFO;
                } else if (std::string(long_options[option_index].name) == "last_mcp_cmd") {
                    action = DebugInfo::Action::DBGFS_LAST_MCP_COMMAND_INFO;
                } else if (std::string(long_options[option_index].name) == "last_iwp_cmd") {
                    action = DebugInfo::Action::DBGFS_LAST_IWP_COMMAND_INFO;
                } else if (std::string(long_options[option_index].name) == "kernel") {
                    action = DebugInfo::Action::KERNEL_INFO;
                } else if (std::string(long_options[option_index].name) == "kernel_grouped") {
                    action = DebugInfo::Action::KERNEL_GROUPED_INFO;
                } else if (std::string(long_options[option_index].name) == "kernel_ordered") {
                    action = DebugInfo::Action::KERNEL_ORDERED_INFO;
                } else if (std::string(long_options[option_index].name) == "thread_event") {
                    action = DebugInfo::Action::THREAD_EVENT;
                } else if (std::string(long_options[option_index].name) == "rtm") {
                    action = DebugInfo::Action::RTM_INFO;
                } else if (std::string(long_options[option_index].name) == "input-file") {
                    DebugInfo debug_info = DebugInfo(optarg);
                    return EXIT_SUCCESS;
                } else {
                    fprintf(stderr, "Unknown option: %s\n", long_options[option_index].name);;
                    failed = true;
                }
                // Do not duplicate actions
                if (std::find(actions.begin(), actions.end(), action) == actions.end()) {
                    actions.push_back(action);
                }
                break;
            }

            case '?':
                if (optopt == 0) {
                    // long option
                    fprintf(stderr, "%s: invalid option \n", base_name);
                } else if (optopt == 'i') {
                    fprintf(stderr, "%s: missing argument to '-%c'\n", base_name, optopt);
                } else {
                    fprintf(stderr, "%s: invalid option '-%c'\n", base_name, optopt);
                }
                failed = true;
                break;

            default:
                failed = true;
        }
        if (failed) {
            break;
        }
    }
    if (failed) {
        printUsage(base_name);
        return EXIT_FAILURE;
    }
    // Here the command is valid and the actions are all stored

    // Setting up the debug info
    DebugInfo debug_info;
    if (!debug_info.exec(do_top, do_verbose, &actions)) {
        return EXIT_SUCCESS;
    }
    return EXIT_FAILURE;
}
