#include "types.h"

#include "sega.h"
#include "tools.h"

#include "maths.h"
#include "vdp.h"
#include "vdp_tile.h"


u16 randbase;


void fastmemset(void *to, u8 value, u16 len)
{
    u8 *dst8;
    u32 *dst32;
    u32 value32;
    u16 cnt;

    dst8 = (u8 *) to;

    if (len < 16)
    {
        cnt = len;
        while (cnt--) *dst8++ = value;
        return;
    }

    while ((((u32) dst8) & 3) && (len--)) *dst8++ = value;

    value32 = (value << 0) | (value << 8) | (value << 16) | (value << 24);
    dst32 = (u32 *) dst8;

    cnt = len >> 4;
    while (cnt--)
    {
        *dst32++ = value32;
        *dst32++ = value32;
        *dst32++ = value32;
        *dst32++ = value32;
    }

    cnt = (len >> 2) & 3;
    while (cnt--) *dst32++ = value32;

    dst8 = (u8 *) dst32;

    cnt = len & 3;
    while (cnt--) *dst8++ = value;
}

void fastmemcpy(void *to, const void *from, u16 len)
{
    const u8 *src8;
    const u32 *src32;
    u8 *dst8;
    u32 *dst32;
    u16 cnt;

    dst8 = (u8 *) to;
    src8 = (u8 *) from;

    if ((len < 16) || ((((u32)to) & 3) != (((u32)from) & 3)))
    {
        cnt = len;
        while (cnt--) *dst8++ = *src8++;
        return;
    }

    while ((((u32) dst8) & 3) && (len--)) *dst8++ = *src8++;

    dst32 = (u32 *) dst8;
    src32 = (u32 *) src8;

    cnt = len >> 4;
    while (cnt--)
    {
        *dst32++ = *src32++;
        *dst32++ = *src32++;
        *dst32++ = *src32++;
        *dst32++ = *src32++;
    }

    cnt = (len >> 2) & 3;
    while (cnt--) *dst32++ = *src32++;

    dst8 = (u8 *) dst32;
    src8 = (u8 *) src32;

    cnt = len & 3;
    while (cnt--) *dst8++ = *src8++;
}

void memset8(void *to, u8 value, u16 len)
{
    u8 *dst;
    u16 cnt;

    dst = (u8 *) to;
    cnt = len;
    while(cnt--) *dst++ = value;
}

void memcpy8(void *to, const void *from, u16 len)
{
    u8 *dst;
    u8 *src;
    u16 cnt;

    dst = (u8 *) to;
    src = (u8 *) from;
    cnt = len;
    while(cnt--) *dst++ = *src++;
}


u32 strlen(const char *str)
{
    const char *src;

    src = str;
    while (*src++);

    return (src - str) - 1;
}

char* strcpy(register char *to, const char *from)
{
    const char *src;
    char *dst;

    src = from;
    dst = to;

    while ((*dst++ = *src++));

    return to;
}


u32 intToStr(s32 value, char *str, s32 minsize)
{
    u32 res;
    char data[16];
    char *dst;

    data[15] = 0;
    dst = &data[15];
    if (value < 0) res = -value;
    else res = value;
    while (res)
    {
        *--dst = '0' + (res % 10);
        res /= 10;
        minsize--;
    }
    while (minsize > 0)
    {
        *--dst = '0';
        minsize--;
    }
    if (value < 0) *--dst = '-';
    strcpy(str, dst);

    return strlen(str);
}

u32 uintToStr(u32 value, char *str, s32 minsize)
{
    u32 res;
    char data[16];
    char *dst;

    data[15] = 0;
    dst = &data[15];
    res = value;
    while (res)
    {
        *--dst = '0' + (res % 10);
        res /= 10;
        minsize--;
    }
    while (minsize > 0)
    {
        *--dst = '0';
        minsize--;
    }
    strcpy(str, dst);

    return strlen(str);
}

u32 fix32ToStr(fix32 value, char *str)
{
    u32 len;

    len = intToStr(fix32ToInt(value), str, 1);
    str[len++] = '.';
    len += uintToStr((fix32Frac(value) * 1000) / (1 << FIX32_FRAC_BITS), &str[len], 3);

    return len;
}

u32 fix16ToStr(fix16 value, char *str)
{
    u32 len;

    len = intToStr(fix16ToInt(value), str, 1);
    str[len++] = '.';
    len += uintToStr((fix16Frac(value) * 1000) / (1 << FIX16_FRAC_BITS), &str[len], 3);

    return len;
}

#define QSORT(type)                                 \
void QSort_##type(type *data, u16 left, u16 right)  \
{                                                   \
    u16 i, j;                                       \
    type val;                                       \
                                                    \
    do                                              \
    {                                               \
        i = left;                                   \
        j = right;                                  \
        val = data[(left + right) / 2];             \
                                                    \
        do                                          \
        {                                           \
            while (data[i] < val) i++;              \
            while (data[j] > val) j--;              \
            if (i <= j)                             \
            {                                       \
                u16 tmp;                            \
                                                    \
                tmp = data[i];                      \
                data[i] = data[j];                  \
                data[j] = tmp;                      \
                i++;                                \
                j--;                                \
            }                                       \
        } while(i <= j);                            \
                                                    \
        if (left < j) QSort_##type(data, left, j);  \
        left = i;                                   \
    } while (i >= right);                           \
}

QSORT(u8)
QSORT(s8)
QSORT(u16)
QSORT(s16)
QSORT(u32)
QSORT(s32)


u32 getFPS()
{
    static u32 framecnt;
    static u32 last;
    static u32 result;
    u32 current;
    u32 vcnt;

    vcnt = GET_VCOUNTER;
    if (vcnt >= 224) vcnt -= 224;
    else vcnt += (255 - 224);
    current = (vtimer << 8) + vcnt;

	if ((current - last) > 2000)
    {
        u32 base;

        if (ISPALSYSTEM) base = 50;
        else base = 60;
        result = ((base << 8) * framecnt) / (current - last);
        last = current;
        framecnt = 1;
    }
	else framecnt++;

	return result;
}

void showFPS()
{
    char str[16];

    // display FPS
    intToStr(getFPS(), str, 1);
    VDP_fillTileRect(APLAN, 0, 2, 2, 6, 1);
    VDP_drawText(APLAN, str, 0x8000, 2, 2);
}


u16 random()
{
    randbase ^= (randbase >> 1) ^ GET_HVCOUNTER;
    randbase ^= (randbase << 1);

    return randbase;
}
