#pragma once

#include <stdint.h>
#include <stddef.h>

#include <bnb/export.h>
#include <bnb/common_types.h>

#ifdef __cplusplus
extern "C"
{
#endif


    typedef struct frame_data_holder frame_data_holder_t;
    typedef struct recognizer_holder recognizer_holder_t;

    typedef struct
    {
        uint64_t frx;
        uint64_t ruler;
        uint64_t eye_state;
        uint64_t open_mouth;
        uint64_t smile;
        uint64_t raised_brows;
        uint64_t shifted_brows;
        uint64_t occlusion_mask;
        uint64_t action_units;
        uint64_t background_segm;
        uint64_t body_segm;
        uint64_t face_segm;
        uint64_t hair_segm;
        uint64_t neck_segm;
        uint64_t skin_segm;
        uint64_t lips_segm;
        uint64_t brows_segm;
        uint64_t eyes_segm;
        uint64_t hand_skeleton;
        uint64_t hand_gestures;
        uint64_t lips_shine;
        uint64_t eyes_correction;
        uint64_t lips_correction;
        uint64_t action_units_antijitter;
        uint64_t face_skin_segm;
        uint64_t pupillary_distance;
        uint64_t frown_detector;
        uint64_t teeth_tone;
        uint64_t lips_glare;
    } bnb_recognizer_features_id_t;

    typedef enum
    {
        BNB_BACKGROUND = 0,
        BNB_BODY,
        BNB_FACE,
        BNB_HAIR,
        BNB_NECK,
        BNB_SKIN,
        BNB_LIPS,
        BNB_BROW_LEFT,
        BNB_BROW_RIGHT,
        BNB_EYE_PUPIL_LEFT,
        BNB_EYE_PUPIL_RIGHT,
        BNB_EYE_SCLERA_LEFT,
        BNB_EYE_SCLERA_RIGHT,
        BNB_EYE_IRIS_LEFT,
        BNB_EYE_IRIS_RIGHT,
        BNB_FACE_SKIN
    } bnb_segm_type_t;

    typedef enum
    {
        NONE = 0,
        LIKE,
        OK,
        PALM,
        ROCK,
        V
    } bnb_hand_gesture_t;

    typedef struct
    {
        unsigned char* y_plane;
        unsigned char* uv_plane;
        bnb_image_format_t format;
    } bnb_yuv_422_image_t;

    typedef struct
    {
        unsigned char* data;
        bnb_image_format_t format;
        bnb_pixel_format_t pixel_format;
    } bnb_bpc8_image_t;

    typedef struct
    {
        float model_view_m[16];
        float projection_m[16];
    } bnb_frx_camera_pos_t;

    typedef struct
    {
        int hasFaceRectangle;
        float leftTop_x;
        float leftTop_y;
        float rightTop_x;
        float rightTop_y;
        float rightBottom_x;
        float rightBottom_y;
        float leftBottom_x;
        float leftBottom_y;
    } bnb_frx_face_rct_t;

    typedef struct
    {
        bnb_frx_face_rct_t rectangle;
        bnb_frx_camera_pos_t camera_position;

        int landmarks_count;
        const float* landmarks;

        int latents_count;
        const float* latents;

        int vertices_count;
        const float* vertices;
    } bnb_face_data_t;

    typedef struct
    {
        int vertices_count;
        const float* vertices;
    } bnb_static_face_data_t;

    typedef struct
    {
        bnb_hand_gesture_t gesture;
        int vertices_count;
        const float* vertices;

        float transform[16];

    } bnb_hand_data_t;

    // order of values in au array:
    typedef enum
    {
        BNB_AU_BrowDownLeft, // 0
        BNB_AU_BrowDownRight,
        BNB_AU_BrowInnerUp,
        BNB_AU_BrowOuterUpLeft,
        BNB_AU_BrowOuterUpRight,
        BNB_AU_CheekPuff,
        BNB_AU_CheekSquintLeft,
        BNB_AU_CheekSquintRight,
        BNB_AU_JawForward,
        BNB_AU_JawLeft, // 9
        BNB_AU_JawRight,
        BNB_AU_JawOpen,
        BNB_AU_MouthClose,
        BNB_AU_MouthFunnel,
        BNB_AU_MouthPucker,
        BNB_AU_MouthLeft,
        BNB_AU_MouthRight,
        BNB_AU_MouthSmileLeft,
        BNB_AU_MouthSmileRight,
        BNB_AU_MouthDimpleLeft, // 19
        BNB_AU_MouthDimpleRight,
        BNB_AU_MouthRollUpper,
        BNB_AU_MouthShrugUpper,
        BNB_AU_MouthShrugLower,
        BNB_AU_MouthRollLower,
        BNB_AU_MouthFrownLeft,
        BNB_AU_MouthFrownRight,
        BNB_AU_MouthUpperUpLeft,
        BNB_AU_MouthUpperUpRight,
        BNB_AU_MouthLowerDownLeft, // 29
        BNB_AU_MouthLowerDownRight,
        BNB_AU_NoseSneerLeft,
        BNB_AU_NoseSneerRight,
        BNB_AU_MouthPressLeft,
        BNB_AU_MouthPressRight,
        BNB_AU_MouthStretchLeft,
        BNB_AU_MouthStretchRight,
        BNB_AU_EyeBlinkLeft,
        BNB_AU_EyeBlinkRight,
        BNB_AU_EyeWideLeft, // 39
        BNB_AU_EyeWideRight,
        BNB_AU_EyeSquintLeft,
        BNB_AU_EyeSquintRight,
        BNB_AU_EyeLookDownLeft,
        BNB_AU_EyeLookInLeft,
        BNB_AU_EyeLookOutLeft,
        BNB_AU_EyeLookUpLeft,
        BNB_AU_EyeLookDownRight,
        BNB_AU_EyeLookInRight,
        BNB_AU_EyeLookOutRight,
        BNB_AU_EyeLookUpRight, // 50
        BNB_AU_total_au_count = 51
    } bnb_action_units_mapping_t;

    typedef struct
    {
        float rot_x, rot_y, rot_z;
        const float* units;
    } bnb_action_units_t;

    typedef enum
    {
        bnb_fit_width,  //!< Always fit to width, rect_scale = w_t / w_f
        bnb_fit_height, //!< Always fit to height, rect_scale = h_t / h_f
        bnb_fit_inside, //!< Fit all source inside target = fit to min rect_scale of width and height modes
        bnb_fit_outside //!< Fit to fill all target = fit to max rect_scale of width and height modes
    } bnb_rect_fit_mode_t;

    typedef struct
    {
        int x;
        int y;
        int w;
        int h;
    } bnb_pixel_rect_t;

    typedef struct
    {
        float mv[16];
        float p[16];

        float v[16];
    } bnb_face_transform_t;

    typedef struct
    {
        int width;
        int height;
        int channel;
        int inverse;
        float common_transform[9];
        float gl_transform[16];
    } bnb_segm_mask_t;

    typedef struct
    {
        bnb_segm_mask_t common_data;
        const void* data;
    } bnb_segm_mask_data_t;

    typedef struct
    {
        int width;
        int height;
        int channel;
        int inverse;
        float v_min;
        float v_max;
        float common_transform[9];
        float gl_transform[16];
    } bnb_lips_shine_mask_t;

    typedef struct
    {
        bnb_lips_shine_mask_t common_data;
        const void* data;
    } bnb_lips_shine_mask_data_t;

    /*
     * Error processing
     */
    /**
     * Opaque structure to represent an error. Always the last argument of the call.
     * You may pass a `NULL`. You must call `bnb_error_destroy` if the error was returned.
     */
    typedef struct bnb_error bnb_error;

    BNB_EXPORT void bnb_error_destroy(bnb_error*);
    BNB_EXPORT const char* bnb_error_get_message(const bnb_error*);
    BNB_EXPORT const char* bnb_error_get_type(const bnb_error*);

    /*
     * Worker or Main Thread
     */

    BNB_EXPORT void bnb_set_logging_enabled(bool is_enabled, bnb_error**);

    BNB_EXPORT void bnb_recognizer_env_init(const char* client_token, bnb_error**);
    BNB_EXPORT void bnb_recognizer_env_release(bnb_error**);

    BNB_EXPORT recognizer_holder_t* bnb_recognizer_init(const char* resource_folder, bool is_async, bnb_error**);
    BNB_EXPORT void bnb_recognizer_release(recognizer_holder_t* rec, bnb_error**);

    BNB_EXPORT bnb_recognizer_features_id_t bnb_recognizer_get_features_id();
    BNB_EXPORT void bnb_recognizer_set_features(recognizer_holder_t* rec, const uint64_t* features, uint32_t fatures_count, bnb_error**);
    BNB_EXPORT void bnb_recognizer_insert_feature(recognizer_holder_t* rec, uint64_t features, bnb_error**);
    BNB_EXPORT void bnb_recognizer_remove_feature(recognizer_holder_t* rec, uint64_t features, bnb_error**);
    BNB_EXPORT void bnb_recognizer_set_max_faces(recognizer_holder_t* rec, int count, bnb_error**);
    BNB_EXPORT void bnb_recognizer_set_offline_mode(recognizer_holder_t* rec, bool on, bnb_error**);
    BNB_EXPORT void bnb_recognizer_surface_created(recognizer_holder_t* rec, int width, int height, bnb_error**);
    BNB_EXPORT void bnb_recognizer_surface_destroyed(recognizer_holder_t* rec, bnb_error**);

    /*
     * Worker Thread
     */

    BNB_EXPORT frame_data_holder_t* bnb_frame_data_init(bnb_error**);
    BNB_EXPORT void bnb_frame_data_release(frame_data_holder_t* fd, bnb_error**);

    BNB_EXPORT void bnb_frame_data_set_yuv_img(frame_data_holder_t* frame_data, bnb_yuv_422_image_t* image, bnb_error**);
    BNB_EXPORT void bnb_frame_data_set_bpc8_img(frame_data_holder_t* frame_data, bnb_bpc8_image_t* image, bnb_error**);

    BNB_EXPORT bool bnb_recognizer_process_frame_data(recognizer_holder_t* rec, frame_data_holder_t* fd, bnb_error**);
    BNB_EXPORT void bnb_recognizer_push_frame_data(recognizer_holder_t* rec, frame_data_holder_t* fd, bnb_error** error);
    BNB_EXPORT bool bnb_recognizer_pop_frame_data(recognizer_holder_t* rec, frame_data_holder_t* fd, bnb_error** error);
    BNB_EXPORT void bnb_recognizer_clear(recognizer_holder_t* rec, bnb_error**);

    BNB_EXPORT bool bnb_frame_data_has_frx_result(const frame_data_holder_t* fd, bnb_error**);

    BNB_EXPORT const bnb_face_transform_t bnb_frame_data_get_face_transform(const frame_data_holder_t* fd, int face_index, int width, int height, bnb_rect_fit_mode_t mode, bnb_error**);
    BNB_EXPORT void bnb_fit_rects_aspect_ratio(bnb_pixel_rect_t* source_rect, bnb_pixel_rect_t* target_rect, bnb_rect_fit_mode_t mode, bnb_error**);

    BNB_EXPORT int bnb_frame_data_get_face_count(const frame_data_holder_t* fd, bnb_error**);
    BNB_EXPORT const bnb_face_data_t bnb_frame_data_get_face(const frame_data_holder_t* fd, int face_index, bnb_error**);
    BNB_EXPORT const bnb_static_face_data_t get_static_pos_data();
    BNB_EXPORT const void get_extended_static_pos_data(float* verts, int* inds);
    BNB_EXPORT const int get_extended_static_pos_vertices_size();
    BNB_EXPORT const int get_extended_static_pos_indices_size();

    BNB_EXPORT int get_static_uv_size();
    BNB_EXPORT void get_static_uv(float* data);

    BNB_EXPORT bool bnb_frame_data_has_action_units(const frame_data_holder_t* fd, bnb_error**);
    BNB_EXPORT const bnb_action_units_t bnb_frame_data_get_action_units(const frame_data_holder_t* fd, int face_index, bnb_error**);

    BNB_EXPORT int bnb_frame_data_get_tex_coords_size(const frame_data_holder_t* fd, bnb_error**);
    BNB_EXPORT const float* bnb_frame_data_get_tex_coords(const frame_data_holder_t* fd, bnb_error**);

    BNB_EXPORT int bnb_frame_data_get_triangles_size(const frame_data_holder_t* fd, bnb_error**);
    BNB_EXPORT const int* bnb_frame_data_get_triangles(const frame_data_holder_t* fd, bnb_error**);

    BNB_EXPORT int bnb_frame_data_get_wire_indicies_size(const frame_data_holder_t* fd, bnb_error**);
    BNB_EXPORT const int* bnb_frame_data_get_wire_indicies(const frame_data_holder_t* fd, bnb_error**);

    BNB_EXPORT bool bnb_frame_data_has_pupils(const frame_data_holder_t* fd, bnb_error**);
    BNB_EXPORT void bnb_frame_data_extract_pupils(const frame_data_holder_t* data, float* pupils, bnb_error**);

    BNB_EXPORT bool bnb_frame_data_is_eye_open(const frame_data_holder_t* data, bool is_left, bnb_error**);

    BNB_EXPORT float bnb_frame_data_get_ruller(const frame_data_holder_t* data, bnb_error**);

    BNB_EXPORT bool bnb_frame_data_is_light_from_right(const frame_data_holder_t* data, bnb_error**);

    BNB_EXPORT bool bnb_frame_data_get_is_mouth_open(const frame_data_holder_t* data, bnb_error**);
    BNB_EXPORT bool bnb_frame_data_get_is_smile(const frame_data_holder_t* data, bnb_error**);
    BNB_EXPORT bool bnb_frame_data_get_right_eye_is_open(const frame_data_holder_t* data, bnb_error**);
    BNB_EXPORT bool bnb_frame_data_get_left_eye_is_open(const frame_data_holder_t* data, bnb_error**);
    BNB_EXPORT bool bnb_frame_data_get_is_brows_raised(const frame_data_holder_t* data, bnb_error**);
    BNB_EXPORT bool bnb_frame_data_get_is_brows_shifted(const frame_data_holder_t* data, bnb_error**);
    BNB_EXPORT bnb_hand_data_t bnb_frame_data_get_hand(const frame_data_holder_t* fd, int width, int height, bnb_rect_fit_mode_t mode, bool flip_y, bnb_error** error);
    BNB_EXPORT bnb_segm_mask_data_t bnb_frame_data_get_segmentation_mask_data(const frame_data_holder_t* fd, bnb_segm_type_t mask, bnb_error** error);
    BNB_EXPORT bnb_hand_gesture_t bnb_frame_data_get_gesture(const frame_data_holder_t* fd, bnb_error** error);
    BNB_EXPORT bnb_lips_shine_mask_data_t bnb_frame_data_get_lips_shine_mask(const frame_data_holder_t* fd, bnb_error** error);
    BNB_EXPORT float bnb_frame_data_get_pupillary_distance(const frame_data_holder_t* fd, bnb_error** error);


#ifdef __cplusplus
} // extern "C"
#endif
