/**
 * Copyright 2019,2020,2021 Sony Corporation
 */
 
#ifndef XR_API_DEFS_H_
#define XR_API_DEFS_H_

#include "xr_math.h"

// = Standard Library =============================
#include <stdint.h>
#include <string>
#include <wchar.h>

// XR_PTR_SIZE (in bytes)
#if (defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__))
    #define XR_PTR_SIZE 8
#else
    #define XR_PTR_SIZE 4
#endif

#if !defined(XR_DEFINE_HANDLE)
    #if (XR_PTR_SIZE == 8)
        #define XR_DEFINE_HANDLE(object) typedef struct object##_T* object;
    #else
        #define XR_DEFINE_HANDLE(object) typedef uint64_t object;
    #endif
#endif

static const int SRD_XR_ENUM_INVALID = 0x7FFFFFFF;

XR_DEFINE_HANDLE(SrdXrSpace);

typedef int64_t SrdXrTime;
typedef uint64_t SrdXrFlags64;
typedef SrdXrFlags64 SrdXrCompositionLayerFlags;

static const SrdXrCompositionLayerFlags SRD_XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT = 0x00000001;
static const SrdXrCompositionLayerFlags SRD_XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT = 0x00000002;
static const SrdXrCompositionLayerFlags SRD_XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT = 0x00000004;

enum class SrdXrResult {
    SUCCESS = 0,
    EVENT_UNAVAILABLE = 4,
    ERROR_VALIDATION_FAILURE = -1,
    ERROR_RUNTIME_FAILURE = -2,
    ERROR_OUT_OF_MEMORY = -3,
    ERROR_API_VERSION_UNSUPPORTED = -4,
    ERROR_INITIALIZATION_FAILED = -6,
    ERROR_FUNCTION_UNSUPPORTED = -7,
    ERROR_FEATURE_UNSUPPORTED = -8,
    ERROR_LIMIT_REACHED = -10,
    ERROR_SIZE_INSUFFICIENT = -11,
    ERROR_HANDLE_INVALID = -12,
    ERROR_INSTANCE_LOST = -13,
    ERROR_SESSION_RUNNING = -14,
    ERROR_SESSION_NOT_RUNNING = -16,
    ERROR_SESSION_LOST = -17,
    ERROR_SYSTEM_INVALID = -18,
    ERROR_SESSION_NOT_READY = -28,
    ERROR_SESSION_NOT_STOPPING = -29,
    ERROR_FILE_ACCESS_ERROR = -32,
    ERROR_FILE_CONTENTS_INVALID = -33,
    ERROR_FORM_FACTOR_UNSUPPORTED = -34,
    ERROR_FORM_FACTOR_UNAVAILABLE = -35,
    ERROR_CALL_ORDER_INVALID = -37,
    ERROR_GRAPHICS_DEVICE_INVALID = -38,
    ERROR_POSE_INVALID = -39,
    ERROR_INDEX_OUT_OF_RANGE = -40,
    ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED = -41,

    ERROR_RUNTIME_NOT_FOUND = -1001,

    ERROR_DEVICE_NOT_FOUND = -1011,
    ERROR_DISPLAY_NOT_FOUND = -1012,
    ERROR_USB_NOT_CONNECTED = -1013,
    ERROR_USB_3_NOT_CONNECTED = -1014,
};

enum class SrdXrPlatformId {
    SRD = 0,
};

enum class SrdXrViewConfigurationType {
    PRIMARY_MONO = 1,
    PRIMARY_STEREO = 2,
};

enum class SrdXrSessionState {
    UNKNOWN = 0,
    IDLE = 1,
    READY = 2,
    SYNCHRONIZED = 3,
    VISIBLE = 4,
    FOCUSED = 5,
    STOPPING = 6,
    LOSS_PENDING = 7,
    EXITING = 8,
};

enum class SrdXrCoordinateSystem {
    RIGHT_Y_UP_Z_BACK = 0,
    RIGHT_Y_UP_Z_FORWARD = 1,
    LEFT_Y_UP_Z_FORWARD = 2,
    LEFT_Z_UP_X_FORWARD = 3,
};

enum class SrdXrCoordinateUnit {
    METER = 0,
    CENTIMETER = 1,
    MILLIMETER = 2,
};

enum class SrdXrGraphicsAPI {
    OpenGL = 0,
    DirectX_11 = 1,
    DirectX_12 = 2,
    Metal = 3,
    Vulkan = 4,
};

enum class SrdXrPerformanceMode {
    NOMAL = 0,
    LIGHTWEIGHT = 1,
};

struct SrdXrRect {
    long left;

    long top;

    long right;

    long bottom;

    SrdXrRect()
        : left(0)
        , top(0)
        , right(0)
        , bottom(0) {}

    SrdXrRect(long l, long t, long r, long b)
        : left(l)
        , top(t)
        , right(r)
        , bottom(b) {}

    SrdXrRect(const SrdXrRect& rect)
        : left(rect.left)
        , top(rect.top)
        , right(rect.right)
        , bottom(rect.bottom) {}
};

const uint32_t kDeviceSerialNumberMaxSize = 256;
struct SrdXrDeviceInfo {
    uint32_t device_index;

    wchar_t device_serial_number[kDeviceSerialNumberMaxSize];

    SrdXrRect target_monitor_rectangle;

    SrdXrRect primary_monitor_rectangle;

    SrdXrDeviceInfo() {};
    SrdXrDeviceInfo(const SrdXrDeviceInfo& obj)
        : device_index(obj.device_index)
        , target_monitor_rectangle(obj.target_monitor_rectangle)
        , primary_monitor_rectangle(obj.primary_monitor_rectangle) {

        wcscpy_s(device_serial_number, _countof(device_serial_number), obj.device_serial_number);
    }
};

enum class SrdXrLogLevels {
    TRACE = 0,
    DEBUG = 1,
    INFO = 2,
    WARN = 3,
    ERR = 4,
    CRITICAL = 5,
    OFF = 6,
};
using SrdXrLogCallback = void(*)(const char*, SrdXrLogLevels);

struct SrdXrSessionCreateInfo {
    SrdXrPlatformId platform_id;

    SrdXrCoordinateSystem coordinate_system;

    SrdXrCoordinateUnit coordinate_unit;

    SrdXrDeviceInfo device;

    SrdXrSessionCreateInfo()
        : platform_id(SrdXrPlatformId::SRD)
        , coordinate_system(SrdXrCoordinateSystem::RIGHT_Y_UP_Z_BACK)
        , coordinate_unit(SrdXrCoordinateUnit::METER)
        , device(SrdXrDeviceInfo()) {}
};

struct SrdXrSessionBeginInfo {
    SrdXrViewConfigurationType primary_view_configuration_type;
};

struct SrdXrCompositionLayerBaseHeader {
    SrdXrCompositionLayerFlags layer_flags;
    SrdXrSpace space;
};

struct SrdXrFrameEndInfo {
    SrdXrTime display_time;

    uint32_t layer_count;

    const SrdXrCompositionLayerBaseHeader* const* layers;
};

struct SrdXrViewLocateInfo {
    SrdXrViewConfigurationType view_configuration_type;

    SrdXrTime display_time;

    SrdXrSpace space;
};

struct SrdXrFovf {
    float angle_left;
    float angle_right;
    float angle_up;
    float angle_down;

    SrdXrFovf()
        : angle_left(0.f), angle_right(0.f), angle_up(0.f), angle_down(0.f) {}

    SrdXrFovf(float in_angle)
        : angle_left(in_angle), angle_right(in_angle), angle_up(in_angle), angle_down(in_angle) {}

    SrdXrFovf(float in_angle_left, float in_angle_right, float in_angle_up, float in_angle_down)
        : angle_left(in_angle_left), angle_right(in_angle_right), angle_up(in_angle_up), angle_down(in_angle_down) {}
};


struct SrdXrFrustumPlanesf {
    float viewLeft;
    float viewRight;
    float viewBottom;
    float viewTop;
    float nearZ;
    float farZ;

    SrdXrFrustumPlanesf(float horizontal_fov, float vertical_fov, float near_clip, float far_clip) :
        viewLeft(near_clip * tanf(horizontal_fov * 0.5f)), viewRight(-near_clip * tanf(horizontal_fov * 0.5f)),
        viewBottom(-near_clip * tanf(vertical_fov * 0.5f)), viewTop(near_clip * tanf(vertical_fov * 0.5f)),
        nearZ(near_clip), farZ(far_clip) {

    }

    SrdXrFrustumPlanesf() : SrdXrFrustumPlanesf(1.47f, 0.70f, 0.15f, 10.f) {
    }

    SrdXrFrustumPlanesf(float in_view_left, float in_view_right, float in_view_bottom, float in_view_top,
                        float in_near_z, float in_far_z ) :
        viewLeft(in_view_left), viewRight(in_view_right), viewBottom(in_view_bottom), viewTop(in_view_top), nearZ(in_near_z), farZ(in_far_z) {
    }

    SrdXrFovf ToFov() {
        return SrdXrFovf(atan2f(fabsf(viewLeft), fabsf(nearZ)), atan2f(fabsf(viewRight), fabsf(nearZ)),
                         atan2f(fabsf(viewTop), fabsf(nearZ)), atan2f(fabsf(viewBottom), fabsf(nearZ)));
    }

};

struct SrdXrPosef {
    SrdXrQuaternionf orientation;

    SrdXrVector3f position;

    SrdXrPosef() {}

    SrdXrPosef(SrdXrQuaternionf in_orientation, SrdXrVector3f in_position)
        : orientation(in_orientation), position(in_position) {}
};

struct SrdXrDisplayResolution {
    uint32_t width;

    uint32_t height;

    uint32_t area;

    SrdXrDisplayResolution()
        : width(0), height(0), area(width * height) {}

    SrdXrDisplayResolution(uint32_t in_width, uint32_t in_height)
        : width(in_width), height(in_height), area(width * height) {}
};

struct SrdXrDisplaySize {
    float width_m;

    float height_m;

    SrdXrDisplaySize()
        : width_m(0.345f), height_m(0.193f) {}

    SrdXrDisplaySize(float in_width_m, float in_height_m)
        : width_m(in_width_m), height_m(in_height_m) {}
};

struct SrdXrView {
    SrdXrPosef pose;

    SrdXrFovf fov;
};

struct SrdXrProjectionMatrixInfo {
    SrdXrGraphicsAPI graphics_api;

    SrdXrCoordinateSystem coordinate_system;

    float near_clip;  // The unit is arbitrary

    float far_clip;  // The unit is arbitrary

    bool reversed_z;

    SrdXrProjectionMatrixInfo()
        : graphics_api(SrdXrGraphicsAPI::OpenGL)
        , coordinate_system(SrdXrCoordinateSystem::RIGHT_Y_UP_Z_FORWARD)
        , near_clip(0.15f)
        , far_clip(10.f)
        , reversed_z(false) {}
};

struct SrdXrProjectionMatrix {
    SrdXrMatrix4x4f projection;

    SrdXrMatrix4x4f left_projection;

    SrdXrMatrix4x4f right_projection;
};

struct SrdXrPlatformSpecificInternalData {};

struct SrdXrPlatformSpecificData {};

struct SrdXrEventDataBuffer {
    SrdXrSessionState state;
};

struct SrdXrDisplayLocateInfo {
    SrdXrTime display_time;
    SrdXrSpace space;
};

struct SrdXrDisplay {
    SrdXrDisplaySize display_size;

    SrdXrDisplayResolution display_resolution;

    SrdXrPosef display_pose;

    SrdXrDisplay()
        : display_size(34.5f, 19.5f), display_resolution(3840, 2160) {}

    SrdXrDisplay(SrdXrDisplaySize in_display_size, SrdXrDisplayResolution in_display_resolution, SrdXrPosef in_display_pose)
        : display_size(in_display_size), display_resolution(in_display_resolution), display_pose(in_display_pose) {}
};

struct SrdXrTexture {
    void* texture;

    uint32_t width;

    uint32_t height;
};

struct SrdXrViewpointCompositorParams {
    float param_1;

    float param_2;

    float param_3;

    SrdXrVector3f scale_factor;

    SrdXrViewpointCompositorParams()
        : param_1(0.f)
        , param_2(0.f)
        , param_3(0.f)
    {}
};

struct SrdXrCameraSettings {
    float gain;

    bool auto_exposure_enabled;

    SrdXrCameraSettings()
        : gain(1.f)
        , auto_exposure_enabled(true)
    {}
};

struct SrdXrCrosstalkCorrectionSettings {
    bool compensation_enabled;

    SrdXrCrosstalkCorrectionSettings(bool in_compensation_enabled)
        : compensation_enabled(in_compensation_enabled)
    {}

    SrdXrCrosstalkCorrectionSettings()
        : compensation_enabled(true)
    {}
};

enum class SrdXrCrosstalkCorrectionLevel {
    GRADATION_CORRECTION_MEDIUM = 0,
    GRADATION_CORRECTION_ALL = 1,
    GRADATION_CORRECTION_HIGH_PRECISE = 2,
};

struct SrdXrCrosstalkCorrectionSettings_v1_1_0 {
    bool compensation_enabled;
    SrdXrCrosstalkCorrectionLevel correction_level;

    SrdXrCrosstalkCorrectionSettings_v1_1_0(SrdXrCrosstalkCorrectionLevel in_correction_level)
        : correction_level(in_correction_level)
    {}

    SrdXrCrosstalkCorrectionSettings_v1_1_0()
        : correction_level(SrdXrCrosstalkCorrectionLevel::GRADATION_CORRECTION_MEDIUM)
    {}
};


enum SrdXrDeviceConnectionState {
    NOT_CONNECTED = 0,
    CONNECTED = 1,
};

enum SrdXrDevicePowerState {
    POWER_OFF = 0,
    POWER_ON = 1,
};

struct SrdXrDeviceState {
    SrdXrDeviceConnectionState connection_state;

    SrdXrDevicePowerState power_state;

    SrdXrDeviceState()
        : connection_state(SrdXrDeviceConnectionState::NOT_CONNECTED)
        , power_state(SrdXrDevicePowerState::POWER_OFF) {}
};

enum class SrdXrCrosstalkCorrectionMode {
    DISABLED = 0,
    DEPENDS_ON_APPLICATION = 1,
    GRADATION_CORRECTION_MEDIUM = 2,
    GRADATION_CORRECTION_ALL = 3,
    GRADATION_CORRECTION_HIGH_PRECISE = 4,
};

struct SrdXrCrosstalkCorrectionSystemSettings {
    SrdXrCrosstalkCorrectionMode crosstalk_compensation_mode;

    float gamma;
    float alpha;
    float limit_l;
    float limit_h;

    SrdXrCrosstalkCorrectionSystemSettings(SrdXrCrosstalkCorrectionMode in_crosstalk_compensation_mode, float in_gamma, float in_alpha, float in_limit_l, float in_limit_h)
        : crosstalk_compensation_mode(in_crosstalk_compensation_mode)
        , gamma(in_gamma)
        , alpha(in_alpha)
        , limit_l(in_limit_l)
        , limit_h(in_limit_h)
    {}

    SrdXrCrosstalkCorrectionSystemSettings()
        : crosstalk_compensation_mode(SrdXrCrosstalkCorrectionMode::DEPENDS_ON_APPLICATION)
        , gamma(0.f)
        , alpha(0.f)
        , limit_l(0.f)
        , limit_h(0.f)
    {}
};

struct SrdXrColorManagementSettings {
    bool is_input_texture_gamma_corrected;

    float gamma;

    SrdXrColorManagementSettings(bool in_is_input_texture_gamma_corrected, float in_gamma)
        : is_input_texture_gamma_corrected(in_is_input_texture_gamma_corrected)
        , gamma(in_gamma)
    {}

    SrdXrColorManagementSettings()
        : is_input_texture_gamma_corrected(false)
        , gamma(1.f)
    {}
};

#endif // XR_API_DEFS_H_
