//  Copyright (C) 2002 Ingo Ruhnke <grumbel@gmx.de>
//
//  This program is free software; you can redistribute it and/or
//  modify it under the terms of the GNU General Public License
//  as published by the Free Software Foundation; either version 2
//  of the License, or (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

/* To compile this programm:

    % gcc -o png2freecraft  png2freecraft.cc -lpng
 */

/* This programm can be used to fix the palette of a indexed png file
   to be suitable for freecraft. It works like this:

   1) You create a RGBA image in Gimp

   2) You convert it to indexed with 247 colors
   
   [Generate Optimal Palette # Colors 247]
   
   3) You run png2freecraft on the image

   4) The final images will be written to out.png in the current
      directory
 */

#include <string>
#include <vector>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <png.h>

class Color
{
public:
  int red;
  int green;
  int blue;

  Color ()
    : red (0), green (255), blue (0)
  {
  }

  Color (int r, int g, int b) 
    : red (r), green (g), blue (b)
  {    
  }
};

class Image
{
private:
  int m_width;
  int m_height;
  int m_transcol;
  std::vector<int> m_image;
  std::vector<Color> m_palette;
  
public:
  /** Load an image from a given png source */
  Image (const std::string& filename) 
  {
    FILE* fp;
    png_structp png_ptr;
    png_infop info_ptr;
    png_uint_32 pwidth, pheight;
    int bit_depth, color_type, interlace_type, compression_type, filter_type;
    int row_bytes;

    if ((fp = fopen(filename.c_str (), "rb")) == NULL)
      {
	perror (filename.c_str ());
	exit (EXIT_FAILURE);
      }

    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
				     NULL, NULL, NULL);
    info_ptr = png_create_info_struct(png_ptr);
    png_init_io(png_ptr, fp);
    png_read_info(png_ptr, info_ptr);
    png_get_IHDR(png_ptr, info_ptr, &pwidth, &pheight,
		 &bit_depth, &color_type, &interlace_type,
		 &compression_type, &filter_type);
    row_bytes = png_get_rowbytes(png_ptr, info_ptr);

    // Create the 'data' array
    png_bytep row_pointers[pheight];
    for (unsigned int i = 0; i < pheight; i++)
      row_pointers[i] = new png_byte[row_bytes];

    png_read_image(png_ptr, row_pointers);
    
    if (color_type != PNG_COLOR_TYPE_PALETTE)
	{
	  std::cout << "Unsupported color type" << std::endl;
	  exit (EXIT_FAILURE);
	}

    int num_colors;
    int num_trans = 0;
    png_colorp lpalette;
    png_bytep trans;
    png_color_16p trans_values;

    // Read some more data
    png_get_PLTE(png_ptr, info_ptr, &lpalette, &num_colors);
    png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values);

    // not sure what trans_values stand for

    //for (int i = 0; i < num_trans; ++i)
    //std::cout <<  "transcolors: " << trans_values[i] << std::endl;

    if (num_trans > 1)
      {
	std::cout << "Multiple transcolors not supported" << std::endl;
	exit (EXIT_FAILURE);
      }
    else if (num_trans == 1)
      m_transcol = trans[0];
    else
      m_transcol = -1;

    for (int i = 0; i < num_trans; i++)
      std::cout << "transcolor: " << int(trans[i]) << std::endl;
    
    m_width = pwidth;
    m_height = pheight;

    m_image.resize (m_width * m_height);

    // Convert the png into our internal data structure
    for (int y = 0; y < m_height; y++)
      {
	for (int i = 0; i < row_bytes; i++)
	  {
	    m_image[i + (m_width * y)] = row_pointers[y][i];
	  }
      }

    assert (num_colors <= 256);

    if (num_colors > 248)
      {
	std::cout << "Image has more than 248 colors (" << num_colors << "), bailout" << std::endl;
	exit (EXIT_FAILURE);
      }

    m_palette.resize (256);
    for (int i = 0; i < num_colors; ++i)
      {
	m_palette[i].red = lpalette[i].red;
	m_palette[i].green = lpalette[i].green;
	m_palette[i].blue = lpalette[i].blue;
      }

    png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
    fclose (fp);
  }
  
  ~Image () 
  {
  }

  void write_png (std::string filename) 
  {
    FILE* fp;
    png_structp png_ptr;
    png_infop info_ptr;
    png_colorp palette;
    //png_uint_32 bytes_per_pixel = 1;

   fp = fopen(filename.c_str (), "wb");
   if (fp == NULL)
     assert (false);

   png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
				    NULL, NULL, NULL);

   png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
   info_ptr = png_create_info_struct(png_ptr);
   png_init_io(png_ptr, fp);

   png_set_IHDR(png_ptr, info_ptr, m_width, m_height, 8 /* bitdepth */,
		PNG_COLOR_TYPE_PALETTE,
		PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

   palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH
				    * sizeof (png_color));

   std::cout << "MAX: " << PNG_MAX_PALETTE_LENGTH << std::endl;
   for (unsigned int i = 0; i < m_palette.size(); ++i)
     {
       palette[i].red   = m_palette[i].red;
       palette[i].green = m_palette[i].green;
       palette[i].blue  = m_palette[i].blue;
     }
   
   /** insert palette converter here */
   png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH);

   png_write_info(png_ptr, info_ptr);


   png_uint_32 height = m_height, width = m_width;
   png_byte image[height * width /* *bytes_per_pixel */];
   png_bytep row_pointers[height];
   
   // fill the image with data
   for (unsigned int i = 0; i < m_image.size (); ++i)
     {
       image[i] = m_image[i];
     }

   for (unsigned int k = 0; k < height; k++)
     row_pointers[k] = image + k * width /* * bytes_per_pixel*/;

   png_write_image(png_ptr, row_pointers);

   png_write_end(png_ptr, info_ptr);
   png_free(png_ptr, palette);
   //png_free(png_ptr, trans);
   png_destroy_write_struct(&png_ptr, &info_ptr);
   fclose(fp);
  }

  /** swaps color a and color b in both the palette and the image */
  void swap_colors (int a, int b) 
  {
    std::swap (m_palette[a], m_palette[b]);

    for (std::vector<int>::iterator i = m_image.begin (); i != m_image.end (); ++i)
      {
	if (*i == a)
	  *i = b;
	else if (*i == b)
	  *i = a;
      }
  }

  void set_color (int i, Color c) {
    m_palette[i] = c;
  }

  int get_transcolor () {
    return m_transcol;
  }
  
  int num_colors () {
    return m_palette.size ();
  }

  int get_width () {
    return m_width;
  }
  
  int get_height () {
    return m_height;
  }
};

int main (int argc, char* argv[])
{
  if (argc != 3)
    {
      printf ("Usage: %s INFILE OUTFILE\n", argv[0]);
      exit (EXIT_FAILURE);
    }
  else
    {
      Image mypng (argv[1]);
      std::cout << "Width: " << mypng.get_width ()
		<< " Height: " << mypng.get_height ()
		<< " Colors: " << mypng.num_colors () << std::endl;

      /*
	#0          general background (RGB = 0,0,0)

	#208        replaced by player color 1
	#209        replaced by player color 2
	#210        replaced by player color 3
	#211        replaced by player color 4
	
	#253        general editors color (should NEVER be used in any image, RGB = 255,50,255)
	
	#255        general transparency color (RGB, 255,255,255)
      */

      if (mypng.get_transcolor () != -1)
	mypng.swap_colors (mypng.get_transcolor (), 255);

      // Swap all colors to un used palette positions
      mypng.swap_colors (0, 254);
      mypng.swap_colors (208, 252);
      mypng.swap_colors (209, 251);
      mypng.swap_colors (210, 250);
      mypng.swap_colors (211, 249);
      
      // This are in a range which shouldn't get touched, so changing
      //them might not be needed
      //mypng.swap_colors (253, 250);
      //mypng.swap_colors (255, 249);

      // Set all hard-coded colors to some usefull defaults for preview
      mypng.set_color (0, Color (0, 0, 0));

      mypng.set_color (208, Color ( 60,  60, 0));
      mypng.set_color (209, Color (110, 110, 0));
      mypng.set_color (210, Color (200, 200, 0));
      mypng.set_color (211, Color (255, 255, 0));

      mypng.set_color (253, Color (255, 50, 255));
      mypng.set_color (255, Color (255, 255, 255));

      mypng.write_png (argv[2]);
    }
}


// EOF //

