/*
  fill.c

  Fill Magic Tool Plugin
  Tux Paint - A simple drawing program for children.

  Copyright (c) 2002-2008 by Bill Kendrick and others; see AUTHORS.txt
  bill@newbreedsoftware.com
  http://www.tuxpaint.org/

  Flood fill code based on Wikipedia example:
  http://www.wikipedia.org/wiki/Flood_fill/C_example
  by Damian Yerrick - http://www.wikipedia.org/wiki/Damian_Yerrick

  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
  (See COPYING.txt)

  Last updated: July 8, 2008
  $Id: fill.c,v 1.11 2008/07/10 20:26:39 wkendrick Exp $
*/

#include <stdio.h>
#include <string.h>
#include "tp_magic_api.h"
#include "SDL_image.h"


/* Our globals: */

static Mix_Chunk * fill_snd;
static Uint8 fill_r, fill_g, fill_b;

/* Local function prototypes: */

static int colors_close(magic_api * api, SDL_Surface * canvas,
			Uint32 c1, Uint32 c2);
static void do_flood_fill(magic_api * api, SDL_Surface * canvas, int x, int y,
                   Uint32 cur_colr, Uint32 old_colr);


// No setup required:
int fill_init(magic_api * api)
{
  char fname[1024];

  snprintf(fname, sizeof(fname), "%s/sounds/magic/fill.wav",
	    api->data_directory);
  fill_snd = Mix_LoadWAV(fname);

  return(1);
}

Uint32 fill_api_version(void) { return(TP_MAGIC_API_VERSION); }

// We have multiple tools:
int fill_get_tool_count(magic_api * api)
{
  return(1);
}

// Load our icons:
SDL_Surface * fill_get_icon(magic_api * api, int which)
{
  char fname[1024];

  snprintf(fname, sizeof(fname), "%s/images/magic/fill.png",
	   api->data_directory);

  return(IMG_Load(fname));
}

// Return our names, localized:
char * fill_get_name(magic_api * api, int which)
{
  return(strdup(gettext_noop("Fill")));
}

// Return our descriptions, localized:
char * fill_get_description(magic_api * api, int which, int mode)
{
  return(strdup(gettext_noop(
"Click in the picture to fill that area with color.")));
}


// Affect the canvas on drag:
void fill_drag(magic_api * api, int which, SDL_Surface * canvas,
	          SDL_Surface * last, int ox, int oy, int x, int y,
                  SDL_Rect * update_rect)
{
}

// Affect the canvas on click:
void fill_click(magic_api * api, int which, int mode,
	           SDL_Surface * canvas, SDL_Surface * last,
	           int x, int y, SDL_Rect * update_rect)
{
  do_flood_fill(api, canvas, x, y, SDL_MapRGB(canvas->format,
                                              fill_r, fill_g, fill_b),
                api->getpixel(canvas, x, y));

  update_rect->x = 0;
  update_rect->y = 0;
  update_rect->w = canvas->w;
  update_rect->h = canvas->h;
}

void fill_release(magic_api * api, int which,
	           SDL_Surface * canvas, SDL_Surface * last,
	           int x, int y, SDL_Rect * update_rect)
{
}

void fill_shutdown(magic_api * api)
{
  Mix_FreeChunk(fill_snd);
}

// Record the color from Tux Paint:
void fill_set_color(magic_api * api, Uint8 r, Uint8 g, Uint8 b)
{
  fill_r = r;
  fill_g = g;
  fill_b = b;
}

// Use colors:
int fill_requires_colors(magic_api * api, int which)
{
  return 1;
}


static int colors_close(magic_api * api, SDL_Surface * canvas,
			Uint32 c1, Uint32 c2)
{
  Uint8 r1, g1, b1, r2, g2, b2;

  if (c1 == c2)
  {
    /* Get it over with quick, if possible! */

    return 1;
  }
  else
  {
    double r, g, b;
    SDL_GetRGB(c1, canvas->format, &r1, &g1, &b1);
    SDL_GetRGB(c2, canvas->format, &r2, &g2, &b2);

    // use distance in linear RGB space
    r = api->sRGB_to_linear(r1) - api->sRGB_to_linear(r2);
    r *= r;
    g = api->sRGB_to_linear(g1) - api->sRGB_to_linear(g2);
    g *= g;
    b = api->sRGB_to_linear(b1) - api->sRGB_to_linear(b2);
    b *= b;

    // easy to confuse:
    //   dark grey, brown, purple
    //   light grey, tan
    //   red, orange
    return r + g + b < 0.04;
  }
}


static void do_flood_fill(magic_api * api, SDL_Surface * canvas, int x, int y,
                   Uint32 cur_colr, Uint32 old_colr)
{
  int fillL, fillR, i, in_line;
  static unsigned char prog_anim;


  if (cur_colr == old_colr || colors_close(api, canvas, cur_colr, old_colr))
    return;


  fillL = x;
  fillR = x;

  prog_anim++;
  if ((prog_anim % 4) == 0)
  {
    api->update_progress_bar();
    api->playsound(fill_snd, (x * 255) / canvas->w, 255);
  }


  /* Find left side, filling along the way */

  in_line = 1;

  while (in_line)
  {
    api->putpixel(canvas, fillL, y, cur_colr);
    fillL--;

    in_line =
      (fillL < 0) ? 0 : colors_close(api, canvas,
				     api->getpixel(canvas, fillL, y),
                                     old_colr);
  }

  fillL++;

  /* Find right side, filling along the way */

  in_line = 1;
  while (in_line)
  {
    api->putpixel(canvas, fillR, y, cur_colr);
    fillR++;

    in_line = (fillR >= canvas->w) ? 0 : colors_close(api, canvas,
					   api->getpixel(canvas, fillR, y),
                                                      old_colr);
  }

  fillR--;


  /* Search top and bottom */

  for (i = fillL; i <= fillR; i++)
  {
    if (y > 0 && colors_close(api, canvas, api->getpixel(canvas, i, y - 1),
			      old_colr))
      do_flood_fill(api, canvas, i, y - 1, cur_colr, old_colr);

    if (y < canvas->h
        && colors_close(api, canvas, api->getpixel(canvas, i, y + 1), old_colr))
      do_flood_fill(api, canvas, i, y + 1, cur_colr, old_colr);
  }
}

void fill_switchin(magic_api * api, int which, int mode, SDL_Surface * canvas)
{
}

void fill_switchout(magic_api * api, int which, int mode, SDL_Surface * canvas)
{
}

int fill_modes(magic_api * api, int which)
{
  return(MODE_PAINT);
}
