#include "App.h"

App* App::instance = nullptr;

App::App(const int& argc, char* argv[]) {

    if(instance != nullptr) {
        std::cout << "App already exists\nThere can only be one!\n";
        std::abort();
    }

    std::cout << "Initializing...\n";

    instance = this;

    handleArgs(argc, argv);

}

App::~App() {

    std::cout << "\nExiting...";
    for(auto it = modules.begin(); it != modules.end(); it++) {
        void* handle = (*it)->getHandle();
        delete *it;
        dlclose(handle);
        modules.erase(it);
    }
}

void App::run() {
    std::cout << "\nTesting...\n";

    // Main loop
    while (!done && !modules.empty()) {

        for(auto it = modules.begin(); it != modules.end(); it++)
            (*it)->run();
    }
    
}

void App::load(std::string lib) {

    void* handle = dlopen(lib.c_str(), RTLD_LAZY);

    if(!handle) {
        std::cout << "could not open lib!\n";
        return;
    }

    Module::create_t* create = (Module::create_t*) dlsym(handle, "create");
    
    if(dlerror()) {
        std::cout << "error finding create function in file: " << lib << std::endl;
    }

    Module* m = create(handle);

    for(auto it = modules.begin(); it != modules.end(); it++) {
        
        if((*it)->getName() != m->getName()) {
            std::cout << "Module \"" << m->getName() << "\" is already loaded!\n";
            return;
        }
    }

    m->setSelf(modules.end());
    modules.push_back(m);
}
        
void App::unload(std::list<Module*>::iterator it) {
    void* handle = (*it)->getHandle();
    delete *it;
    dlclose(handle);
    modules.erase(it);
}

void App::handleArgs(const int& argc, char* argv[]) {

    int i = 1;

    if(argc == 1) {
        printHelp();
        end();
    }
    
    while(i < argc) {
        if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
            printHelp();
            end();
        } else {
            break;
        }
        i++;
    }

    while(i < argc) {
        
        load(argv[i]);

        i++;
    }
}

void App::printHelp() {
    std::cout << "archimedes [ -h | --help ] /path/to/some/library.so\n";
}
