#include <gtest/gtest.h>
#include <json.hpp>
#include <filesystem>
//#define private public
#include <Registration.h>
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h> 


#include <windows.h>
#include <string>
#include <iostream>

using namespace mdd;
namespace TEST_MODULE_SQL {
    const char* db_path = "../../../../../lib/test/db/materials.db";
    
    std::string ExePath() {
        TCHAR buffer[MAX_PATH] = { 0 };
        GetModuleFileName(NULL, buffer, MAX_PATH);
        std::string::size_type pos = std::string(buffer).find_last_of("\\/");
        return std::string(buffer).substr(0, pos);
    }


    static int callback(void* data, int argc, char** argv, char** azColName) {
        int i;
        fprintf(stderr, "%s: ", (const char*)data);
        for (i = 0; i < argc; i++) {
            printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
        }
        printf("\n");
        return 0;
    }

    void sqlite3_exec_debug(sqlite3* db, char* sql) {
        char* zErrMsg = 0;
        int rc;

        // Execute SQL statement
        rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);

        if (rc != SQLITE_OK) {
            fprintf(stderr, "SQL error: %s\n", zErrMsg);
            sqlite3_free(zErrMsg);
        }
        else {
            fprintf(stdout, "Table created successfully\n");
        }
    }

    void create_test_db(std::string path) {
            sqlite3* db;
            int rc;
            char* sql;

            // Open database
            rc = sqlite3_open(path.c_str(), &db);

            if (rc) {
                fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
                return;
            }
            else {
                fprintf(stdout, "Opened database successfully\n");
            }

            // Create SQL statement
            sql = "CREATE TABLE MATERIAL("  \
                "ID INTEGER PRIMARY KEY     AUTOINCREMENT," \
                "NAME           TEXT    NOT NULL," \

                "E11            FLOAT   NOT NULL," \
                "E22            FLOAT   NOT NULL," \
                "E33            FLOAT   NOT NULL," \

                "PR12           FLOAT   NOT NULL," \
                "PR13           FLOAT   NOT NULL," \
                "PR23           FLOAT   NOT NULL," \

                "G12            FLOAT   NOT NULL," \
                "G13            FLOAT   NOT NULL," \
                "G23            FLOAT   NOT NULL," \

                "DENS           FLOAT           ," \

                "ALP11          FLOAT           ," \
                "ALP22          FLOAT           ," \
                "ALP33          FLOAT           ," \

                "K11            FLOAT           ," \
                "K22            FLOAT           ," \
                "K33            FLOAT           ," \

                "BET11          FLOAT           ," \
                "BET22          FLOAT           ," \
                "BET33          FLOAT           ," \

                "D11            FLOAT           ," \
                "D22            FLOAT           ," \
                "D33            FLOAT           );";

            sqlite3_exec_debug(db, sql);

           // Create SQL statement
            sql = "INSERT INTO MATERIAL (   NAME,        E11,    E22,  E33,  PR12, PR13, PR23, G12,   G13,   G23,    DENS, ALP11,  ALP22, ALP33, K11,    K22,   K33,   BET11, BET22, BET33, D11,    D22,    D33) "  \
                "VALUES (                   'EP-E Glas', 42.5E9, 11E9, 11E9, 0.28, 0.28, 0.28, 4.2E9, 4.2E9, 2.56E9, 1950, 5.7E-6, 45E-6, 45E-6, 0.72E3, 0.5E3, 0.5E3, 0E-3,  4E-3,  4E-3,  4.4E-3, 3.1E-3, 3.1E-3); ";

            sqlite3_exec_debug(db, sql);

            sqlite3_close(db);
    }

    auto regi = Registration();

    TEST(ModuleSQL, TestSQL) {
        if (!std::filesystem::exists(db_path)) {
            create_test_db(db_path);
        }

        sqlite3* db;
        char* zErrMsg = 0;
        int rc;
        char* sql;
        const char* data = "Callback function called";

        // Open database
        rc = sqlite3_open(db_path, &db);

        if (rc) {
            fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
            return;
        }
        else {
            fprintf(stderr, "Opened database successfully\n");
        }

        // Create SQL statement
        sql = "SELECT * from MATERIAL";

        // Execute SQL statement
        rc = sqlite3_exec(db, sql, callback, (void*)data, &zErrMsg);

        if (rc != SQLITE_OK) {
            fprintf(stderr, "SQL error: %s\n", zErrMsg);
            sqlite3_free(zErrMsg);
        }
        else {
            fprintf(stdout, "Operation done successfully\n");
        }
        sqlite3_close(db);

        EXPECT_TRUE(std::filesystem::exists(db_path));
    }

   
    TEST(ModuleSQL, TestGetSQLTableStructure) {
        if (!std::filesystem::exists(db_path)) {
            create_test_db(db_path);
        }

        sqlite3* db;
        char* zErrMsg = 0;
        int rc;
        char* sql;
        const char* data = "Callback function called";

        // Open database
        rc = sqlite3_open(db_path, &db);

        if (rc) {
            fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
            return;
        }
        else {
            fprintf(stderr, "Opened database successfully\n");
        }

        // Create SQL statement
        sql =   "SELECT * "\
                "FROM   sqlite_master "\
                "WHERE type = 'table' ";

        // Execute SQL statement
        rc = sqlite3_exec(db, sql, callback, (void*)data, &zErrMsg);

        if (rc != SQLITE_OK) {
            fprintf(stderr, "SQL error: %s\n", zErrMsg);
            sqlite3_free(zErrMsg);
        }
        else {
            fprintf(stdout, "Operation done successfully\n");
        }
        sqlite3_close(db);

        EXPECT_TRUE(std::filesystem::exists(db_path));
    }

    TEST(ModuleSQL, OpenDB) {
        if (!std::filesystem::exists(db_path)) {
            create_test_db(db_path);
        }

        std::cout << ExePath() << std::endl;
        IModule::Ptr mod = regi.generateModule("ModuleSQL");
        json config = mod->getConfiguration();
        config["configure"]["database"]["value"] = db_path;
        mod->configure(config);

        EXPECT_TRUE(std::filesystem::exists(db_path));
    }

    TEST(ModuleSQL, TestDataExists) {
        if (!std::filesystem::exists(db_path)) {
            create_test_db(db_path);
        }

        sqlite3* db;
        char* zErrMsg = 0;
        int rc;
        char* sql;
        const char* data = "Callback function called";

        // Open database
        rc = sqlite3_open(db_path, &db);

        if (rc) {
            fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
            return;
        }
        else {
            fprintf(stderr, "Opened database successfully\n");
        }

        // Create SQL statement
        sql = "SELECT * from MATERIAL";

        // Execute SQL statement
        rc = sqlite3_exec(db, sql, callback, (void*)data, &zErrMsg);

        if (rc != SQLITE_OK) {
            fprintf(stderr, "SQL error: %s\n", zErrMsg);
            sqlite3_free(zErrMsg);
        }
        else {
            fprintf(stdout, "Operation done successfully\n");
        }
        sqlite3_close(db);

        EXPECT_TRUE(std::filesystem::exists(db_path));
    }
    //*/
}