/**********************************************************************
 *
 * File :     palette.c
 *
 * Abstract : Some RenderWare palette handling functions
 *
 **********************************************************************
 *
 * This file is a product of Criterion Software Ltd.
 *
 * This file is provided as is with no warranties of any kind and is
 * provided without any obligation on Criterion Software Ltd. or
 * Canon Inc. to assist in its use or modification.
 *
 * Criterion Software Ltd. will not, under any
 * circumstances, be liable for any lost revenue or other damages arising
 * from the use of this file.
 *
 * Copyright (c) 1995 Criterion Software Ltd.
 * All Rights Reserved.
 *
 * RenderWare is a trademark of Canon Inc.
 *
 ************************************************************************/

/**********************************************************************
 *
 * Header files.
 *
 **********************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "rwlib.h"
#include "palette.h"

/**********************************************************************
 *
 * Constant definitions.
 *
 **********************************************************************/

/* Maximum line length when parsing a file. */

#define MAXLINELEN           80

/* Palette file strings. */

#define PALETTEIDENTSTRING   "JASC-PAL"
#define PALETTEVERSIONSTRING "0100"

/**********************************************************************
 *
 * Functions.
 *
 **********************************************************************/

/************************************************************************
 *
 *      Function:       ReadPalette()
 *                      
 *      Description:    Read a palette file into an array
 *
 *      Parameters:     filename - name of file to read.
 *                      palette - array of RwPalleteEntry to store the
 *                                read palette.
 *                      palette_size - number of elements in 'palette' array
 *
 *      Return Value:   Returns number of entries read or 0 on error
 *                      palette contains palette.
 *
 ************************************************************************/

int ReadPalette(char *fileName, RwPaletteEntry *palette, int palette_size)
{
    FILE          *file;
    char          buffer[MAXLINELEN];
    int           i;
    unsigned int  red;
    unsigned int  green;
    unsigned int  blue;
	int 		  numEntries;

    /* Ensure the given palette is valid. */

    if (!palette)
        return(0);

    /* Open the input file. */
    if (!(file = fopen(fileName, "r")))
        return(0);

    /* Read the file ident string. */

    if (fgets(buffer, sizeof(buffer), file) == NULL)
    {
        fclose(file);
        return(0);
    }

    /* Ensure it matches the palette file ident string. */

    if (strncmp(buffer, PALETTEIDENTSTRING, sizeof(PALETTEIDENTSTRING) - 1) != 0)
    {
        fclose(file);
        return(0);
    }

    /* Read the version string. */    

    if (fgets(buffer, sizeof(buffer), file) == NULL)
    {
        fclose(file);
        return(0);
    }

    /* Ensure it matches the palette file version string. */

    if (strncmp(buffer, PALETTEVERSIONSTRING, sizeof(PALETTEVERSIONSTRING) - 1) != 0)
    {
        fclose(file);
        return(0);
    }

    /* Read the number of colors. */
    if (fgets(buffer, sizeof(buffer), file) == NULL)
    {
        fclose(file);
        return(0);
    }

    /* Extract the number of colors. */

    if (sscanf(buffer, "%d", &numEntries) != 1)
    {
        fclose(file);
        return (0);
    }

    /* Ensure there are a sensible number of colors in the palette. */
    if ((numEntries < 0) || (numEntries > 256) || (numEntries > palette_size))
    {
        fclose(file);
        return 0;
    }

    /* Read the palette entries. */

    for (i = 0; i < numEntries; i++)
    {
        /* Extract the red, green and blue color components. */

        if (fscanf(file, "%u %u %u", &red, &green, &blue) != 3)
        {
            fclose(file);
            return(0);
        }

        /* Store this palette entry. */

        palette[i].r = (unsigned char)red;
        palette[i].g = (unsigned char)green;
        palette[i].b = (unsigned char)blue;
    }

    /*
     * The palette was read successfully - close the file and return
     * the new palette.
     */

    fclose(file);

    return numEntries;
}

/************************************************************************
 *
 *      Function:       WritePalette()
 *                      
 *      Description:    Write a palette into a file
 *
 *      Parameters:     filename - name of file to write.
 *                      palette - array of RwPalleteEntry to write
 *                      palette_size - number of elements in 'palette' array
 *
 *      Return Value:   TRUE on success. FALSE on failure
 *
 ************************************************************************/
int WritePalette(char *fileName, RwPaletteEntry *palette, int palette_size )
{
    FILE *file;
    int   i;

    /* Ensure a valid palette has been given. */
    if (palette == NULL)
        return FALSE;

    /* Open the output file. */
    file = fopen(fileName, "w");
    if (file == NULL)
        return FALSE;

    /* Output the file ident string. */

    if (fprintf(file, "%s\n", PALETTEIDENTSTRING) < 0)
    {
        fclose(file);
            return FALSE;
    }

    /* Output the file version string. */

    if (fprintf(file, "%s\n", PALETTEVERSIONSTRING) < 0)
    {
        fclose(file);
            return FALSE;
    }

    /* Output the number of colors. */

    if (fprintf(file, "%d\n", palette_size) < 0)
    {
        fclose(file);
            return FALSE;
    }

    /* Output each palette entry in turn */
    for (i = 0; i < palette_size; i++)
    {
        /* Output a single palette entry. */

        if (fprintf(file, "%u %u %u\n",
                          (unsigned int)palette[i].r,
                          (unsigned int)palette[i].g,
                          (unsigned int)palette[i].b) < 0)
        {
            fclose(file);
                return FALSE;
        }
    }

    /* All went well, so close the file and return the palette. */

    fclose(file);

    return TRUE;
}

/************************************************************************
 *
 *      Function:       BuildColorCube()
 *                      
 *      Description:    Construct a color cube (with the given number of 
 *                      bits for red, green and blue) in the given palette 
 *                      starting at the given offset.
 *
 *      Parameters:     palette - color cube returned here.
 *                      palette_size - number of elements in 'palette' array
 *                      redBits, greenBits, blueBits - number of bits
 *                      of Red, Green and Blue respectively to use when
 *                      constructing the color cube
 *
 *      Return Value:   TRUE on success. FALSE on failure
 *
 ************************************************************************/
int 
BuildColorCube(RwPaletteEntry *palette, int palette_size,
                 int redBits, int greenBits, int blueBits)
{
    int             numReds;
    int             numGreens;
    int             numBlues;
    int             red;
    int             green;
    int             blue;

    /* Ensure the palette is valid. */        

    if (palette == NULL)
        return FALSE;

    /*
     * Compute the number of reds, greens and blues given the number
     * of bits specified.
     */

    numReds   = 1 << redBits;
    numGreens = 1 << greenBits;
    numBlues  = 1 << blueBits;

    /*
     * Ensure that the number of colors in the color cube will fit in
     * the number of entries available in the palette.
     */

    if (numReds * numGreens * numBlues > palette_size)
        return FALSE;

    /*
     * Generate the color cube.
     */    

    for (red = 0; red < numReds; red++)
    {
        for (green = 0; green < numGreens; green++)
        {
            for (blue = 0; blue < numBlues; blue++)
            {
                palette->r = (unsigned char)(((double)red   / (double)numReds) * 255.0);
                palette->g = (unsigned char)(((double)green / (double)numGreens) * 255.0);
                palette->b = (unsigned char)(((double)blue  / (double)numBlues)  * 255.0);
                palette++;
            }
        }
    }

    /*
     * All is well so return success.
     */
    return TRUE;
}

/************************************************************************
 *
 *      Function:       CheckAndReadPalette()
 *                      
 *      Description:    If we are running on a palette based device then
 *                      load the specified palette. The first 'n' palette
 *                      entries will be loaded if the total number is
 *                      greater that the number available.
 *
 ************************************************************************/
void CheckAndReadPalette(char *filename)
{
    RwInt32 PaletteBased;

    /* Are we running on a palette based device */

    RwGetDeviceInfo(rwPALETTEBASED, &PaletteBased, sizeof(RwInt32));

    if (PaletteBased)
    {
        /* Yes */
        RwInt32 firstEntry;
        RwInt32 lastEntry;
	RwPaletteEntry palette[256];
	RwInt32 numEntries;

        /* find the range of available entries */
  	RwGetDeviceInfo(rwFIRSTPALETTEENTRY, &firstEntry, sizeof(firstEntry));
  	RwGetDeviceInfo(rwLASTPALETTEENTRY, &lastEntry, sizeof(lastEntry));
 
        /* Read the palette */
	numEntries = (RwInt32)ReadPalette(filename, palette, sizeof(palette));

        /* clamp the number of entries that we can set */

        if (numEntries > lastEntry + 1 - firstEntry)
            numEntries = lastEntry + 1 - firstEntry;

        /* Set the RenderWare palette */
        RwSetPaletteEntries(firstEntry, numEntries, palette, (RwInt32)0);
    }
}

/**********************************************************************
 *
 * End of file.
 *
 **********************************************************************/
