#include "Mesh.h"
#include <Python.h>
#include <swap.h>

#define MAX(x, y) (((x) > (y)) ? (x) : (y))

enum
{
    kVertexFormatFloat,
    kVertexFormatFloat16,
    kVertexFormatUNorm8,
    kVertexFormatSNorm8,
    kVertexFormatUNorm16,
    kVertexFormatSNorm16,
    kVertexFormatUInt8,
    kVertexFormatSInt8,
    kVertexFormatUInt16,
    kVertexFormatSInt16,
    kVertexFormatUInt32,
    kVertexFormatSInt32
};

PyObject *unpack_vertexdata(PyObject *self, PyObject *args)
{
    // define vars
    int componentByteSize;
    uint32_t m_VertexCount;
    uint8_t swap;
    // char format;
    uint8_t *vertexData; // m_VertexData.m_DataSize
    uint32_t m_StreamOffset;
    uint32_t m_StreamStride;
    uint32_t m_ChannelOffset;
    uint32_t m_ChannelDimension;
    Py_ssize_t vertexDataSize;

    if (!PyArg_ParseTuple(args, "y#iIIIIIb", &vertexData, &vertexDataSize, &componentByteSize, &m_VertexCount, &m_StreamOffset, &m_StreamStride, &m_ChannelOffset, &m_ChannelDimension, &swap))
        return NULL;

    Py_ssize_t componentBytesLength = m_VertexCount * m_ChannelDimension * componentByteSize;
    uint8_t *componentBytes = (uint8_t *)PyMem_Malloc(componentBytesLength + 1);
    componentBytes[componentBytesLength] = 0;

    // check if max values are ok
    uint32_t maxVertexDataAccess = (m_VertexCount - 1) * m_StreamStride + m_ChannelOffset + m_StreamOffset + componentByteSize * (m_ChannelDimension - 1) + componentByteSize;
    if (maxVertexDataAccess > vertexDataSize)
    {
        printf("maxVertexDataAccess: %d, vertexDataSize: %d\n", maxVertexDataAccess, vertexDataSize);
        PyErr_SetString(PyExc_ValueError, "Vertex data access out of bounds");
        return NULL;
    }

    for (uint32_t v = 0; v < m_VertexCount; v++)
    {
        uint32_t vertexOffset = m_StreamOffset + m_ChannelOffset + m_StreamStride * v;
        for (uint32_t d = 0; d < m_ChannelDimension; d++)
        {
            uint32_t vertexDataOffset = vertexOffset + componentByteSize * d;
            uint32_t componentOffset = componentByteSize * (v * m_ChannelDimension + d);
            memcpy(componentBytes + componentOffset, vertexData + vertexDataOffset, componentByteSize);
        }
    }

    if (swap) // swap bytes
    {
        if (componentByteSize == 2)
        {
            uint16_t *componentUints = (uint16_t *)componentBytes;
            for (uint32_t i = 0; i < componentBytesLength; i += 2)
            {
                *componentUints++ = bswap16(*componentUints);
            }
        }
        else if (componentByteSize == 4)
        {

            uint32_t *componentUints = (uint32_t *)componentBytes;
            for (uint32_t i = 0; i < componentBytesLength; i += 4)
            {
                *componentUints++ = bswap32(*componentUints);
            }
        }
    }

    PyObject *res = PyByteArray_FromStringAndSize(componentBytes, componentBytesLength);
    PyMem_Free(componentBytes);
    return res;

    // fast enough in Python
    // uint32_t itemCount = componentBytesLength / componentByteSize;
    // PyObject *lst = PyList_New(itemCount);
    // if (!lst)
    //     return NULL;

    // switch (format)
    // {
    // case kVertexFormatFloat:
    // {
    //     float *items = (float *)componentBytes;
    //     for (uint32_t i = 0; i < itemCount; i++)
    //     {
    //         PyList_SetItem(lst, i, PyFloat_FromDouble((double)*items++));
    //     }
    //     // result[i] = BitConverter.ToSingle(inputBytes, i * 4);
    //     break;
    // }
    // case kVertexFormatFloat16:
    // {
    //     uint16_t *items = (uint16_t *)componentBytes;
    //     for (uint32_t i = 0; i < itemCount; i++)
    //     {
    //         double x = _PyFloat_Unpack2(items++, 0);
    //         if (x == -1.0 && PyErr_Occurred())
    //         {
    //             return NULL;
    //         }
    //         PyList_SetItem(lst, i, PyFloat_FromDouble(x));
    //     }
    //     // result[i] = Half.ToHalf(inputBytes, i * 2);
    //     break;
    // }
    // case kVertexFormatUNorm8:
    // {
    //     uint8_t *items = componentBytes;
    //     for (uint32_t i = 0; i < itemCount; i++)
    //     {
    //         PyList_SetItem(lst, i, PyFloat_FromDouble((double)(*items++ / 255.0f)));
    //     }
    //     // result[i] = inputBytes[i] / 255f;
    //     break;
    // }
    // case kVertexFormatSNorm8:
    // {
    //     int8_t *items = (int8_t *)componentBytes;
    //     for (uint32_t i = 0; i < itemCount; i++)
    //     {
    //         PyList_SetItem(lst, i, PyFloat_FromDouble((double)MAX((*items++ / 127.0f), -1.0f)));
    //     }
    //     // result[i] = Math.Max((sbyte)inputBytes[i] / 127f, -1f);
    //     break;
    // }
    // case kVertexFormatUNorm16:
    // {
    //     uint16_t *items = (uint16_t *)componentBytes;
    //     for (uint32_t i = 0; i < itemCount; i++)
    //     {
    //         PyList_SetItem(lst, i, PyFloat_FromDouble((double)(*items++ / 65535.0f)));
    //     }
    //     // result[i] = BitConverter.ToUInt16(inputBytes, i * 2) / 65535f;
    //     break;
    // }
    // case kVertexFormatSNorm16:
    // {
    //     int16_t *items = (int16_t *)componentBytes;
    //     for (uint32_t i = 0; i < itemCount; i++)
    //     {
    //         PyList_SetItem(lst, i, PyFloat_FromDouble((double)MAX((*items++ / 32767.0f), -1.0f)));
    //     }
    //     // result[i] = Math.Max(BitConverter.ToInt16(inputBytes, i * 2) / 32767f, -1f);
    //     break;
    // }
    // case kVertexFormatUInt8:
    // case kVertexFormatSInt8:
    // {
    //     uint8_t *items = componentBytes;
    //     for (uint32_t i = 0; i < itemCount; i++)
    //     {
    //         PyList_SetItem(lst, i, PyLong_FromUnsignedLong((uint32_t)*items++));
    //     }
    //     // result[i] = inputBytes[i];
    //     break;
    // }
    // case kVertexFormatUInt16:
    // case kVertexFormatSInt16:
    // {
    //     uint16_t *items = (uint16_t *)componentBytes;
    //     for (uint32_t i = 0; i < itemCount; i++)
    //     {
    //         PyList_SetItem(lst, i, PyLong_FromUnsignedLong((uint32_t)*items++));
    //     }
    //     // result[i] = BitConverter.ToInt16(inputBytes, i * 2);
    //     break;
    // }
    // case kVertexFormatUInt32:
    // case kVertexFormatSInt32:
    // {
    //     uint32_t *items = (uint32_t *)componentBytes;
    //     for (uint32_t i = 0; i < itemCount; i++)
    //     {
    //         PyList_SetItem(lst, i, PyLong_FromUnsignedLong(*items++));
    //     }
    //     // result[i] = BitConverter.ToInt32(inputBytes, i * 4);
    //     break;
    // }
    // }
    // free(componentBytes);
    // return lst;
}
