/* ----------------------------------------------------------------------------
 *         SAM Software Package License
 * ----------------------------------------------------------------------------
 * Copyright (c) 2011, Atmel Corporation
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the disclaimer below.
 *
 * Atmel's name may not be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * ----------------------------------------------------------------------------
 */

//------------------------------------------------------------------------------
//         Headers
//------------------------------------------------------------------------------

#include "board.h"

#include <string.h>

//-----------------------------------------------------------------------------
//         Define
//-----------------------------------------------------------------------------
/// BMP offset for header
#define  IMAGE_OFFSET       0x100


//------------------------------------------------------------------------------
//         Internal constants
//------------------------------------------------------------------------------


//------------------------------------------------------------------------------
//         Internal types
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
/// Describe the BMP palette
//------------------------------------------------------------------------------
typedef struct _BMPPaletteEntry
{
    /// Blue value
    uint8_t b;
    /// Green value
    uint8_t g;
    /// Red value
    uint8_t r;
    /// Filler character value
    uint8_t filler;
} BMPPaletteEntry ;

//------------------------------------------------------------------------------
//         Exported functions
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
/// Test if BMP is valid
/// \param file  Buffer holding the file to examinate.
/// \return 1 if the header of a BMP file is valid; otherwise returns 0.
//------------------------------------------------------------------------------
uint8_t BMP_IsValid( void *file )
{
    return ((BMPHeader*) file)->type == BMP_TYPE ;
}

//------------------------------------------------------------------------------
/// Returns the size of a BMP image given at least its header (the file does
/// not have to be complete).
/// \param file  Pointer to the buffer which holds the BMP file.
/// \return size of BMP image
//------------------------------------------------------------------------------
uint32_t BMP_GetFileSize( void *file )
{
    return ((BMPHeader *) file)->fileSize ;
}

//-----------------------------------------------------------------------------
/// Write a BMP header
/// \param pAddressHeader Begin address of the BMP
/// \param bmpHSize BMP heigth size
/// \param bmpVSize BMP width size
/// \param nbByte_Pixels Number of byte per pixels
//-----------------------------------------------------------------------------
void WriteBMPheader( uint32_t* pAddressHeader, uint32_t  bmpHSize, uint32_t  bmpVSize, uint8_t nbByte_Pixels )
{
    uint32_t i;
    uint32_t* fill;
    BMPHeader *Header;

    fill = pAddressHeader;
    for ( i=0 ; i < IMAGE_OFFSET ; i+=4 )
    {
        *fill++ = 0;
    }

    Header = (BMPHeader*) pAddressHeader;

    Header->type = BMP_TYPE;
    Header->fileSize = (bmpHSize * bmpVSize * nbByte_Pixels) + IMAGE_OFFSET;
    Header->reserved1 = 0;
    Header->reserved2 = 0;
    Header->offset = IMAGE_OFFSET;
    Header->headerSize = BITMAPINFOHEADER;
    Header->width  = bmpHSize;
    Header->height = bmpVSize;
    Header->planes = 1;
    Header->bits = nbByte_Pixels * 8;
    Header->compression = 0;
    Header->imageSize = bmpHSize * bmpVSize * nbByte_Pixels;
    Header->xresolution = 0;
    Header->yresolution = 0;
    Header->ncolours = 0;
    Header->importantcolours = 0;
}


//------------------------------------------------------------------------------
/// debug function, dislay BMP header
/// \param pAddressHeader Address of the BMP
//------------------------------------------------------------------------------
#if (TRACE_LEVEL >= TRACE_LEVEL_INFO)
void BMP_displayHeader( uint32_t* pAddressHeader )
{
    BMPHeader *header;

    header = (BMPHeader*) pAddressHeader;

    TRACE_INFO("BMP\n\r");
    TRACE_INFO("type       0x%X \n\r", header->type);
    TRACE_INFO("fileSize   %d \n\r", (int)header->fileSize);
    TRACE_INFO("reserved1  %d \n\r", (int)header->reserved1);
    TRACE_INFO("reserved2  %d \n\r", (int)header->reserved2);
    TRACE_INFO("offset     %d \n\r", (int)header->offset);
    TRACE_INFO("headerSize %d \n\r", (int)header->headerSize);
    TRACE_INFO("width      %d \n\r", (int)header->width);
    TRACE_INFO("height     %d \n\r", (int)header->height);
    TRACE_INFO("planes     %d \n\r", (int)header->planes);
    TRACE_INFO("bits       %d \n\r", (int)header->bits);
    TRACE_INFO("compression %d \n\r", (int)header->compression);
    TRACE_INFO("imageSize   %d \n\r", (int)header->imageSize);
    TRACE_INFO("xresolution %d \n\r", (int)header->xresolution);
    TRACE_INFO("yresolution %d \n\r", (int)header->yresolution);
    TRACE_INFO("ncolours    %d \n\r", (int)header->ncolours);
    TRACE_INFO("importantcolours %d\n\r", (int)header->importantcolours);
}
#endif

//------------------------------------------------------------------------------
/// Loads a BMP image located at the given address, decodes it and stores the
/// resulting image inside the provided buffer. Image must have the specified
/// width & height.
/// If no buffer is provided, this function simply checks if it is able to
/// decode the image.
/// \param file  Buffer which holds the BMP file.
/// \param buffer  Buffer in which to store the decoded image.
/// \param width  Buffer width in pixels.
/// \param height  Buffer height in pixels.
/// \param bpp  Number of bits per pixels that the buffer stores.
/// \return 0 if the image has been loaded; otherwise returns an error code.
//------------------------------------------------------------------------------
uint8_t BMP_Decode( void *file, uint8_t *buffer, uint32_t width, uint32_t height, uint8_t bpp )
{
    BMPHeader *header;
    uint32_t i, j;
    uint8_t r, g, b;
    uint8_t *image;

    // Read header information
    header = (BMPHeader*) file;

    // Verify that the file is valid
    if ( !BMP_IsValid( file ) )
    {
        TRACE_ERROR("BMP_Decode: File type is not 'BM' (0x%04X).\n\r",header->type);

        return 1;
    }

    // Check that parameters match
    if ( (header->compression != 0) || (header->width != width) || (header->height != height))
    {
        TRACE_ERROR("BMP_Decode: File format not supported\n\r");
        TRACE_ERROR(" -> .compression = %u\n\r", (unsigned int)header->compression);
        TRACE_ERROR(" -> .width = %u\n\r", (unsigned int)header->width);
        TRACE_ERROR(" -> .height = %u\n\r", (unsigned int)header->height);
        TRACE_ERROR(" -> .bits = %d\n\r", (int)header->bits);

        return 2;
    }

    // Get image data
    image = (uint8_t *) ((uint32_t) file + header->offset);

    // Check that the bpp resolution is supported
    // Only a 24-bit output & 24- or 8-bit input are supported
    if ( bpp != 24 )
    {
        TRACE_ERROR("BMP_Decode: Output resolution not supported\n\r");

        return 3;
    }
    else
    {
        if (header->bits == 24)
        {
            // Decoding is ok
            if (!buffer) return 0;

            // Get image data (swapping red & blue)
            for ( i=0 ; i < height ; i++ )
            {
                for ( j=0 ; j < width; j++ )
                {
                    r = image[((height - i - 1) * width + j) * 3 + 2];
                    g = image[((height - i - 1) * width + j) * 3 + 1];
                    b = image[((height - i - 1) * width + j) * 3];

    #if defined(BOARD_LCD_RGB565)
                    // Interlacing
                    r = ((r << 1) & 0xF0) | ((g & 0x80) >> 4) | ((r & 0x80) >> 5);
                    g = (g << 1) & 0xF8;
                    b = b & 0xF8;

                    buffer[(i * width + j) * 3] = b;
                    buffer[(i * width + j) * 3 + 1] = g;
                    buffer[(i * width + j) * 3 + 2] = r;

    #else
                    buffer[(i * width + j) * 3] = r;
                    buffer[(i * width + j) * 3 + 1] = g;
                    buffer[(i * width + j) * 3 + 2] = b;
    #endif //#if defined(BOARD_LCD_RGB565)
                }
            }
        }
        else
        {
            if ( header->bits == 8 )
            {
                BMPPaletteEntry palette[256];

                // Decoding is ok
                if (!buffer) return 0;

                // Retrieve palette
                memcpy( palette, (uint8_t *) ((uint32_t) file + sizeof( BMPHeader )), header->offset - sizeof( BMPHeader ) ) ;

                // Decode image (reversing row order)
                for ( i=0 ; i < height ; i++ )
                {
                    for (j=0; j < width; j++)
                    {
                        r = palette[image[(height - i - 1) * width + j]].r;
                        g = palette[image[(height - i - 1) * width + j]].g;
                        b = palette[image[(height - i - 1) * width + j]].b;

                        buffer[(i * width + j) * 3] = r;
                        buffer[(i * width + j) * 3 + 1] = g;
                        buffer[(i * width + j) * 3 + 2] = b;
                    }
                }
            }
            else
            {

                TRACE_ERROR("BMP_Decode: Input resolution not supported\n\r");
                TRACE_INFO("header->bits 0x%X \n\r", header->bits);
                return 4 ;
            }
        }
    }

    return 0 ;
}

//------------------------------------------------------------------------------
/// Convert RGB 565 to RGB 555 (RGB 555 is adapted to LCD)
/// \param fileSource  Buffer which holds the RGB file
/// \param fileDestination  Buffer in which to store the decoded image
/// \param width  Buffer width in pixels.
/// \param height  Buffer height in pixels.
/// \param bpp  Number of bits per pixels that the buffer stores.
//------------------------------------------------------------------------------
void RGB565toBGR555( uint8_t *fileSource, uint8_t *fileDestination, uint32_t width, uint32_t height, uint8_t bpp )
{
    uint32_t i;
    uint32_t j;
    uint32_t row;

    for (i=0; i < height*(bpp/8); i++)
    {
        row = (i*width*(bpp/8));

        for (j=0; j <= width*(bpp/8); j+=2)
        {
            fileDestination[row+j] = ((fileSource[row+j+1]>>3)&0x1F)
                                    | (fileSource[row+j]&0xE0);
            fileDestination[row+j+1] = (fileSource[row+j+1]&0x03)
                                    | ((fileSource[row+j]&0x1F)<<2);
        }
    }
}
