import ctypes


# K4A_DECLARE_HANDLE(k4a_device_t);
class _handle_k4a_device_t(ctypes.Structure):
    _fields_ = [
        ("_rsvd", ctypes.c_size_t),
    ]

# handling device open/close/start/capture
k4a_device_t = ctypes.POINTER(_handle_k4a_device_t)


# K4A_DECLARE_HANDLE(k4a_capture_t);
class _handle_k4a_capture_t(ctypes.Structure):
    _fields_ = [
        ("_rsvd", ctypes.c_size_t),
    ]

# after capturing image by device.open(), handling captured image
k4a_capture_t = ctypes.POINTER(_handle_k4a_capture_t)


# K4A_DECLARE_HANDLE(k4a_image_t);
class _handle_k4a_image_t(ctypes.Structure):
    _fields_ = [
        ("_rsvd", ctypes.c_size_t),
    ]

# handling image's metadata (exposure, h, w, size, timestamp, ...)
k4a_image_t = ctypes.POINTER(_handle_k4a_image_t)


# K4A_DECLARE_HANDLE(k4a_transformation_t);
class _handle_k4a_transformation_t(ctypes.Structure):
    _fields_ = [
        ("_rsvd", ctypes.c_size_t),
    ]

# format transformation like color_to_depth, depth_to_pts
k4a_transformation_t = ctypes.POINTER(_handle_k4a_transformation_t)

# class k4a_result_t(CtypeIntEnum):
k4a_result_t = ctypes.c_int
K4A_RESULT_SUCCEEDED = 0
K4A_RESULT_FAILED = 1

# class k4a_buffer_result_t(CtypeIntEnum):
k4a_buffer_result_t = ctypes.c_int
K4A_BUFFER_RESULT_SUCCEEDED = 0
K4A_BUFFER_RESULT_FAILED = 1
K4A_BUFFER_RESULT_TOO_SMALL = 2

# class k4a_wait_result_t(CtypeIntEnum):
k4a_wait_result_t = ctypes.c_int
K4A_WAIT_RESULT_SUCCEEDED = 0
K4A_WAIT_RESULT_FAILED = 1
K4A_WAIT_RESULT_TIMEOUT = 2

# Debug message level
# class k4a_log_level_t(CtypeIntEnum):
k4a_log_level_t = ctypes.c_int
K4A_LOG_LEVEL_CRITICAL = 0
K4A_LOG_LEVEL_ERROR = 1
K4A_LOG_LEVEL_WARNING = 2
K4A_LOG_LEVEL_INFO = 3
K4A_LOG_LEVEL_TRACE = 4
K4A_LOG_LEVEL_OFF = 5

# class k4a_depth_mode_t(CtypeIntEnum):
k4a_depth_mode_t = ctypes.c_int
K4A_DEPTH_MODE_OFF = 0
K4A_DEPTH_MODE_NFOV_2X2BINNED = 1
K4A_DEPTH_MODE_NFOV_UNBINNED = 2
K4A_DEPTH_MODE_WFOV_2X2BINNED = 3
K4A_DEPTH_MODE_WFOV_UNBINNED = 4
K4A_DEPTH_MODE_PASSIVE_IR = 5

# class k4a_color_resolution_t(CtypeIntEnum):
k4a_color_resolution_t = ctypes.c_int
K4A_COLOR_RESOLUTION_OFF = 0
K4A_COLOR_RESOLUTION_720P = 1
K4A_COLOR_RESOLUTION_1080P = 2
K4A_COLOR_RESOLUTION_1440P = 3
K4A_COLOR_RESOLUTION_1536P = 4
K4A_COLOR_RESOLUTION_2160P = 5
K4A_COLOR_RESOLUTION_3072P = 6

# class k4a_image_format_t(CtypeIntEnum):
k4a_image_format_t = ctypes.c_int
K4A_IMAGE_FORMAT_COLOR_MJPG = 0
K4A_IMAGE_FORMAT_COLOR_NV12 = 1
K4A_IMAGE_FORMAT_COLOR_YUY2 = 2
K4A_IMAGE_FORMAT_COLOR_BGRA32 = 3
K4A_IMAGE_FORMAT_DEPTH16 = 4
K4A_IMAGE_FORMAT_IR16 = 5
K4A_IMAGE_FORMAT_CUSTOM8 = 6
K4A_IMAGE_FORMAT_CUSTOM16 = 7
K4A_IMAGE_FORMAT_CUSTOM = 8

# depth_image to color interpolation 
# class k4a_transformation_interpolation_type_t(CtypeIntEnum):
k4a_transformation_interpolation_type_t = ctypes.c_int
K4A_TRANSFORMATION_INTERPOLATION_TYPE_NEAREST = 0
K4A_TRANSFORMATION_INTERPOLATION_TYPE_LINEAR = 1

# class k4a_fps_t(CtypeIntEnum):
k4a_fps_t = ctypes.c_int
K4A_FRAMES_PER_SECOND_5 = 0
K4A_FRAMES_PER_SECOND_15 = 1
K4A_FRAMES_PER_SECOND_30 = 2

# class k4a_color_control_command_t(CtypeIntEnum):
k4a_color_control_command_t = ctypes.c_int
K4A_COLOR_CONTROL_EXPOSURE_TIME_ABSOLUTE = 0
K4A_COLOR_CONTROL_AUTO_EXPOSURE_PRIORITY = 1
K4A_COLOR_CONTROL_BRIGHTNESS = 2
K4A_COLOR_CONTROL_CONTRAST = 3
K4A_COLOR_CONTROL_SATURATION = 4
K4A_COLOR_CONTROL_SHARPNESS = 5
K4A_COLOR_CONTROL_WHITEBALANCE = 6
K4A_COLOR_CONTROL_BACKLIGHT_COMPENSATION = 7
K4A_COLOR_CONTROL_GAIN = 8
K4A_COLOR_CONTROL_POWERLINE_FREQUENCY = 9

# class k4a_color_control_mode_t(CtypeIntEnum):
k4a_color_control_mode_t = ctypes.c_int
K4A_COLOR_CONTROL_MODE_AUTO = 0
K4A_COLOR_CONTROL_MODE_MANUAL = 1

# Using When record with two or more devices
# class k4a_wired_sync_mode_t(CtypeIntEnum):
k4a_wired_sync_mode_t = ctypes.c_int
K4A_WIRED_SYNC_MODE_STANDALONE = 0
K4A_WIRED_SYNC_MODE_MASTER = 1
K4A_WIRED_SYNC_MODE_SUBORDINATE = 2

# class k4a_calibration_type_t(CtypeIntEnum):
k4a_calibration_type_t = ctypes.c_int
K4A_CALIBRATION_TYPE_UNKNOWN = -1
K4A_CALIBRATION_TYPE_DEPTH = 0
K4A_CALIBRATION_TYPE_COLOR = 1
K4A_CALIBRATION_TYPE_GYRO = 2
K4A_CALIBRATION_TYPE_ACCEL = 3
K4A_CALIBRATION_TYPE_NUM = 4

# class k4a_calibration_model_type_t(CtypeIntEnum):
k4a_calibration_model_type_t = ctypes.c_int
K4A_CALIBRATION_LENS_DISTORTION_MODEL_UNKNOWN = 0
K4A_CALIBRATION_LENS_DISTORTION_MODEL_THETA = 1
K4A_CALIBRATION_LENS_DISTORTION_MODEL_POLYNOMIAL_3K = 2
K4A_CALIBRATION_LENS_DISTORTION_MODEL_RATIONAL_6KT = 3
K4A_CALIBRATION_LENS_DISTORTION_MODEL_BROWN_CONRADY = 4

# class k4a_firmware_build_t(CtypeIntEnum):
k4a_firmware_build_t = ctypes.c_int
K4A_FIRMWARE_BUILD_RELEASE = 0
K4A_FIRMWARE_BUILD_DEBUG = 1

# class k4a_firmware_signature_t(CtypeIntEnum):
k4a_firmware_signature_t = ctypes.c_int
K4A_FIRMWARE_SIGNATURE_MSFT = 0
K4A_FIRMWARE_SIGNATURE_TEST = 1
K4A_FIRMWARE_SIGNATURE_UNSIGNED = 2


# define K4A_SUCCEEDED(_result_) (_result_ == K4A_RESULT_SUCCEEDED)
def K4A_SUCCEEDED(result):
    return result == K4A_RESULT_SUCCEEDED


# define K4A_FAILED(_result_) (!K4A_SUCCEEDED(_result_))
def K4A_FAILED(result):
    return not K4A_SUCCEEDED(result)

# Callback Function
"""
typedef void(k4a_logging_message_cb_t)(
    void *context,
    k4a_log_level_t level,
    const char *file,
    const int line,
    const char *message
);

typedef void(k4a_memory_destroy_cb_t)(void *buffer, void *context);

typedef uint8_t *(k4a_memory_allocate_cb_t)(int size, void **context);
"""


class _k4a_device_configuration_t(ctypes.Structure):
    _fields_ = [
        ("color_format", ctypes.c_int),
        ("color_resolution", ctypes.c_int),
        ("depth_mode", ctypes.c_int),
        ("camera_fps", ctypes.c_int),
        ("synchronized_images_only", ctypes.c_bool),
        ("depth_delay_off_color_usec", ctypes.c_int32),
        ("wired_sync_mode", ctypes.c_int),
        ("subordinate_delay_off_master_usec", ctypes.c_uint32),
        ("disable_streaming_indicator", ctypes.c_bool),
    ]


k4a_device_configuration_t = _k4a_device_configuration_t


class _k4a_calibration_extrinsics_t(ctypes.Structure):
    _fields_ = [
        ("rotation", ctypes.c_float * 9),
        ("translation", ctypes.c_float * 3),
    ]

# rotation => 3x3(9) rotation, translation => Vector(x, y, z)
k4a_calibration_extrinsics_t = _k4a_calibration_extrinsics_t


class _param(ctypes.Structure):
    _fields_ = [
        ("cx", ctypes.c_float),
        ("cy", ctypes.c_float),
        ("fx", ctypes.c_float),
        ("fy", ctypes.c_float),
        ("k1", ctypes.c_float),
        ("k2", ctypes.c_float),
        ("k3", ctypes.c_float),
        ("k4", ctypes.c_float),
        ("k5", ctypes.c_float),
        ("k6", ctypes.c_float),
        ("codx", ctypes.c_float),
        ("cody", ctypes.c_float),
        ("p2", ctypes.c_float),
        ("p1", ctypes.c_float),
        ("metric_radius", ctypes.c_float),
    ]


class k4a_calibration_intrinsic_parameters_t(ctypes.Union):
    _fields_ = [
        ("param", _param),
        ("v", ctypes.c_float * 15),
    ]


class _k4a_calibration_intrinsics_t(ctypes.Structure):
    _fields_ = [
        ("type", ctypes.c_int),
        ("parameter_count", ctypes.c_uint),
        ("parameters", k4a_calibration_intrinsic_parameters_t),
    ]


#v => Parameter counts (_params)
k4a_calibration_intrinsics_t = _k4a_calibration_intrinsics_t


class _k4a_calibration_camera_t(ctypes.Structure):
    _fields_ = [
        ("extrinsics", k4a_calibration_extrinsics_t),
        ("intrinsics", k4a_calibration_intrinsics_t),
        ("resolution_width", ctypes.c_int),
        ("resolution_height", ctypes.c_int),
        ("metric_radius", ctypes.c_float),
    ]


k4a_calibration_camera_t = _k4a_calibration_camera_t

# k4a_calibration_extrinsics_t[4][4] => 3D 변환을 가능하게 해줌.
class _k4a_calibration_t(ctypes.Structure):
    _fields_ = [
        ("depth_camera_calibration", k4a_calibration_camera_t),
        ("color_camera_calibration", k4a_calibration_camera_t),
        (
            "extrinsics",
            (k4a_calibration_extrinsics_t * K4A_CALIBRATION_TYPE_NUM)
            * K4A_CALIBRATION_TYPE_NUM,
        ),
        ("depth_mode", ctypes.c_int),
        ("color_resolution", ctypes.c_int),
    ]


k4a_calibration_t = _k4a_calibration_t


class _k4a_version_t(ctypes.Structure):
    _fields_ = [
        ("major", ctypes.c_uint32),
        ("minor", ctypes.c_uint32),
        ("iteration", ctypes.c_uint32),
    ]


k4a_version_t = _k4a_version_t


class _k4a_hardware_version_t(ctypes.Structure):
    _fields_ = [
        ("rgb", k4a_version_t),
        ("depth", k4a_version_t),
        ("audio", k4a_version_t),
        ("depth_sensor", k4a_version_t),
        ("firmware_build", ctypes.c_int),
        ("firmware_signature", ctypes.c_int),
    ]


k4a_hardware_version_t = _k4a_hardware_version_t


class _xy(ctypes.Structure):
    _fields_ = [
        ("x", ctypes.c_float),
        ("y", ctypes.c_float),
    ]

    def __iter__(self):
        return {"x": self.x, "y": self.y}

    def __str__(self):
        return str(self.__iter__())

# Two dimensional floating point vector
class k4a_float2_t(ctypes.Union):
    _fields_ = [("xy", _xy), ("v", ctypes.c_float * 2)]

    def __init__(self, v=(0, 0)):
        super().__init__()
        self.xy = _xy(v[0], v[1])

    def __iter__(self):
        xy = self.xy.__iter__()
        xy.update({"v": [v for v in self.v]})
        return xy

    def __str__(self):
        return self.xy.__str__()


class _xyz(ctypes.Structure):
    _fields_ = [
        ("x", ctypes.c_float),
        ("y", ctypes.c_float),
        ("z", ctypes.c_float),
    ]

    def __iter__(self):
        return {"x": self.x, "y": self.y, "z": self.z}

    def __str__(self):
        return str(self.__iter__())

# Three dimensional floating point vector
class k4a_float3_t(ctypes.Union):
    _fields_ = [("xyz", _xyz), ("v", ctypes.c_float * 3)]

    def __init__(self, v=(0, 0, 0)):
        super().__init__()
        self.xyz = _xyz(v[0], v[1], v[2])

    def __iter__(self):
        xyz = self.xyz.__iter__()
        xyz.update({"v": [v for v in self.v]})
        return xyz

    def __str__(self):
        return self.xyz.__str__()


class k4a_imu_sample_t(ctypes.Structure):
    _fields_ = [
        ("temperature", ctypes.c_float),
        ("acc_sample", k4a_float3_t),
        ("acc_timestamp_usec", ctypes.c_uint64),
        ("gyro_sample", k4a_float3_t),
        ("gyro_timestamp_usec", ctypes.c_uint64),
    ]


IMU_SAMPLE_SIZE = ctypes.sizeof(k4a_imu_sample_t)


K4A_DEVICE_DEFAULT = 0
K4A_WAIT_INFINITE = -1

color_command_dict = {
    "K4A_COLOR_CONTROL_EXPOSURE_TIME_ABSOLUTE": 0,
    "K4A_COLOR_CONTROL_AUTO_EXPOSURE_PRIORITY": 1,
    "K4A_COLOR_CONTROL_BRIGHTNESS": 2,
    "K4A_COLOR_CONTROL_CONTRAST": 3,
    "K4A_COLOR_CONTROL_SATURATION": 4,
    "K4A_COLOR_CONTROL_SHARPNESS": 5,
    "K4A_COLOR_CONTROL_WHITEBALANCE": 6,
    "K4A_COLOR_CONTROL_BACKLIGHT_COMPENSATION": 7,
    "K4A_COLOR_CONTROL_GAIN": 8,
    "K4A_COLOR_CONTROL_POWERLINE_FREQUENCY": 9
}