#include "types.h"

#include "tools.h"
#include "maths.h"
#include "maths3D.h"

#include "vdp.h"
#include "bitmap.h"
#include "bitmapx.h"



static s16 LeftPoly[BITMAP_PIXHEIGHT * 2];
static s16 RightPoly[BITMAP_PIXHEIGHT * 2];

// used to store polygone min Y and max Y
static s16 minY;
static s16 maxY;


u8 getPixelDirect(s16 x, s16 y)
{
    volatile u32 *plctrl;
    volatile u16 *pwdata;
    u16 addr;

    addr = TILE_BITMAP + (((x >> 2) + ((y >> 3) * BITMAP_WIDTH)) * 32);
    addr += (x & 2) + ((y & 7) * 4);

    plctrl = (u32 *) GFX_CTRL_PORT;
    pwdata = (u16 *) GFX_DATA_PORT;

    *plctrl = GFX_READ_VRAM_ADDR(addr);
    if (x & 1) return *pwdata & 0xFF;
    else return *pwdata >> 8;
}

void setPixelDirect(s16 x, s16 y, u8 col)
{
    volatile u32 *plctrl;
    volatile u16 *pwdata;
    u16 addr;
    u16 data;

    addr = TILE_BITMAP + (((x >> 2) + ((y >> 3) * BITMAP_WIDTH)) * 32);
    addr += (x & 2) + ((y & 7) * 4);

    plctrl = (u32 *) GFX_CTRL_PORT;
    pwdata = (u16 *) GFX_DATA_PORT;

    *plctrl = GFX_READ_VRAM_ADDR(addr);
    data = *pwdata;

    *plctrl = GFX_WRITE_VRAM_ADDR(addr);
    if (x & 1) *pwdata = (data & 0xFF00) | col;
    else *pwdata = (data & 0x00FF) | (col << 8);
}


u8 getPixel(s16 x, s16 y)
{
    return BitmapBuffer[x + (y * BITMAP_PITCH)];
}

void setPixel(s16 x, s16 y, u8 col)
{
    BitmapBuffer[x + (y * BITMAP_PITCH)] = col;
}


void drawLine(s16 x1, s16 y1, s16 x2, s16 y2, u8 col)
{
    s16 dx, dy;
    s16 step_each, step_delta;
    u8 *buf;

    dx = x2 - x1;
    dy = y2 - y1;
    buf = &BitmapBuffer[x1 + (y1 * BITMAP_PITCH)];

    if (dx < 0)
    {
        dx = -dx;
        if (dy < 0)
        {
            dy = -dy;
            if (dx > dy)
            {
                step_each = -1;
                step_delta = -BITMAP_PITCH;
            }
            else
            {
                s16 tmp;

                // we swap dx and dy
                tmp = dy;
                dy = dx;
                dx = tmp;

                step_each = -BITMAP_PITCH;
                step_delta = -1;
            }
        }
        else
        {
            if (dx > dy)
            {
                step_each = -1;
                step_delta = BITMAP_PITCH;
            }
            else
            {
                s16 tmp;

                // we swap dx and dy
                tmp = dy;
                dy = dx;
                dx = tmp;

                step_each = BITMAP_PITCH;
                step_delta = -1;
            }
        }
    }
    else
    {
        if (dy < 0)
        {
            dy = -dy;
            if (dx > dy)
            {
                step_each = 1;
                step_delta = -BITMAP_PITCH;
            }
            else
            {
                s16 tmp;

                // we swap dx and dy
                tmp = dy;
                dy = dx;
                dx = tmp;

                step_each = -BITMAP_PITCH;
                step_delta = 1;
            }
        }
        else
        {
            if (dx > dy)
            {
                step_each = 1;
                step_delta = BITMAP_PITCH;
            }
            else
            {
                s16 tmp;

                // we swap dx and dy
                tmp = dy;
                dy = dx;
                dx = tmp;

                step_each = BITMAP_PITCH;
                step_delta = 1;
            }
        }
    }

    {
        s16 delta;
        u16 cnt;

        cnt = dx;
        delta = dx;

        while(cnt--)
        {
            *buf = col;

            buf += step_each;
            if ((delta -= dy) < 0)
            {
                buf += step_delta;
                delta += dx;
            }
        }
    }
}


static void calculatePolyEdge(vect2D_s16 *from, vect2D_s16 *to)
{
    s16 *src;
    vect2D_s16 pts_clip[2];
    fix16 step;

    {
        s16 deltaX;
        s16 deltaY;

        if ((deltaY = to->y - from->y) == 0) return;
        deltaX = to->x - from->x;

        // deltaY > 0  ==> left side
        if (deltaY > 0)
        {
            // out of bitmap screen
            if ((from->x < 0) && (to->x < 0)) return;
            // out of bitmap screen
            if (from->y >= bitmap_height_pixel) return;
            // out of bitmap screen
            if (to->y < 0) return;

            step = fix16Div(intToFix16(deltaX), intToFix16(deltaY));

            pts_clip[0].x = from->x;
            pts_clip[0].y = from->y;
            pts_clip[1].x = to->x;
            pts_clip[1].y = to->y;

            src = LeftPoly;
        }
        else        // deltaY < 0  ==> right side
        {
            // out of bitmap screen
            if ((from->x >= bitmap_width_pixel) && (to->x >= bitmap_width_pixel)) return;
            // out of bitmap screen
            if (from->y < 0) return;
            // out of bitmap screen
            if (to->y >= bitmap_height_pixel) return;

            step = fix16Div(intToFix16(-deltaX), intToFix16(-deltaY));

            pts_clip[0].x = to->x;
            pts_clip[0].y = to->y;
            pts_clip[1].x = from->x;
            pts_clip[1].y = from->y;

            src = RightPoly;
        }
    }
    // process clipping
    if (pts_clip[0].y < 0)
    {
        pts_clip[0].x += fix16ToInt(fix16Mul(step, intToFix16(-pts_clip[0].y)));
        pts_clip[0].y = 0;
    }
    if (pts_clip[1].y >= bitmap_height_pixel) pts_clip[1].y = bitmap_height_pixel - 1;

    {
        s16 len;

        // nothing to do
        if ((len = (pts_clip[1].y - pts_clip[0].y)) <= 0) return;

        if (pts_clip[0].y < minY) minY = pts_clip[0].y;
        if (pts_clip[1].y > maxY) maxY = pts_clip[1].y;

        src += pts_clip[0].y;

        {
            fix16 x;

            x = intToFix16(pts_clip[0].x);

            while(len--)
            {
                *src++ = fix16ToInt(x);
                x += step;
            }
        }
    }
}

/*
static void calculatePolyEdgeCol(vect2D_s16 *from, vect2D_s16 *to, s16 colfrom, s16 colto)
{
    s16 *src;
    vect2D_s16 pts_clip[2];
    fix16 step;
    fix16 stepcol;

    {
        s16 deltaX;
        s16 deltaCol;
        s16 deltaY;

        if ((deltaY = to->y - from->y) == 0) return;
        deltaX = to->x - from->x;
        deltaCol = colto - colfrom;

        // deltaY > 0  ==> left side
        if (deltaY > 0)
        {
            // out of bitmap screen
            if ((from->x < 0) && (to->x < 0)) return;
            // out of bitmap screen
            if (from->y >= bitmap_height_pixel) return;
            // out of bitmap screen
            if (to->y < 0) return;

            step = fix16Div(intToFix16(deltaX), intToFix16(deltaY));
            stepcol = fix16Div(intToFix16(deltaCol), intToFix16(deltaY));

            pts_clip[0].x = from->x;
            pts_clip[0].y = from->y;
            pts_clip[1].x = colfrom;
            pts_clip[1].y = to->y;

            src = LeftPoly;
        }
        else        // deltaY < 0  ==> right side
        {
            // out of bitmap screen
            if ((from->x >= bitmap_width_pixel) && (to->x >= bitmap_width_pixel)) return;
            // out of bitmap screen
            if (from->y < 0) return;
            // out of bitmap screen
            if (to->y >= bitmap_height_pixel) return;

            step = fix16Div(intToFix16(-deltaX), intToFix16(-deltaY));
            stepcol = fix16Div(intToFix16(-deltaCol), intToFix16(-deltaY));

            pts_clip[0].x = to->x;
            pts_clip[0].y = to->y;
            pts_clip[1].x = colto;
            pts_clip[1].y = from->y;

            src = RightPoly;
        }
    }
    // process clipping
    if (pts_clip[0].y < 0)
    {
        fix16 tmp;

        tmp = intToFix16(-pts_clip[0].y);
        pts_clip[0].x += fix16ToInt(fix16Mul(step, tmp));
        pts_clip[1].x += fix16Mul(stepcol, tmp);
        pts_clip[0].y = 0;
    }
    if (pts_clip[1].y >= bitmap_height_pixel) pts_clip[1].y = bitmap_height_pixel - 1;

    {
        s16 len;

        // nothing to do
        if ((len = (pts_clip[1].y - pts_clip[0].y)) <= 0) return;

        if (pts_clip[0].y < minY) minY = pts_clip[0].y;
        if (pts_clip[1].y > maxY) maxY = pts_clip[1].y;

        src += pts_clip[0].y;

        {
            fix16 x;
            fix16 col;

            x = intToFix16(pts_clip[0].x);
            col = intToFix16(pts_clip[1].x);

            while(len--)
            {
                src[BITMAP_PIXHEIGHT] = col;
                col += stepcol;
                *src++ = fix16ToInt(x);
                x += step;
            }
        }
    }
}
*/


void drawPolygone(vect2D_s16 *pts, u16 numpts, u8 col, u8 culling)
{
    minY = BITMAP_PIXHEIGHT;
    maxY = 0;

    {
        vect2D_s16 *curpts;
        u16 i;

        curpts = pts;
        i = numpts - 1;
        while(i--)
        {
            calculatePolyEdge(&curpts[0], &curpts[1]);
            curpts++;
        }

        // last line
        calculatePolyEdge(&curpts[0], &pts[0]);
    }

    {
        s16 len;

        // something to do
        if ((len = maxY - minY) > 0)
        {
            s16 *left;
            s16 *right;
            u8 *buf;
            u8 c;

            left = &LeftPoly[minY];
            right = &RightPoly[minY];
            buf = &BitmapBuffer[minY * BITMAP_PITCH];
            c = col | (col << 4);

            while (len--)
            {
                s16 x1, x2;

                x1 = *left++;
                x2 = *right++;

                if (x1 < 0) x1 = 0;
                if (x2 >= bitmap_width_pixel) x2 = bitmap_width_pixel - 1;

                // backface ?
                if (x1 > x2)
                {
                    s16 tmp;

                    if (culling) return;

                    tmp = x1;
                    x1 = x2;
                    x2 = tmp;
                }

                // inline it can help
//                fastmemset(&buf[x1], c, (x2 - x1) + 1);

                {
                    // do horizontal line
                    u8 *dst8;
                    u16 *dst16;
                    u16 cnt;
                    u16 cnt2;
                    u16 c16;

                    dst8 = &buf[x1];
                    cnt = (x2 - x1) + 1;

                    if ((u32) (dst8) & 1)
                    {
                        *dst8++ = c;
                        cnt--;
                    }

                    c16 = c + (c << 8);
                    dst16 = (u16 *) dst8;
                    cnt2 = cnt >> 1;

                    while (cnt2--) *dst16++ = c16;

                    if (cnt & 1)
                    {
                        dst8 = (u8 *) dst16;
                        *dst8 = c;
                    }
                }

                buf += BITMAP_PITCH;
            }
        }
    }
}


void drawPolygone3D(const u16 *ind, u16 numpts, const vect3D_f16 *norm, u8 col)
{
    // primary backface detection
    {
        fix16 value;

        value = fix16Mul(norm->x, camview_trans.x) +
                fix16Mul(norm->y, camview_trans.y) +
                fix16Mul(norm->z, camview_trans.z);
        if (value >= 0) return;
    }

    minY = BITMAP_PIXHEIGHT;
    maxY = 0;

    {
        const u16 *curind;
        u16 i;

        curind = ind;
        i = numpts - 1;
        while(i--)
        {
            calculatePolyEdge(&pts_2D[curind[0]], &pts_2D[curind[1]]);
            curind++;
        }

        // last line
        calculatePolyEdge(&pts_2D[*curind], &pts_2D[*ind]);
    }

    {
        s16 len;

        // something to do
        if ((len = maxY - minY) > 0)
        {
            s16 *left;
            s16 *right;
            u8 *buf;
            u8 c;

            left = &LeftPoly[minY];
            right = &RightPoly[minY];
            buf = &BitmapBuffer[minY * BITMAP_PITCH];

            {
                fix16 light;

                // light calculation
                light = fix16Mul(norm->x, light_trans.x) +
                        fix16Mul(norm->y, light_trans.y) +
                        fix16Mul(norm->z, light_trans.z);

                c = (col / 2) - fix16Mul(light / 2, col);
                c |= c << 4;
            }

            while (len--)
            {
                s16 x1, x2;

                x1 = *left++;
                x2 = *right++;

                if (x1 < 0) x1 = 0;
                if (x2 >= bitmap_width_pixel) x2 = bitmap_width_pixel - 1;

                // secondary backface detection
                if (x1 < x2)
                {
                    // do horizontal line
                    u8 *dst8;
                    u16 *dst16;
                    u16 cnt;
                    u16 cnt2;
                    u16 c16;

                    dst8 = &buf[x1];
//                    cnt = (x2 - x1) + 1;
                    cnt = x2 - x1;

                    if ((u32) (dst8) & 1)
                    {
                        *dst8++ = c;
                        cnt--;
                    }

                    c16 = c + (c << 8);
                    dst16 = (u16 *) dst8;
                    cnt2 = cnt >> 1;

                    while (cnt2--) *dst16++ = c16;

                    if (cnt & 1)
                    {
                        dst8 = (u8 *) dst16;
                        *dst8 = c;
                    }
                }

                buf += BITMAP_PITCH;
            }
        }
    }
}

/*
void drawPolygone3DGouraud(const u16 *ind, u16 numpts, const vect3D_f16 *facenorm, const vect3D_f16 *norm, u8 col)
{
    // primary backface detection
    {
        fix16 value;

        value = fix16Mul(facenorm->x, camview_trans.x) +
                fix16Mul(facenorm->y, camview_trans.y) +
                fix16Mul(facenorm->z, camview_trans.z);
        if (value >= 0) return;
    }

    minY = BITMAP_PIXHEIGHT;
    maxY = 0;

    {
        const u16 *curind;
        const vect3D_f16 *curnorm;
        fix16 light;
        s16 colfrom, colto;
        u16 i;

        curind = ind;

        // light calculation
        curnorm = &norm[curind[0]];
        light = fix16Mul(curnorm->x, light_trans.x) +
                fix16Mul(curnorm->y, light_trans.y) +
                fix16Mul(curnorm->z, light_trans.z);
        colfrom = col - fix16Mul(light, col);

        i = numpts - 1;
        while(i--)
        {
            // light calculation
            curnorm = &norm[curind[1]];
            light = fix16Mul(curnorm->x, light_trans.x) +
                    fix16Mul(curnorm->y, light_trans.y) +
                    fix16Mul(curnorm->z, light_trans.z);
            colto = col - fix16Mul(light, col);

            calculatePolyEdgeCol(&pts_2D[curind[0]], &pts_2D[curind[1]], colfrom, colto);
            curind++;
            colfrom = colto;
        }

        // light calculation
        curnorm = &norm[*ind];
        light = fix16Mul(curnorm->x, light_trans.x) +
                fix16Mul(curnorm->y, light_trans.y) +
                fix16Mul(curnorm->z, light_trans.z);
        colto = col - fix16Mul(light, col);

        // last line
        calculatePolyEdgeCol(&pts_2D[*curind], &pts_2D[*ind], colfrom, colto);
    }

    {
        s16 len;

        // something to do
        if ((len = maxY - minY) > 0)
        {
            s16 *left;
            s16 *right;
            u8 *buf;

            left = &LeftPoly[minY];
            right = &RightPoly[minY];
            buf = &BitmapBuffer[minY * BITMAP_PITCH];

            while (len--)
            {
                s16 x1, x2;
                fix16 col1, col2;

                col1 = left[BITMAP_PIXHEIGHT];
                x1 = *left++;
                col2 = right[BITMAP_PIXHEIGHT];
                x2 = *right++;

                if (x1 < 0) x1 = 0;
                if (x2 >= bitmap_width_pixel) x2 = bitmap_width_pixel - 1;

                // secondary backface detection
                if (x1 < x2)
                {
                    // do horizontal gouraud line
                    u8 *dst8;
                    u16 cnt;
                    fix16 stepc;
                    u8 c;

                    dst8 = &buf[x1];
//                    cnt = (x2 - x1) + 1;
                    cnt = x2 - x1;
                    stepc = fix16Div(col2 - col1, intToFix16(cnt));

                    while (cnt--)
                    {
//                        c = round16(col1);
                        c = fix16ToInt(col1);
//                        *dst8++ = c;
                        *dst8++ = c | (c << 4);
                        col1 += stepc;
                    }
                }

                buf += BITMAP_PITCH;
            }
        }
    }
}
*/
