#include <filein/json_loader.hpp>

void save_configuration(const boost::json::value &value, const std::filesystem::path directory, const char* file_name)
{
    std::filesystem::path file_path = directory / std::filesystem::path(file_name);
    file_path.replace_extension("json");

    std::ofstream out_file(file_path);

    if (!out_file.is_open())
        std::cerr << "Error opening the file: " << file_path << std::endl;
    else
    {
        std::string indent = "    "; // 4-space-indent
        pretty_print(out_file, value, &indent);

        if(out_file.fail()) 
            std::cerr << "Error writing into export file" << std::endl;

        std::cout << "File created successfully: " << file_path << std::endl;       
    }
}

const boost::json::value read_file(const std::filesystem::path &filename)
{
    // This option needs newer version of boost json library
    // boost::json::parse_options opt;
    // opt.numbers = boost::json::number_precision::precise;
    std::ifstream t(filename);
    std::stringstream buffer;
    buffer << t.rdbuf();
    return boost::json::parse(buffer /*, opt */);
}

glm::mat4 get_transformation_matrix(const boost::json::value &matrix)
{
    if (matrix.is_string()) // if "identity"
    {
        return glm::mat4(1.0f);
    }

    glm::mat4 mat;
    const auto matrix_array = matrix.as_array();
    int row_index = 0;
    for (const auto &row : matrix_array)
    {
        const auto row_array = row.as_array();
        int col_index = 0;
        for (const auto &elem : row_array)
        {
            if (elem.is_int64())
                mat[col_index][row_index] = static_cast<float>(elem.as_int64());
            else
                mat[col_index][row_index] = static_cast<float>(elem.as_double());

            ++col_index;
        }

        ++row_index;
    }

    return mat;
}

// Code from:
// https://www.boost.org/doc/libs/1_76_0/libs/json/doc/html/json/examples.html
void pretty_print(std::ostream& os, boost::json::value const& jv, std::string* indent)
{
    std::string indent_;
    if(! indent)
        indent = &indent_;
    switch(jv.kind())
    {
    case boost::json::kind::object:
    {
        os << "{\n";
        indent->append(4, ' ');
        auto const& obj = jv.get_object();
        if(! obj.empty())
        {
            auto it = obj.begin();
            for(;;)
            {
                os << *indent << boost::json::serialize(it->key()) << " : ";
                pretty_print(os, it->value(), indent);
                if(++it == obj.end())
                    break;
                os << ",\n";
            }
        }
        os << "\n";
        indent->resize(indent->size() - 4);
        os << *indent << "}";
        break;
    }

    case boost::json::kind::array:
    {
        os << "[\n";
        indent->append(4, ' ');
        auto const& arr = jv.get_array();
        if(! arr.empty())
        {
            auto it = arr.begin();
            for(;;)
            {
                os << *indent;
                pretty_print( os, *it, indent);
                if(++it == arr.end())
                    break;
                os << ",\n";
            }
        }
        os << "\n";
        indent->resize(indent->size() - 4);
        os << *indent << "]";
        break;
    }

    case boost::json::kind::string:
    {
        os << boost::json::serialize(jv.get_string());
        break;
    }

    case boost::json::kind::uint64:
        os << jv.get_uint64();
        break;

    case boost::json::kind::int64:
        os << jv.get_int64();
        break;

    case boost::json::kind::double_:
        os << jv.get_double();
        break;

    case boost::json::kind::bool_:
        if(jv.get_bool())
            os << "true";
        else
            os << "false";
        break;

    case boost::json::kind::null:
        os << "null";
        break;
    }

    if(indent->empty())
        os << "\n";
}