#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...\n";
    for(auto it = modules.begin(); it != modules.end(); it++) {
        void* handle = (*it)->getHandle();
        std::cout << "retrieved handle\n";
        delete *it;
        std::cout << "deleted module\n";
        dlclose(handle);
        std::cout << "closed lib\n";
        it = modules.erase(it);
        std::cout << "erased list node\n";
    }
}

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

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

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

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

    void* h = dlopen(lib.c_str(), RTLD_NOW);

    if(!h) {
        std::cout << "could not open lib: \"" << lib.c_str() << "\"\nError: " << dlerror() << std::endl;
        return;
    }

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

    Module* m = create(h, App::Get());

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

    modules.push_back(m);
    m->setSelf(--modules.end());
    modules.end()++;
}
        
void App::unload(decltype(Module::self) it) {
    void* h = (*it)->getHandle();
    std::cout << "retrieved handle\nAttempting to delete module: " << (*it)->getName() << std::endl;
    delete *it;
    std::cout << "deleted module\n";
    char* mesg = dlerror();
    if(h && !mesg) {
        std::cout << "h: " << h << " attempting to dlclose\n";
        dlclose(h);
        std::cout << "closed handle\n";
    } else {
        std::cout << "error: " << mesg << "\n";
    }
}

void App::stopModule(decltype(Module::self) it) {
    toClose.push_back(*it);
    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) {
        std::cout << "Attempting to load: " << argv[i] << std::endl;
        load(argv[i]);

        i++;
    }
}

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