/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "GLcommon/PaletteTexture.h"
#include <stdio.h>



struct Color
{
  Color(unsigned char r, unsigned char g,unsigned char b, unsigned char a):red(r),green(g),blue(b),alpha(a){};
  unsigned char red;
  unsigned char green;
  unsigned char blue;
  unsigned char alpha;
};

void getPaletteInfo(GLenum internalFormat,unsigned int& indexSizeBits,unsigned int& colorSizeBytes,GLenum& colorFrmt) {

        colorFrmt = GL_RGB;
        switch(internalFormat)
    {
    case GL_PALETTE4_RGB8_OES:
        indexSizeBits = 4;
        colorSizeBytes = 3;
        break;

    case GL_PALETTE4_RGBA8_OES:
        indexSizeBits = 4;
        colorSizeBytes = 4;
        colorFrmt = GL_RGBA;
        break;

    case GL_PALETTE4_RGBA4_OES:
    case GL_PALETTE4_RGB5_A1_OES:
        colorFrmt = GL_RGBA;
        /* fall-through */
    case GL_PALETTE4_R5_G6_B5_OES:
        indexSizeBits = 4;
        colorSizeBytes = 2;
        break;

    case GL_PALETTE8_RGB8_OES:
        indexSizeBits = 8;
        colorSizeBytes = 3;
        break;

    case GL_PALETTE8_RGBA8_OES:
        indexSizeBits = 8;
        colorSizeBytes = 4;
        colorFrmt = GL_RGBA;
        break;

    case GL_PALETTE8_RGBA4_OES:
    case GL_PALETTE8_RGB5_A1_OES:
        colorFrmt = GL_RGBA;
        /* fall-through */
    case GL_PALETTE8_R5_G6_B5_OES:
        indexSizeBits = 8;
        colorSizeBytes = 2;
        break;
    }
}


Color paletteColor(const unsigned char* pallete,unsigned int index,GLenum format)
{
        short s;
        switch(format) {
        //RGB
    case GL_PALETTE4_RGB8_OES:
    case GL_PALETTE8_RGB8_OES:
            return Color(pallete[index],pallete[index+1],pallete[index+2],0);
    case GL_PALETTE8_R5_G6_B5_OES:
    case GL_PALETTE4_R5_G6_B5_OES:
            s = *((short *)(pallete+index));
            return Color((s >> 11)*255/31,((s >> 5) & 0x3f)*255/63 ,(s & 0x1f)*255/31,0);

        //RGBA
    case GL_PALETTE4_RGBA8_OES:
    case GL_PALETTE8_RGBA8_OES:
            return Color(pallete[index],pallete[index+1],pallete[index+2],pallete[index+3]);
    case GL_PALETTE4_RGBA4_OES:
    case GL_PALETTE8_RGBA4_OES:
            s = *((short *)(pallete+index));
            return Color(((s >> 12) & 0xf)*255/15,((s >> 8) & 0xf)*255/15,((s >> 4) & 0xf)*255/15 ,(s & 0xf)*255/15);
    case GL_PALETTE4_RGB5_A1_OES:
    case GL_PALETTE8_RGB5_A1_OES:
            s = *((short *)(pallete+index));
            return Color(((s >> 11) & 0x1f)*255/31,((s >> 6) & 0x1f)*255/31,((s >> 1) & 0x1f)*255/31 ,(s & 0x1) * 255);
        default:
            return Color(255,255,255,255);
    }
}

unsigned char* uncompressTexture(GLenum internalformat,GLenum& formatOut,GLsizei width,GLsizei height,GLsizei imageSize, const GLvoid* data,GLint level) {

    unsigned int indexSizeBits;  //the size of the color index in the pallete
    unsigned int colorSizeBytes; //the size of each color cell in the pallete

    getPaletteInfo(internalformat,indexSizeBits,colorSizeBytes,formatOut);
    if(!data)
    {
        return NULL;
    }

    const unsigned char* palette = static_cast<const unsigned char *>(data);

    //the pallete positioned in the begininng of the data
    // so we jump over it to get to the colos indices in the palette

    int nColors = 2 << (indexSizeBits -1); //2^indexSizeBits
    int paletteSizeBytes = nColors*colorSizeBytes;
    const unsigned char* imageIndices =  palette + paletteSizeBytes;

    //jumping to the the correct mipmap level
    for(int i=0;i<level;i++) {
        imageIndices+= (width*height*indexSizeBits)/8;
        width  = width  >> 1;
        height = height >> 1;
    }

    int colorSizeOut = (formatOut == GL_RGB? 3:4);
    int nPixels = width*height;
    unsigned char* pixelsOut = new unsigned char[nPixels*colorSizeOut];
    if(!pixelsOut) return NULL;

    int leftBytes = ((palette + imageSize) /* the end of data pointer*/
                      - imageIndices);
    int leftPixels = (leftBytes * 8 )/indexSizeBits;

    int maxIndices = (leftPixels < nPixels) ? leftPixels:nPixels;

    //filling the pixels array
    for(int i =0 ; i < maxIndices ; i++) {
        int paletteIndex = 0;
        int indexOut = i*colorSizeOut;
        if(indexSizeBits == 4) {
            paletteIndex = (i%2) == 0 ?
                           imageIndices[i/2] >> 4:  //upper bits
                           imageIndices[i/2] & 0xf; //lower bits
        } else {
            paletteIndex = imageIndices[i];
        }

        paletteIndex*=colorSizeBytes;
        Color c = paletteColor(palette,paletteIndex,internalformat);

        pixelsOut[indexOut] = c.red;
        pixelsOut[indexOut+1] = c.green;
        pixelsOut[indexOut+2] = c.blue;
        if(formatOut == GL_RGBA) {
            pixelsOut[indexOut+3] = c.alpha;
        }
    }
    return pixelsOut;
}