<commit_before>#pragma once

#include <tuple>


#include <v8.h>

#include "casts_impl.h"
#include "v8helpers.h"
#include "unspecified_parameter_value.h"

namespace v8toolkit {

// define this to log debugging messages while running
//#define V8TOOLKIT_PARAMETER_BUILDER_LOGGING

#ifdef V8TOOLKIT_PARAMETER_BUILDER_LOGGING
#define PB_PRINT(format_string, ...) \
    std::cerr << fmt::format(format_string, ##__VA_ARGS__) << std::endl;
#else
#define PB_PRINT(format_string, ...)
#endif


/**
 * Used by CallCallable to build an actual list of values to send to a function.  Each call to ParameterBuilder
 * is responsible for one parameter.
 *
 * If the parameter isn't self contained (like a char *), then the memory allocated for it will be stored in `stuff`
 * which is automatically cleaned up when the function returns.
 *
 * If no explicit parameter is provided from JavaScript, a default value may be available in `default_args_tuple`
 */
template<class T, class = void>
struct ParameterBuilder;


/**
 * Handles lvalue reference types by delegating the call to the pointer version and dereferencing the
 * returned pointer - except for char type, because char [const] * is special
 */
template <class T>
struct ParameterBuilder<T &, std::enable_if_t<!std::is_same_v<std::remove_const_t<T>, char> && !is_wrapped_type_v<T>>> {
    template<int default_arg_position = -1, class DefaultArgsTuple = std::tuple<>>
    T & operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
                 std::vector<std::unique_ptr<StuffBase>> & stuff,
                 DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {
        PB_PRINT("ParameterBuilder handling lvalue reference: {}", demangle<T>());
        return * ParameterBuilder<T*>()(info, i, stuff, std::move(default_args_tuple));
    }
};


template <class T>
struct ParameterBuilder<T &, std::enable_if_t<std::is_same_v<std::remove_const_t<T>, char> && !is_wrapped_type_v<T>>> {
    template<int default_arg_position = -1, class DefaultArgsTuple = std::tuple<>>
    T & operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
                   std::vector<std::unique_ptr<StuffBase>> & stuff,
                   DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {
        PB_PRINT("ParameterBuilder handling lvalue reference for char: {}", demangle<T>());
        auto value = info[i++];
        auto isolate = info.GetIsolate();
        stuff.push_back(std::make_unique<Stuff<char>>(std::make_unique<char>(CastToNative<char>()(isolate, value))));
        return *static_cast<Stuff<char> &>(*stuff.back()).get();

    }
};


template <class T>
struct ParameterBuilder<T &&, std::enable_if_t<!is_wrapped_type_v<T>>> {

    template<int default_arg_position = -1, class DefaultArgsTuple = std::tuple<>>
    T && operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
                   std::vector<std::unique_ptr<StuffBase>> & stuff,
                   DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {

        PB_PRINT("ParameterBuilder handling rvalue reference to unwrapped type: {}", demangle<T>());
//        std::cerr << fmt::format("default_arg_position: {}", default_arg_position) << std::endl;
        return std::move(*(ParameterBuilder<T*>().template operator()<default_arg_position>(info, i, stuff, std::move(default_args_tuple))));
    }
};



/**
* Pointers to unwrapped types
*/
template <class T>
struct ParameterBuilder<T*, std::enable_if_t<!is_wrapped_type_v<T> >
> {
    using WrappedT = std::remove_const_t<std::remove_pointer_t<std::remove_reference_t<T>>>;

    template<int default_arg_position = -1, class DefaultArgsTuple = std::tuple<>>
    WrappedT * operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
                   std::vector<std::unique_ptr<StuffBase>> & stuff,
                   DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {

        PB_PRINT("ParameterBuilder handling pointers to unwrapped types: {}", demangle<T>());
        if (i >= info.Length()) {
            return get_default_parameter<WrappedT, default_arg_position>(info, i, stuff, default_args_tuple);
        } else {
            stuff.push_back(Stuff<WrappedT>::stuffer(CastToNative<WrappedT>()(info.GetIsolate(), info[i++])));
            return static_cast<Stuff<WrappedT> &>(*stuff.back()).get();
        }
    }
};



/**
* Pointers and references to wrapped types.
* Wrapped types already have a pointer to memory for an existing C++ object in side the javascript object, so no need
* to populate `stuff`
*/
template <class T>
struct ParameterBuilder<T*, std::enable_if_t<is_wrapped_type_v<T> >
> {
    using WrappedT = std::remove_pointer_t<std::remove_reference_t<T>>;

    template<int default_arg_position = -1, class DefaultArgsTuple = std::tuple<>>
    WrappedT * operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
                   std::vector<std::unique_ptr<StuffBase>> & stuff,
                   DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {

        PB_PRINT("ParameterBuilder handling pointer to wrapped type: {}", demangle<T>());

        //std::cerr << fmt::format("ParameterBuilder type: pointer-to {} default_arg_position = {}", v8toolkit::demangle<T>(), default_arg_position) << std::endl;
        if (i >= info.Length()) {
            return *get_default_parameter<WrappedT *, default_arg_position>(info, i, stuff, default_args_tuple);

        } else {
            // try to get the object from inside a javascript object, otherwise, fall back to a CastToNative<T> call
            if (auto wrapped_pointer = CastToNative<WrappedT *>()(info.GetIsolate(), info[i++])) {
                return wrapped_pointer;

            } else if constexpr(CastToNative<WrappedT>::callable()) {
                stuff.push_back(Stuff<WrappedT>::stuffer(CastToNative<WrappedT>()(info.GetIsolate(), info[i++])));
                return static_cast<Stuff<WrappedT> &>(*stuff.back()).get();
            } else {
                throw CastException("Tried to send a pointer to a function but the JavaScript object wasn't a wrapped "
                                        "C++ object and a new object couldn't be created from the JavaScript value provided");
            }
        }
    }
};



template<class T>
struct ParameterBuilder<T, std::enable_if_t<!std::is_pointer_v<T> && !std::is_reference_v<T> && !is_wrapped_type_v<T>>> {
    using NoRefT = std::remove_reference_t<T>;


    template<int default_arg_position = -1, class DefaultArgsTuple = std::tuple<>>
    T operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
               std::vector<std::unique_ptr<StuffBase>> & stuff,
               DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {<commit_msg>fixed handling of const default parameter types<commit_after>#pragma once

#include <tuple>


#include <v8.h>

#include "casts_impl.h"
#include "v8helpers.h"
#include "unspecified_parameter_value.h"

namespace v8toolkit {

// define this to log debugging messages while running
//#define V8TOOLKIT_PARAMETER_BUILDER_LOGGING

#ifdef V8TOOLKIT_PARAMETER_BUILDER_LOGGING
#define PB_PRINT(format_string, ...) \
    std::cerr << fmt::format(format_string, ##__VA_ARGS__) << std::endl;
#else
#define PB_PRINT(format_string, ...)
#endif


/**
 * Used by CallCallable to build an actual list of values to send to a function.  Each call to ParameterBuilder
 * is responsible for one parameter.
 *
 * If the parameter isn't self contained (like a char *), then the memory allocated for it will be stored in `stuff`
 * which is automatically cleaned up when the function returns.
 *
 * If no explicit parameter is provided from JavaScript, a default value may be available in `default_args_tuple`
 */
template<class T, class = void>
struct ParameterBuilder;


/**
 * Handles lvalue reference types by delegating the call to the pointer version and dereferencing the
 * returned pointer - except for char type, because char [const] * is special
 */
template <class T>
struct ParameterBuilder<T &, std::enable_if_t<!std::is_same_v<std::remove_const_t<T>, char> && !is_wrapped_type_v<T>>> {
    template<int default_arg_position = -1, class DefaultArgsTuple = std::tuple<>>
    T & operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
                 std::vector<std::unique_ptr<StuffBase>> & stuff,
                 DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {
        PB_PRINT("ParameterBuilder handling lvalue reference: {}", demangle<T>());
        return * ParameterBuilder<T*>()(info, i, stuff, std::move(default_args_tuple));
    }
};


template <class T>
struct ParameterBuilder<T &, std::enable_if_t<std::is_same_v<std::remove_const_t<T>, char> && !is_wrapped_type_v<T>>> {
    template<int default_arg_position = -1, class DefaultArgsTuple = std::tuple<>>
    T & operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
                   std::vector<std::unique_ptr<StuffBase>> & stuff,
                   DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {
        PB_PRINT("ParameterBuilder handling lvalue reference for char: {}", demangle<T>());
        auto value = info[i++];
        auto isolate = info.GetIsolate();
        stuff.push_back(std::make_unique<Stuff<char>>(std::make_unique<char>(CastToNative<char>()(isolate, value))));
        return *static_cast<Stuff<char> &>(*stuff.back()).get();

    }
};


template <class T>
struct ParameterBuilder<T &&, std::enable_if_t<!is_wrapped_type_v<T>>> {

    template<int default_arg_position = -1, class DefaultArgsTuple = std::tuple<>>
    T && operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
                   std::vector<std::unique_ptr<StuffBase>> & stuff,
                   DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {

        PB_PRINT("ParameterBuilder handling rvalue reference to unwrapped type: {}", demangle<T>());
//        std::cerr << fmt::format("default_arg_position: {}", default_arg_position) << std::endl;
        return std::move(*(ParameterBuilder<T*>().template operator()<default_arg_position>(info, i, stuff, std::move(default_args_tuple))));
    }
};



/**
* Pointers to unwrapped types
*/
template <class T>
struct ParameterBuilder<T*, std::enable_if_t<!is_wrapped_type_v<T> >
> {
    using WrappedT = std::remove_const_t<std::remove_pointer_t<std::remove_reference_t<T>>>;

    template<int default_arg_position = -1, class DefaultArgsTuple = std::tuple<>>
    WrappedT * operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
                   std::vector<std::unique_ptr<StuffBase>> & stuff,
                   DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {

        PB_PRINT("ParameterBuilder handling pointers to unwrapped types: {}", demangle<T>());
        if (i >= info.Length()) {
            return get_default_parameter<WrappedT, default_arg_position>(info, i, stuff, default_args_tuple);
        } else {
            stuff.push_back(Stuff<WrappedT>::stuffer(CastToNative<WrappedT>()(info.GetIsolate(), info[i++])));
            return static_cast<Stuff<WrappedT> &>(*stuff.back()).get();
        }
    }
};



/**
* Pointers and references to wrapped types.
* Wrapped types already have a pointer to memory for an existing C++ object in side the javascript object, so no need
* to populate `stuff`
*/
template <class T>
struct ParameterBuilder<T*, std::enable_if_t<is_wrapped_type_v<T> >
> {
    using WrappedT = std::remove_pointer_t<std::remove_reference_t<T>>;

    template<int default_arg_position = -1, class DefaultArgsTuple = std::tuple<>>
    WrappedT * operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
                   std::vector<std::unique_ptr<StuffBase>> & stuff,
                   DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {

        PB_PRINT("ParameterBuilder handling pointer to wrapped type: {}", demangle<T>());

        //std::cerr << fmt::format("ParameterBuilder type: pointer-to {} default_arg_position = {}", v8toolkit::demangle<T>(), default_arg_position) << std::endl;
        if (i >= info.Length()) {
            return *get_default_parameter<std::remove_const_t<WrappedT> *, default_arg_position>(info, i, stuff, default_args_tuple);

        } else {
            // try to get the object from inside a javascript object, otherwise, fall back to a CastToNative<T> call
            if (auto wrapped_pointer = CastToNative<WrappedT *>()(info.GetIsolate(), info[i++])) {
                return wrapped_pointer;

            } else if constexpr(CastToNative<WrappedT>::callable()) {
                stuff.push_back(Stuff<WrappedT>::stuffer(CastToNative<WrappedT>()(info.GetIsolate(), info[i++])));
                return static_cast<Stuff<WrappedT> &>(*stuff.back()).get();
            } else {
                throw CastException("Tried to send a pointer to a function but the JavaScript object wasn't a wrapped "
                                        "C++ object and a new object couldn't be created from the JavaScript value provided");
            }
        }
    }
};



template<class T>
struct ParameterBuilder<T, std::enable_if_t<!std::is_pointer_v<T> && !std::is_reference_v<T> && !is_wrapped_type_v<T>>> {
    using NoRefT = std::remove_reference_t<T>;


    template<int default_arg_position = -1, class DefaultArgsTuple = std::tuple<>>
    T operator()(const v8::FunctionCallbackInfo<v8::Value> & info, int & i,
               std::vector<std::unique_ptr<StuffBase>> & stuff,
               DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {
{'fraction_non_alphanumeric': 0.1347180574855555, 'fraction_numerical': 0.0035105682732392307, 'mean_word_length': 3.874866310160428, 'pattern_counts': {'":': 0, '<': 222, '<?xml version=': 0, '>': 202, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '745441', 'n_tokens_mistral': 4176, 'n_tokens_neox': 4053, 'n_words': 966}