/* The GIMP -- an image manipulation program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * 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.
 */

#include "config.h"

#include <gtk/gtk.h>

#include "libgimpcolor/gimpcolor.h"
#include "libgimpbase/gimpbase.h"

#include "gimpwidgetstypes.h"

#include "gimppreviewarea.h"


#define MIN_WIDTH  128
#define MIN_HEIGHT 128

static void     gimp_preview_area_class_init    (GimpPreviewAreaClass *klass);
static void     gimp_preview_area_init          (GimpPreviewArea      *area);

static void     gimp_preview_area_finalize      (GObject              *object);

static void     gimp_preview_area_size_allocate (GtkWidget            *widget,
                                                 GtkAllocation        *allocation);
static gboolean gimp_preview_area_expose        (GtkWidget            *widget,
                                                 GdkEventExpose       *event);

static GtkDrawingAreaClass *parent_class = NULL;

GType
gimp_preview_area_get_type (void)
{
  static GType view_type = 0;

  if (! view_type)
    {
      static const GTypeInfo view_info =
      {
        sizeof (GimpPreviewAreaClass),
        NULL,           /* base_init */
        NULL,           /* base_finalize */
        (GClassInitFunc) gimp_preview_area_class_init,
        NULL,           /* class_finalize */
        NULL,           /* class_data */
        sizeof (GimpPreviewArea),
        0,              /* n_preallocs */
        (GInstanceInitFunc) gimp_preview_area_init,
      };

      view_type = g_type_register_static (GTK_TYPE_DRAWING_AREA,
                                          "GimpPreviewArea",
                                          &view_info, 0);
    }

  return view_type;
}

static void
gimp_preview_area_class_init (GimpPreviewAreaClass *klass)
{
  GObjectClass   *object_class;
  GtkWidgetClass *widget_class;
  
  parent_class = g_type_class_peek_parent (klass);

  object_class = G_OBJECT_CLASS (klass);
  widget_class = GTK_WIDGET_CLASS (klass);

  object_class->finalize             = gimp_preview_area_finalize;

  widget_class->size_allocate        = gimp_preview_area_size_allocate;
  widget_class->expose_event         = gimp_preview_area_expose;

}

static void
gimp_preview_area_init (GimpPreviewArea *area)
{
  area->buf = NULL;
}

static void
gimp_preview_area_size_allocate (GtkWidget     *widget,
                                 GtkAllocation *allocation)
{
  GimpPreviewArea *area = GIMP_PREVIEW_AREA (widget);
  
  if (GTK_WIDGET_CLASS (parent_class)->size_allocate)
    GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);

  if (widget->allocation.width  != area->width ||
      widget->allocation.height != area->height)
    {
      area->width  = widget->allocation.width;
      area->height = widget->allocation.height;
      if ( area->buf == NULL )
        area->buf=g_new(guchar, area->width*area->height*3);
      else
        g_renew(guchar, area->buf, area->width*area->height*3);
    }
}         

static void
gimp_preview_area_finalize (GObject *object)
{
  GimpPreviewArea *area = GIMP_PREVIEW_AREA (object);

  if (area->buf)
    {
      g_free (area->buf);
      area->buf = NULL;
    }

  G_OBJECT_CLASS (parent_class)->finalize (object);
}
                                                                                                                                                    

static gboolean
gimp_preview_area_expose (GtkWidget      *widget,
                          GdkEventExpose *event)
{
  GimpPreviewArea *area ;
  gint             width, height;
  
  
  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GIMP_IS_PREVIEW_AREA (widget), FALSE);
  
  area = GIMP_PREVIEW_AREA (widget); 
  
  if (! area->buf || !GTK_WIDGET_DRAWABLE (widget))
    return FALSE;

  width  = widget->allocation.width;
  height = widget->allocation.height;

/* draw */
  gdk_draw_rgb_image (widget->window,
                      widget->style->base_gc[GTK_STATE_NORMAL],
                      0, 0, width, height,
                      GDK_RGB_DITHER_MAX,
                      area->buf,
                      width*3); 

  return FALSE;
}


GtkWidget *
gimp_preview_area_new ()
{
  GtkWidget *area = g_object_new (GIMP_TYPE_PREVIEW_AREA, NULL);

  return area;
}
/**
 * gimp_preview_area_from_buf:
 * @widget: Pointer to a #GtkWidget.
 * @buf: Pointer to a #guchar buffer that contains the preview data.
 * @x: column offset in buffer.
 * @y: row offset in buffer.
 * @width: Width of buffer in pixels.
 * @height: Height of buffer in pixels.
 * @bpp: Bytes per pixel
 *
 * Draw's buf on #GtkDrawingArea. 
 **/
void
gimp_preview_area_draw_buf (GtkWidget *widget, guchar *buf, gint x, gint y, gint width, gint height, gint bpp)
{
  guchar  *rgb_buf = NULL;
  guchar  *rgb_bufpos, *bufpos;
  gint     rgb_pos,		/* RGB buffer vars to generate checkerboard if needed */
           rgb_row, 
           rgb_col, 
           rgb_width, 
           rgb_height;
  gint     pos, col, row ; 
  gint     widget_width, widget_height;
  gint     check;
  GimpPreviewArea *area;
  
  area = GIMP_PREVIEW_AREA (widget);

  /* Check if buffer is allocated */
  g_return_if_fail (area->buf != NULL);
  
  
  if (! GTK_WIDGET_DRAWABLE (widget))
    return;

  /* Determine widget size */
  widget_width  = widget->allocation.width;
  widget_height = widget->allocation.height;
  
  /* substract the real drawing offset */
  rgb_width =   width - x;
  rgb_height =  height - y;  
    
  /* Don't go out of positive limmits */  
  rgb_width  = MIN (rgb_width,widget_width);
  rgb_height = MIN (rgb_height,widget_height);

  /* Stop if negative limmits */  
  g_return_if_fail (rgb_width > 0 && rgb_height > 0);  
   
  rgb_buf  = area->buf;

  /* Fill checkerboard if needed */
  if ( bpp == 2 || bpp == 4 )
    {
       for (rgb_row = 0 ; rgb_row <  widget_height ; rgb_row++)
         for (rgb_col = 0 ; rgb_col <  widget_width ; rgb_col++) 
           {
             rgb_pos = (rgb_row*rgb_width+rgb_col)*3;
             rgb_bufpos = rgb_buf+rgb_pos;
             if ((rgb_row & GIMP_CHECK_SIZE) ^ (rgb_col & GIMP_CHECK_SIZE))
               check = GIMP_CHECK_LIGHT * 255;
             else
               check = GIMP_CHECK_DARK * 255;
             *(rgb_bufpos)   = check;
             *(rgb_bufpos+1) = check;
             *(rgb_bufpos+2) = check;
           }  
    }
  
  /* Start in real buffer at x,y and  0,0 for preview buffer
   *
   *   Drawing            PreviewArea   Drawing            PreviewArea 
   *   0,0                0,0           0,0                0,0         
   *   +----------+       +--+--+       +----------+       +-+---+     
   *   |  3,1     |       |XX|  |       |          |       |X|   |     
   *   |  +--+    |       +--+  |       |        +--+      +-+   |     
   *   |  |XX|    |       |     |       |        |XX|      |     |     
   *   |  +--+    |       +-----+       |        +--+      +-----+     
   *   |          |                     |          |                   
   *   |          |                     |          |                   
   *   +----------+                     +----------+                   
   *    
   */  
  for ( row = y,rgb_row=0 ; row < height && rgb_row < rgb_height ; row++,rgb_row++)
    for ( col = x, rgb_col=0 ; col < width && rgb_col < rgb_width ; col++,rgb_col++ )
       {
          pos = (row*width+col)*bpp;
          bufpos = buf+pos;
          
          rgb_pos = (rgb_row*rgb_width+rgb_col)*3;
          rgb_bufpos = rgb_buf+rgb_pos;          
          
          switch (bpp)
            {
              case 1:
                *(rgb_bufpos)   = *(bufpos);
                *(rgb_bufpos+1) = *(bufpos);
                *(rgb_bufpos+2) = *(bufpos);
                break;
              case 2:
                if (*(buf+pos+1) == 255)
                  {
                    *(rgb_bufpos)   = *(bufpos);
                    *(rgb_bufpos+1) = *(bufpos);
                    *(rgb_bufpos+2) = *(bufpos);
                  }
                else if (*(buf+pos+1) != 0)
                  {
                    *(rgb_bufpos)   += ((*(bufpos) - *(rgb_bufpos)) * *(bufpos+1)) / 255;
                    *(rgb_bufpos+1)  = *(rgb_bufpos);
                    *(rgb_bufpos+2)  = *(rgb_bufpos);                      
                  }
                break;
              case 3:
                *(rgb_bufpos)   = *(bufpos);
                *(rgb_bufpos+1) = *(bufpos+1);
                *(rgb_bufpos+2) = *(bufpos+2);
                break;
              case 4:
                if (*(buf+pos+3) == 255)
                  {
                     *(rgb_bufpos)   = *(bufpos);
                     *(rgb_bufpos+1) = *(bufpos+1);
                     *(rgb_bufpos+2) = *(bufpos+2);
                  }
                else if (*(buf+pos+3) != 0)
                  {
                    *(rgb_bufpos)   += ((*(bufpos+0) - *(rgb_bufpos))   * *(bufpos+3)) / 255;
                    *(rgb_bufpos+1) += ((*(bufpos+1) - *(rgb_bufpos+1)) * *(bufpos+3)) / 255;
                    *(rgb_bufpos+2) += ((*(bufpos+2) - *(rgb_bufpos+2)) * *(bufpos+3)) / 255;                      
                  }
                break;
  	    }
        }      
   return;
}  
