/*
 * DMLib
 * -- Sprite / surface blitting functions
 * Programmed and designed by Matti 'ccr' Hamalainen
 * (C) Copyright 2011 Tecnic Software productions (TNSP)
 */
#include "dmlib.h"

//#define DM_CLIP_DEBUG

typedef struct
{
    int v0, v1, voffs, vadd;
    DMFixedPoint32 vdelta;
} DMQValue;


int dmScaledClipCoord(DMQValue *pv, int v0, int v1, int v2, int clipMin, int clipMax)
{
    DMFixedPoint32 a, b;

    // Basic bounds check
    if (v1 < 1 || v2 < 1 || v0 + v2 < clipMin || v0 >= clipMax)
    {
#ifdef DM_CLIPDEBUG
        printf("out of bounds\n");
#endif
        return -1;
    }

    // Calculate delta
    FP_SETHL(a, v1, 0);
    FP_CONV(b, v2);
    FP_DIV_R(pv->vdelta, a, b);

    // Perform clipping
    if (v0 + v2 >= clipMax)
    {
        pv->vadd = v0;
        pv->v1 = clipMax;
    }
    else
    {
        pv->vadd = clipMax - v2;
        pv->v1 = v0 + v2;
    }
    
    if (v0 < clipMin)
    {
        pv->voffs = clipMin - v0;
        pv->v0 = clipMin;
        pv->vadd -= v0 + clipMin;
    }
    else
    {
        pv->voffs = 0;
        pv->v0 = v0;
    }
    
#ifdef DM_CLIP_DEBUG
    printf("dmClipCoord(%d, (%d, %d), [%d, %d]): vdelta=",
        v0, v1, v2, clipMin, clipMax);
    FP_PRINTF(pv->vdelta);
    printf(", v0=%d, v1=%d, voffs=%d, vadd=%d\n",
        pv->v0, pv->v1, pv->voffs, pv->vadd);
#endif

    return 0;
}


int dmUnscaledClipCoord(DMQValue *pv, int v0, int v1, int clipMin, int clipMax)
{
    // Basic bounds check
    if (v1 < 1 || v0 + v1 < clipMin || v0 >= clipMax)
        return -1;

    // Perform clipping
    if (v0 + v1 >= clipMax)
    {
        pv->vadd = v0;
        pv->v1 = clipMax;
    }
    else
    {
        pv->vadd = clipMax - v1;
        pv->v1 = v0 + v1;
    }
    
    if (v0 < clipMin)
    {
        pv->voffs = clipMin - v0;
        pv->v0 = clipMin;
        pv->vadd -= v0 + clipMin;
    }
    else
    {
        pv->voffs = 0;
        pv->v0 = v0;
    }
    
    return 0;
}


#include "dmblitfunc.h"


static const DMScaledBlitFunc dmScaledBlitTable[DMD_NMODES][DMD_NBITDEPTHS][DMD_NBITDEPTHS] =
{
    // DMD_NONE
    {
        { dmScaledBlitSurface8to8             , dmScaledBlitSurface8to32 },
        { NULL                                , dmScaledBlitSurface32to32 },
    },
    // DMD_TRANSPARENT
    {
        { dmScaledBlitSurface8to8Transparent  , dmScaledBlitSurface8to32Transparent },
        { NULL                                , dmScaledBlitSurface32to32Transparent },
    },
    // DMD_SATURATE
    {
        { dmScaledBlitSurface8to8Saturate     , dmScaledBlitSurface8to32Saturate },
        { NULL                                , dmScaledBlitSurface32to32Saturate },
    },
#if 0
    // DMD_NONE | DMD_ANTIALIAS
    {
        { dmScaledBlitSurface8to8Antialias    , dmScaledBlitSurface8to32Antialias },
        { NULL                                , dmScaledBlitSurface32to32Antialias },
    },
    // DMD_TRANSPARENT | DMD_ANTIALIAS
    {
        { dmScaledBlitSurface8to8AATransp     , dmScaledBlitSurface8to32AATransparent },
        { NULL                                , dmScaledBlitSurface32to32AATransparent },
    },
    // DMD_SATURATE | DMD_ANTIALIAS
    {
        { dmScaledBlitSurface8to8AASaturate   , dmScaledBlitSurface8to32AASaturate },
        { NULL                                , dmScaledBlitSurface32to32AASaturate },
    },
#endif
};

static const int ndmScaledBlitTable = sizeof(dmScaledBlitTable) / sizeof(dmScaledBlitTable[0]);


DMScaledBlitFunc dmGetScaledBlitFunc(SDL_PixelFormat *src, SDL_PixelFormat *dst, int mode)
{
    int isrc, idst;
    if (src == NULL || dst == NULL || mode < 0 || mode >= ndmScaledBlitTable)
        return NULL;

    isrc = dmBitsPerPixel2Index(src->BitsPerPixel);
    idst = dmBitsPerPixel2Index(dst->BitsPerPixel);
    if (isrc < 0 || idst < 0)
        return NULL;
    
    return dmScaledBlitTable[mode][isrc][idst];
}


int dmScaledBlitSurfaceAny(SDL_Surface *src, const int x0, const int y0, const int dwidth, const int dheight, SDL_Surface *dst, int mode)
{
    DMScaledBlitFunc bfunc = dmGetScaledBlitFunc(src->format, dst->format, mode);
    
    if (bfunc == NULL)
        return -15;

    return bfunc(src, x0, y0, dwidth, dheight, dst);
}


static const DMUnscaledBlitFunc dmUnscaledBlitTable[DMD_NMODES][DMD_NBITDEPTHS][DMD_NBITDEPTHS] =
{
    // DMD_NONE
    {
        { dmUnscaledBlitSurface8to8           , dmUnscaledBlitSurface8to32 },
        { NULL                                , dmUnscaledBlitSurface32to32 },
    },
    // DMD_TRANSPARENT
    {
        { dmUnscaledBlitSurface8to8Transparent, dmUnscaledBlitSurface8to32Transparent },
        { NULL                                , dmUnscaledBlitSurface32to32Transparent },
    },
    // DMD_SATURATE
    {
        { dmUnscaledBlitSurface8to8Saturate   , dmUnscaledBlitSurface8to32Saturate },
        { NULL                                , dmUnscaledBlitSurface32to32Saturate },
    },
#if 0
    // DMD_NONE | DMD_ANTIALIAS
    {
        { dmUnscaledBlitSurface8to8Antialias  , dmUnscaledBlitSurface8to32Antialias },
        { NULL                                , dmUnscaledBlitSurface32to32Antialias },
    },
    // DMD_TRANSPARENT | DMD_ANTIALIAS
    {
        { dmUnscaledBlitSurface8to8AATransp   , dmUnscaledBlitSurface8to32AATransparent },
        { NULL                                , dmUnscaledBlitSurface32to32AATransparent },
    },
    // DMD_SATURATE | DMD_ANTIALIAS
    {
        { dmUnscaledBlitSurface8to8AASaturate , dmUnscaledBlitSurface8to32AASaturate },
        { NULL                                , dmUnscaledBlitSurface32to32AASaturate },
    },
#endif
};

static const int ndmUnscaledBlitTable = sizeof(dmUnscaledBlitTable) / sizeof(dmUnscaledBlitTable[0]);



DMUnscaledBlitFunc dmGetUnscaledBlitFunc(SDL_PixelFormat *src, SDL_PixelFormat *dst, int mode)
{
    int isrc, idst;
    if (src == NULL || dst == NULL || mode < 0 || mode >= ndmUnscaledBlitTable)
        return NULL;

    isrc = dmBitsPerPixel2Index(src->BitsPerPixel);
    idst = dmBitsPerPixel2Index(dst->BitsPerPixel);
    if (isrc < 0 || idst < 0)
        return NULL;
    
    return dmUnscaledBlitTable[mode][isrc][idst];
}


int dmUnscaledBlitSurfaceAny(SDL_Surface *src, const int x0, const int y0, SDL_Surface *dst, int mode)
{
    DMUnscaledBlitFunc bfunc = dmGetUnscaledBlitFunc(src->format, dst->format, mode);
    
    if (bfunc == NULL)
        return -15;

    return bfunc(src, x0, y0, dst);
}


SDL_Surface *dmConvertScaledSurface(SDL_Surface *src, SDL_PixelFormat *fmt, Uint32 flags, const int dwidth, const int dheight)
{
    // Create the target surface
    SDL_Surface *result = SDL_CreateRGBSurface(flags, dwidth, dheight, fmt->BitsPerPixel, fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask);
    if (result == NULL)
        return NULL;
    
    // Use scaled blitting to convert the scaled image ..
    dmScaledBlitSurfaceAny(src, 0, 0, dwidth, dheight, result, DMD_NONE);

    return result;
}
