#include "database.h"

namespace PyMKF {

// Definition of the masDatabase variable (declared as extern in common.h)
std::map<std::string, OpenMagnetics::Mas> masDatabase;

void load_databases(json databasesJson) {
    OpenMagnetics::load_databases(databasesJson, true);
}

std::string read_databases(std::string path, bool addInternalData) {
    try {
        auto masPath = std::filesystem::path{path};
        json data;
        std::string line;
        {
            data["coreMaterials"] = json();
            std::ifstream coreMaterials(masPath.append("core_materials.ndjson"));
            while (getline (coreMaterials, line)) {
                json jf = json::parse(line);
                data["coreMaterials"][jf["name"]] = jf;
            }
        }
        {
            data["coreShapes"] = json();
            std::ifstream coreMaterials(masPath.append("core_shapes.ndjson"));
            while (getline (coreMaterials, line)) {
                json jf = json::parse(line);
                data["coreShapes"][jf["name"]] = jf;
            }
        }
        {
            data["wires"] = json();
            std::ifstream coreMaterials(masPath.append("wires.ndjson"));
            while (getline (coreMaterials, line)) {
                json jf = json::parse(line);
                data["wires"][jf["name"]] = jf;
            }
        }
        {
            data["bobbins"] = json();
            std::ifstream coreMaterials(masPath.append("bobbins.ndjson"));
            while (getline (coreMaterials, line)) {
                json jf = json::parse(line);
                data["bobbins"][jf["name"]] = jf;
            }
        }
        {
            data["insulationMaterials"] = json();
            std::ifstream coreMaterials(masPath.append("insulation_materials.ndjson"));
            while (getline (coreMaterials, line)) {
                json jf = json::parse(line);
                data["insulationMaterials"][jf["name"]] = jf;
            }
        }
        {
            data["wireMaterials"] = json();
            std::ifstream coreMaterials(masPath.append("wire_materials.ndjson"));
            while (getline (coreMaterials, line)) {
                json jf = json::parse(line);
                data["wireMaterials"][jf["name"]] = jf;
            }
        }
        OpenMagnetics::load_databases(data, true, addInternalData);
        return "0";
    }
    catch (const std::exception &exc) {
        return std::string{exc.what()};
    }
}

std::string load_mas(std::string key, json masJson, bool expand) {
    try {
        OpenMagnetics::Mas mas(masJson);
        if (expand) {
            mas.set_magnetic(OpenMagnetics::magnetic_autocomplete(mas.get_mutable_magnetic()));
            mas.set_inputs(OpenMagnetics::inputs_autocomplete(mas.get_mutable_inputs(), mas.get_mutable_magnetic()));
        }
        masDatabase[key] = mas;
        return std::to_string(masDatabase.size());
    }
    catch (const std::exception &exc) {
        return std::string{exc.what()};
    }
}

std::string load_magnetic(std::string key, json magneticJson, bool expand) {
    try {
        OpenMagnetics::Magnetic magnetic(magneticJson);
        if (expand) {
            magnetic = OpenMagnetics::magnetic_autocomplete(magnetic);
        }
        OpenMagnetics::Mas mas;
        mas.set_magnetic(magnetic);
        masDatabase[key] = mas;
        return std::to_string(masDatabase.size());
    }
    catch (const std::exception &exc) {
        return std::string{exc.what()};
    }
}

std::string load_magnetics(std::string keys, json magneticJsons, bool expand) {
    try {
        json keysJson = json::parse(keys);
        for (size_t magneticIndex = 0; magneticIndex < magneticJsons.size(); magneticIndex++) {
            OpenMagnetics::Magnetic magnetic(magneticJsons[magneticIndex]);
            if (expand) {
                magnetic = OpenMagnetics::magnetic_autocomplete(magnetic);
            }
            OpenMagnetics::Mas mas;
            mas.set_magnetic(magnetic);
            masDatabase[to_string(keysJson[magneticIndex])] = mas;
        }
        return std::to_string(masDatabase.size());
    }
    catch (const std::exception &exc) {
        return std::string{exc.what()};
    }
}

json read_mas(std::string key) {
    json result;
    to_json(result, masDatabase[key]);
    return result;
}

size_t load_core_materials(std::string fileToLoad) {
    if (fileToLoad != "") {
        OpenMagnetics::load_core_materials(fileToLoad);
    }
    else {
        OpenMagnetics::load_core_materials();
    }
    return OpenMagnetics::coreMaterialDatabase.size();
}

size_t load_core_shapes(std::string fileToLoad) {
    if (fileToLoad != "") {
        OpenMagnetics::load_core_shapes(true, fileToLoad);
    }
    else {
        OpenMagnetics::load_core_shapes();
    }
    return OpenMagnetics::coreShapeDatabase.size();
}

size_t load_wires(std::string fileToLoad) {
    if (fileToLoad != "") {
        OpenMagnetics::load_wires(fileToLoad);
    }
    else {
        OpenMagnetics::load_wires();
    }
    return OpenMagnetics::wireDatabase.size();
}

void clear_databases() {
    OpenMagnetics::clear_databases();
}

bool is_core_material_database_empty() {
    return OpenMagnetics::coreMaterialDatabase.size() == 0;
}

bool is_core_shape_database_empty() {
    return OpenMagnetics::coreShapeDatabase.size() == 0;
}

bool is_wire_database_empty() {
    return OpenMagnetics::wireDatabase.size() == 0;
}

std::string load_magnetics_from_file(std::string path, bool expand) {
    try {
        std::ifstream in(path);
        if (in) {
            std::string line;
            while (getline(in, line)) {
                json jf = json::parse(line);
                OpenMagnetics::Magnetic magnetic(jf);
                if (expand) {
                    magnetic = OpenMagnetics::magnetic_autocomplete(magnetic);
                }
                std::string key = magnetic.get_manufacturer_info()->get_reference().value();
                OpenMagnetics::magneticsCache.load(key, magnetic);
            }
        }
        return std::to_string(OpenMagnetics::magneticsCache.size());
    }
    catch (const std::exception &exc) {
        return std::string{exc.what()};
    }
}

std::string clear_magnetic_cache() {
    try {
        OpenMagnetics::magneticsCache.clear();
        return std::to_string(OpenMagnetics::magneticsCache.size());
    }
    catch (const std::exception &exc) {
        return std::string{exc.what()};
    }
}

void register_database_bindings(py::module& m) {
    m.def("load_databases", &load_databases, "Load all databases from JSON");
    m.def("read_databases", &read_databases, "Read databases from file path");
    m.def("load_mas", &load_mas, "Load a MAS (Magnetic Agnostic Structure) object");
    m.def("load_magnetic", &load_magnetic, "Load a magnetic component");
    m.def("load_magnetics", &load_magnetics, "Load multiple magnetic components");
    m.def("read_mas", &read_mas, "Read a MAS object by key");
    m.def("load_core_materials", &load_core_materials, "Load core materials into database");
    m.def("load_core_shapes", &load_core_shapes, "Load core shapes into database");
    m.def("load_wires", &load_wires, "Load wires into database");
    m.def("clear_databases", &clear_databases, "Clear all loaded databases");
    m.def("is_core_material_database_empty", &is_core_material_database_empty, "Check if core material database is empty");
    m.def("is_core_shape_database_empty", &is_core_shape_database_empty, "Check if core shape database is empty");
    m.def("is_wire_database_empty", &is_wire_database_empty, "Check if wire database is empty");
    m.def("load_magnetics_from_file", &load_magnetics_from_file, "Load magnetic components from file");
    m.def("clear_magnetic_cache", &clear_magnetic_cache, "Clear cached magnetic calculations");
}

} // namespace PyMKF
