/*
 *  Copyright � 2006-2016 SplinterGU (Fenix/Bennugd)
 *  Copyright � 2002-2006 Fenix Team (Fenix)
 *  Copyright � 1999-2002 Jos� Luis Cebri�n Pag�e (Fenix)
 *
 *  This file is part of Bennu - Game Development
 *
 *  This software is provided 'as-is', without any express or implied
 *  warranty. In no event will the authors be held liable for any damages
 *  arising from the use of this software.
 *
 *  Permission is granted to anyone to use this software for any purpose,
 *  including commercial applications, and to alter it and redistribute it
 *  freely, subject to the following restrictions:
 *
 *     1. The origin of this software must not be misrepresented; you must not
 *     claim that you wrote the original software. If you use this software
 *     in a product, an acknowledgment in the product documentation would be
 *     appreciated but is not required.
 *
 *     2. Altered source versions must be plainly marked as such, and must not be
 *     misrepresented as being the original software.
 *
 *     3. This notice may not be removed or altered from any source
 *     distribution.
 *
 */

/* --------------------------------------------------------------------------- */

#include <string.h>

#include <math.h>
#include <stdlib.h>

#include "bgddl.h"

#include "libgrbase.h"

#include "mod_effects.h"

/* --------------------------------------------------------------------------- */
/*
 *  FUNCTION : _get_pixel
 *
 *  Read a pixel from a bitmap
 *
 *  PARAMS :
 *      dest            Destination bitmap
 *      x, y            Pixel coordinates
 *
 *  RETURN VALUE :
 *      1, 8 or 16-bit integer with the pixel value
 *
 */

static int _get_pixel( GRAPH * dest, int x, int y )
{
    if ( x < 0 || y < 0 || x >= ( int )dest->width || y >= ( int )dest->height ) return -1 ;

    switch ( dest->format->depth )
    {
        case 8:
            return *((( uint8_t * )dest->data ) + x + dest->pitch * y) ;

        case 16:
            return *( uint16_t * )((( uint8_t *)dest->data) + ( x << 1 ) + dest->pitch * y) ;

        case 32:
            return *( uint32_t * )((( uint8_t *)dest->data) + ( x << 2 ) + dest->pitch * y) ;

        case 1:
            return ((( uint8_t * )dest->data )[x / 8 + dest->pitch * y] & ( 0x80 >> ( x & 7 ) ) ) ? 1 : 0;
    }
    return -1;
}

/*
 *  FUNCTION : _put_pixel
 *
 *  Paint a pixel with no clipping whatsoever, except by
 *  the bitmap's dimensions
 *
 *  PARAMS :
 *      dest            Destination bitmap
 *      x, y            Pixel coordinates
 *      color           1, 8 or 16-bit pixel value
 *
 *  RETURN VALUE :
 *      None
 *
 */

static void _put_pixel( GRAPH * dest, int x, int y, int color )
{
    if ( x < 0 || y < 0 || x >= ( int )dest->width || y >= ( int )dest->height ) return ;

    dest->modified = 2 ;

    switch ( dest->format->depth )
    {
        case 8:
            *((( uint8_t * )dest->data ) + x + dest->pitch * y) = color ;
            break;

        case 16:
            *( uint16_t * )((( uint8_t *)dest->data) + ( x << 1 ) + dest->pitch * y) = color ;
            break;

        case 32:
            *( uint32_t * )((( uint8_t *)dest->data) + ( x << 2 ) + dest->pitch * y) = color ;
            break;

        case 1:
            if ( color )
                ((( uint8_t * )dest->data )[x / 8 + dest->pitch * y] ) |= ( 0x80 >> ( x & 7 ) ) ;
            else
                ((( uint8_t * )dest->data )[x / 8 + dest->pitch * y] ) &= ~( 0x80 >> ( x & 7 ) ) ;
            break;
    }
}

/* ----------------------------------------------------------------- */

static int modeffects_filter( INSTANCE *my, int *params )
{ //fpg,map,tabla10
    GRAPH * map = bitmap_get( params[0], params[1] ), * map2;
    int *tabla = ( int* )params[2];
    int x, y, i, j;
    int r, g, b, a, r2, g2, b2, c;
    float r1, g1, b1;
    int color;

    if ( !map ) return 0;
    if ( map->format->depth < 16 ) return 0;

    map2 = bitmap_clone( map );

    r1 = 0;
    g1 = 0;
    b1 = 0;
    c = 0;
    for ( i = 0;i < map->width;i++ )
    {
        for ( j = 0;j < map->height;j++ )
        {
            color = _get_pixel( map, i, j );
            if ( !color ) continue;

            gr_get_rgba_depth( map->format->depth, color, &r, &g, &b, &a ); // only need alpha

            for ( y = j - 1;y < j + 2;y++ )
            {
                r2 = 0;
                g2 = 0;
                b2 = 0;
                for ( x = i - 1;x < i + 2;x++, c++ )
                {
                    color = _get_pixel( map, ( x < 0 ) ? 0 : ( x > map->width - 1 ) ? map->width - 1 : x, ( y < 0 ) ? 0 : ( y > map->height - 1 ) ? map->height - 1 : y );
                    if ( !color )
                    {
                        /* Si es transparente, repetimos el ultimo color */
                        r1 += ( float )( r2 * tabla[c] );
                        g1 += ( float )( g2 * tabla[c] );
                        b1 += ( float )( b2 * tabla[c] );
                        continue;
                    }

                    gr_get_rgb_depth( map->format->depth, color, &r2, &g2, &b2 );

                    r1 += ( float )( r2 * tabla[c] );
                    g1 += ( float )( g2 * tabla[c] );
                    b1 += ( float )( b2 * tabla[c] );
                }
            }

            r1 /= tabla[9];
            g1 /= tabla[9];
            b1 /= tabla[9];

            r = ((( int )r1 ) > 255 ) ? 255 : ( int )r1;
            g = ((( int )g1 ) > 255 ) ? 255 : ( int )g1;
            b = ((( int )b1 ) > 255 ) ? 255 : ( int )b1;

            if ( r < 0 )r = 0;
            if ( g < 0 )g = 0;
            if ( b < 0 )b = 0;

            if ( !r && !g && !b )
                c = 0;
            else
                c = gr_rgba_depth( map->format->depth, r, g, b, a );

            _put_pixel( map2, i, j, c );

            r1 = 0;
            g1 = 0;
            b1 = 0;
            c = 0;
        }
    }
    memcpy( map->data, map2->data, map->height*map->pitch );
    bitmap_destroy( map2 );

    return 1 ;
}

static int modeffects_blur( INSTANCE *my, int *params )
{ // fpg,map,tipo
    GRAPH * map = bitmap_get( params[0], params[1] ), *map2;

    int x, y, i, j, c;
    int r, g, b, a, r2, g2, b2, a2;
    int color;

    if ( !map ) return 0;
    if ( map->format->depth < 16 ) return 0;

    switch ( params[2] )
    {
        case BLUR_NORMAL:
            //METODO 1 "RAPIDO" izq y arriba
            for ( i = 0; i < map->width; i++ )
                for ( j = 0; j < map->height; j++ )
                {
                    color = _get_pixel( map, i, j ) ;
                    if ( !color ) continue;
                    gr_get_rgba_depth( map->format->depth, color, &r, &g, &b, &a );
                    if ( i > 0 )
                    {
                        gr_get_rgba_depth( map->format->depth, _get_pixel( map, i - 1, j ), &r2, &g2, &b2, &a2 );
                    }
                    else
                    {
                        gr_get_rgba_depth( map->format->depth, _get_pixel( map, i + 1, j ), &r2, &g2, &b2, &a2 );
                    }
                    r += r2;
                    g += g2;
                    b += b2;
                    if ( j > 0 )
                    {
                        gr_get_rgba_depth( map->format->depth, _get_pixel( map, i, j - 1 ), &r2, &g2, &b2, &a2 );
                    }
                    else
                    {
                        gr_get_rgba_depth( map->format->depth, _get_pixel( map, i, j + 1 ), &r2, &g2, &b2, &a2 );
                    }
                    r += r2;
                    g += g2;
                    b += b2;
                    r /= 3;
                    g /= 3;
                    b /= 3;
                    _put_pixel( map, i, j, gr_rgba_depth( map->format->depth, r, g, b, a ) );
                }
            break;

        case BLUR_3x3:
            // METODO2 LENTO 3x3
            r = 0;
            g = 0;
            b = 0;
            c = 0;
            for ( i = 0;i < map->width;i++ )
                for ( j = 0;j < map->height;j++ )
                {
                    color = _get_pixel( map, i, j ) ;
                    if ( !color ) continue;
                    gr_get_rgba_depth( map->format->depth, color, &r, &g, &b, &a );
                    c++;
                    for ( x = i - 1;x < i + 2;x++ )
                    {
                        for ( y = j - 1;y < j + 2;y++ )
                        {
                            if ( x < 0 || x > map->width - 1 || y < 0 || y > map->height - 1 ) continue;
                            gr_get_rgba_depth( map->format->depth, _get_pixel( map, x, y ), &r2, &g2, &b2, &a2 );
                            r += r2;
                            g += g2;
                            b += b2;
                            c++;
                        }
                    }
                    if ( !c ) continue;
                    r /= c;
                    g /= c;
                    b /= c;
                    _put_pixel( map, i, j, gr_rgba_depth( map->format->depth, r, g, b, a ) );
                    r = 0;
                    g = 0;
                    b = 0;
                    c = 0;
                }
            break;

        case BLUR_5x5:
            // METODO3 aun mas LENTO 5x5
            r = 0;
            g = 0;
            b = 0;
            c = 0;
            for ( i = 0;i < map->width;i++ )
            {
                for ( j = 0;j < map->height;j++ )
                {
                    color = _get_pixel( map, i, j ) ;
                    if ( !color ) continue;
                    gr_get_rgba_depth( map->format->depth, color, &r, &g, &b, &a );
                    c++;
                    for ( x = i - 2;x < i + 3;x++ )
                    {
                        for ( y = j - 2;y < j + 3;y++ )
                        {
                            if ( x < 0 || x > map->width - 1 || y < 0 || y > map->height - 1 ) continue;
                            gr_get_rgba_depth( map->format->depth, _get_pixel( map, x, y ), &r2, &g2, &b2, &a2 );
                            r += r2;
                            g += g2;
                            b += b2;
                            c++;
                        }
                    }
                    if ( !c ) continue;
                    r /= c;
                    g /= c;
                    b /= c;
                    _put_pixel( map, i, j, gr_rgba_depth( map->format->depth, r, g, b, a ) );
                    r = 0;
                    g = 0;
                    b = 0;
                    c = 0;
                }
            }
            break;

        case BLUR_5x5_MAP:
            // METODO4 5x5 mapa adicional
            map2 = bitmap_clone( map );
            r = 0;
            g = 0;
            b = 0;
            c = 0;
            for ( i = 0;i < map->width;i++ )
            {
                for ( j = 0;j < map->height;j++ )
                {
                    color = _get_pixel( map, i, j ) ;
                    if ( !color ) continue;
                    gr_get_rgba_depth( map->format->depth, color, &r, &g, &b, &a );
                    c++;
                    for ( x = i - 2;x < i + 3;x++ )
                    {
                        for ( y = j - 2;y < j + 3;y++ )
                        {
                            if ( x < 0 || x > map->width - 1 || y < 0 || y > map->height - 1 ) continue;
                            gr_get_rgba_depth( map->format->depth, _get_pixel( map, x, y ), &r2, &g2, &b2, &a2 );
                            r += r2;
                            g += g2;
                            b += b2;
                            c++;
                        }
                    }
                    if ( !c ) continue;
                    r /= c;
                    g /= c;
                    b /= c;
                    _put_pixel( map2, i, j, gr_rgba_depth( map->format->depth, r, g, b, a ) );
                    r = 0;
                    g = 0;
                    b = 0;
                    c = 0;
                }
            }
            memcpy( map->data, map2->data, map->height*map->pitch );
            bitmap_destroy( map2 );
            break;
        default:
            break;

    }
    return 1 ;
}

static int modeffects_grayscale( INSTANCE *my, int *params )
{ //fpg,map,tipo
    GRAPH * map = bitmap_get( params[0], params[1] ) ;
    uint32_t i, j, c;
    int r, g, b, a;

    if ( !map ) return 0;
    if ( map->format->depth < 16 ) return 0;

    for ( i = 0;i < map->height;i++ )
        for ( j = 0;j < map->width;j++ )
        {
            gr_get_rgba_depth( map->format->depth, _get_pixel( map, j, i ), &r, &g, &b, &a );
            if ( !r && !g && !b )continue;
            c = ( int )( 0.3 * r + 0.59 * g + 0.11 * b );
            switch ( params[2] )
            {
                case GSCALE_RGB: // RGB
                    c = gr_rgba_depth( map->format->depth, c, c, c, a );
                    break;

                case GSCALE_R: // R
                    c = gr_rgba_depth( map->format->depth, c, 0, 0, a );
                    break;

                case GSCALE_G: // G
                    c = gr_rgba_depth( map->format->depth, 0, c, 0, a );
                    break;

                case GSCALE_B: // B
                    c = gr_rgba_depth( map->format->depth, 0, 0, c, a );
                    break;

                case GSCALE_RG: // RG
                    c = gr_rgba_depth( map->format->depth, c, c, 0, a );
                    break;

                case GSCALE_RB: // RB
                    c = gr_rgba_depth( map->format->depth, c, 0, c, a );
                    break;


                case GSCALE_GB: // GB
                    c = gr_rgba_depth( map->format->depth, 0, c, c, a );
                    break;

                case GSCALE_OFF:
                default:
                    c = gr_rgba_depth( map->format->depth, r, g, b, a );
            }
            _put_pixel( map, j, i, c );
        }

    return 1 ;
}

static int modeffects_rgbscale( INSTANCE *my, int *params )
{ //fpg, map, r, g, b
    GRAPH * map = bitmap_get( params[0], params[1] ) ;
    uint32_t i, j, c;
    int r, g, b, a;

    if ( !map ) return 0;
    if ( map->format->depth < 16 ) return 0;

    for ( i = 0;i < map->height;i++ )
        for ( j = 0;j < map->width;j++ )
        {
            gr_get_rgba_depth( map->format->depth, _get_pixel( map, j, i ), &r, &g, &b, &a );
            if ( !r && !g && !b )continue;
            c = ( int )( 0.3 * r + 0.59 * g + 0.11 * b );
            c = gr_rgba_depth( map->format->depth,
                               ( int )( c * *( float * )( &params[2] ) ),
                               ( int )( c * *( float * )( &params[3] ) ),
                               ( int )( c * *( float * )( &params[4] ) ),
                               a );
            _put_pixel( map, j, i, c );
        }

    return 1 ;
}

/* ----------------------------------------------------------------- */
/* exports                                                           */
/* ----------------------------------------------------------------- */

#include "mod_effects_exports.h"

/* ----------------------------------------------------------------- */