/*
 *  TnImage.h
 *
 *  Created by Thant on 10/1/2010
 *  Copyright 2009 TeleNav Inc. All rights reserved.
 *
 */

#ifndef TN_IMAGE_H
#define TN_IMAGE_H

#include <utility>
#include <foundation/SharedPtr.h>
#include <foundation/SharedArray.h>

#include <foundation/TnTypes.h>

#include <foundation/Math/ColorBytes.hpp>
#include <foundation/Math/Array2.hpp>

#include <RenderInterface/TnRenderInterface.h>

int32_t TnComponentCount(Tn::Graphics::eColorFormat format);


/*
 * TODO: Convert all this over to Array2<ColorBytes<I> >. For now,
 * provide a build function that converts from TnBitmap to
 * Array2<ColorBytes<I> >.
 *
 */


class TnBitmap
{
public:

    /* Raw bitmap, plus how to interpret it. */
    typedef Tn::Foundation::shared_ptr<TnBitmap> ptr;

    TnBitmap(Tn::Foundation::shared_array<uint8_t> const & bytes_,
             uint32_t size_,
             uint32_t width_,
             uint32_t height_,
             Tn::Graphics::eColorFormat format_,
             std::vector<size_t> const & mipOffset_ = std::vector<size_t>(1, 0))
        : bytes(bytes_),
          size(size_),
          width(width_),
          height(height_),
          format(format_),
          mipOffset(mipOffset_){}

    // Copy from raw data
    TnBitmap(void const * data,
             uint32_t size,
             uint32_t width,
             uint32_t height,
             Tn::Graphics::eColorFormat format,
             std::vector<size_t> const & mipOffset = std::vector<size_t>(1, 0));

    TnBitmap() : size(0),
                 width(0),
                 height(0),
                 format(Tn::Graphics::eColorFormat(-1)),
                 mipOffset(std::vector<size_t>(1, 0)) {}
    
    bool FillSub(std::vector<std::vector<uint8_t> > & dst,
                 int32_t origin_x = 0,
                 int32_t origin_y = 0,
                 uint32_t width_ = 0,
                 uint32_t height_ = 0);

    Tn::Foundation::shared_array<uint8_t> bytes;
    uint32_t size;
    uint32_t width;
    uint32_t height;
    Tn::Graphics::eColorFormat format;
    std::vector<size_t> mipOffset;
};


enum eTnImageStatus
{
    eTnImageStatus_Success,
    eTnImageStatus_Pending,
    eTnImageStatus_OutOfMemory,
    eTnImageStatus_UnsupportedFormat,
    eTnImageStatus_UnsupportedAttrib,
    eTnImageStatus_InternalError,
    eTnImageStatus_InvalidValue
};

bool IsCompressedFormat(Tn::Graphics::eColorFormat format);

std::pair<eTnImageStatus, std::pair<Tn::Foundation::shared_array<uint8_t>, size_t> > TnEncodeBMP(TnBitmap const & source);

std::pair<eTnImageStatus,TnBitmap::ptr> TnDecodePNG(Tn::Foundation::shared_array<uint8_t> const & source, size_t size, bool forceGrayToRGB = false);
std::pair<eTnImageStatus,TnBitmap::ptr> TnDecodeJPG(Tn::Foundation::shared_array<uint8_t> const & source, size_t size);
std::pair<eTnImageStatus,TnBitmap::ptr> TnDecodeBMP(Tn::Foundation::shared_array<uint8_t> const & source, size_t size);
std::pair<eTnImageStatus,TnBitmap::ptr> TnDecodeImage(Tn::Foundation::shared_array<uint8_t> const & source, size_t size, bool forceGrayToRGB = false);
std::pair<eTnImageStatus,TnBitmap::ptr> TnDecodeDDS(Tn::Foundation::shared_array<uint8_t> const & source, size_t size);
std::pair<eTnImageStatus,TnBitmap::ptr> TnDecodePVRTC(Tn::Foundation::shared_array<uint8_t> const & source, size_t size);


/*! Returns an index corresponding to the offset from the beginning of
 *  the bitmap. Note it does not return the actual contents of the
 *  byte array, merely the index into the byte array. */

uint32_t TnBitmapIndex(TnBitmap const & bitmap, size_t column, size_t row, size_t channel);

// Destination coordinates are allowed to lie outside source
// image. Pixels outside the source image will be zeroed.
std::pair<eTnImageStatus,TnBitmap::ptr> TnSubImage(TnBitmap::ptr const & source, int32_t x, int32_t y, int32_t w, int32_t h);

std::pair<eTnImageStatus,TnBitmap::ptr> TnConvolve(TnBitmap::ptr const & source, TnBitmap::ptr const & kernel);

void TnSetChannel(TnBitmap&, size_t channel, uint8_t value);

// For creating the convolve filter for the glyph halos. This might not belong here.
TnBitmap::ptr TnGenerateKernel(uint32_t width, uint32_t height, Tn::Graphics::eColorFormat format);

std::pair<eTnImageStatus, TnBitmap::ptr> TnGenerateHaloBackground(TnBitmap const & original, size_t halo_size);

template <int I>
typename Tn::math::Array2<Tn::math::ColorBytes<I> >::ptr TnBuildImage(TnBitmap const & bitmap)
{
    using namespace Tn::math;
    typename Array2<ColorBytes<I> >::ptr result = 
        Array2<ColorBytes<I> >::ptr(new Array2<ColorBytes<I> >(bitmap.width, bitmap.height));

    int32_t channels = TnComponentCount(bitmap.format);
    int32_t size = bitmap.width * bitmap.height;
    uint8_t const * src = &bitmap.bytes[0];
    ColorBytes<I>* dest = result[0];

    if (TnComponentCount(bitmap.format)>=I)
    {
        for (int j = 0; j < size; ++j)
        {
            for (int i = 0; i < I; ++i)
            {
                (*dest)[i] = src[i];
            }
            ++dest;
            src += channels;
        }
    }
    else 
    {
        for (int j = 0; j < size; ++j)
        {
            for (int i = 0; i < channels; ++i)
            {
                (*dest)[i] = src[i];
            }
            for (int i = channels; i < I; ++i)
            {
                (*dest)[i] = 0;
            }

            ++dest;
            src += channels;
        }
    }

    return result;
}


template <int I>
typename Tn::math::Array2<Tn::math::ColorBytes<I> >::ptr TnBuildImage(int32_t width, int32_t height, void const * raw_data, int32_t channels)
{
    using namespace Tn::math;

    uint8_t const * data = reinterpret_cast<uint8_t const *>(raw_data);

    typename Array2<ColorBytes<I> >::ptr result = 
        typename Array2<ColorBytes<I> >::ptr(new Array2<ColorBytes<I> >(width, height));

    int32_t size = width * height;
    uint8_t const * src = data;
    ColorBytes<I>* dest = (*result)[0];

    if (channels>=I)
    {
        for (int j = 0; j < size; ++j)
        {
            for (int i = 0; i < I; ++i)
            {
                (*dest)[i] = src[i];
            }
            ++dest;
            src += channels;
        }
    }
    else 
    {
        for (int j = 0; j < size; ++j)
        {
            for (int i = 0; i < channels; ++i)
            {
                (*dest)[i] = src[i];
            }
            for (int i = channels; i < I; ++i)
            {
                (*dest)[i] = 0;
            }

            ++dest;
            src += channels;
        }
    }

    return result;
}

#endif
