/* ----------------------------------------------------------------------------
 *         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.
 * ----------------------------------------------------------------------------
 */

/** \file */

/*----------------------------------------------------------------------------
 *        Headers
 *----------------------------------------------------------------------------*/

#include "board.h"
#include <string.h>

/*----------------------------------------------------------------------------
 *        Definition
 *----------------------------------------------------------------------------*/

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

/*----------------------------------------------------------------------------
 *        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
 *----------------------------------------------------------------------------*/
/**
 * \brief 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 ;
}

/**
 * \brief 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 ;
}

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

    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;
}


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

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

/**
 * \brief 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", 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 )
            {
                // Decoding is ok
                if (!buffer) return 0;

                // Retrieve palette
                BMPPaletteEntry palette[256];
                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 ;
}

/**
 * \brief 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);
        }
    }
}
