/* -*- mode:C; indent-tabs-mode:t; tab-width:8; c-basic-offset:2; -*- */

/* gnome-stones - main.c
 *
 * Time-stamp: <1999/01/18 18:50:24 carsten>
 *
 * Copyright (C) 1998, 1999 Carsten Schaar
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "main.h"
#include "view.h"
#include "object.h"
#include "types.h"
#include "preferences.h"

/*****************************************************************************/
/* Some declarations */


gint view_expose_event_cb (GtkWidget *widget, GdkEventExpose *event, 
			   gpointer data);
static gint view_expose (GtkWidget *widget, GdkEventExpose *event);

extern GStonesCave *cave;


/*****************************************************************************/
/* Stuff that is needed for the GTK* type system */


static void
view_class_init (GStonesViewClass *class)
{
  GtkWidgetClass *widget_class;
 
  widget_class= (GtkWidgetClass *) class;



#ifndef USE_GNOME_CANVAS
  widget_class->expose_event= view_expose;
#endif
}



static void
view_init (GStonesView *view)
{
  view->display_mode        = DISPLAY_IMAGE;

  /* cave display */
  view->x_offset            = 0;
  view->y_offset            = 0;
  view->x_scrolling         = TRUE;
  view->y_scrolling         = TRUE;

  /* image display */
  view->image               = NULL;
  
  /* curtain display */
  view->curtain_display_mode= CURTAIN_DISPLAY_NONE;
  view->curtain_timeout     = 0;
  view->curtain             = 0;
}


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

  if (!view_type)
    {
      GTypeInfo view_info=
      {
	sizeof (GStonesViewClass),

        NULL,
        NULL,

	(GClassInitFunc) view_class_init,
        NULL,
        NULL,

	sizeof (GStonesView),
        0,
	(GInstanceInitFunc) view_init,
        
	NULL
      };
      
      view_type= g_type_register_static (gtk_drawing_area_get_type (), 
                                         "GStonesView", 
                                         &view_info,
                                         0 );
    }
  
  return view_type;
} 
  

/*****************************************************************************/


GtkWidget *
view_new (GdkPixbuf *curtain_image)
{
  GStonesView    *view;
#ifdef USE_GNOME_CANVAS
  GnomeCanvas    *canvas;
#else
  GtkDrawingArea *drawing_area;
#endif
  GtkWidget      *widget;
  GtkObject      *object;

  g_return_val_if_fail (curtain_image, NULL);

  gtk_widget_push_colormap (gdk_rgb_get_colormap ());

  view= g_object_new (view_get_type (), NULL);
  
  gtk_widget_pop_colormap ();

#ifdef USE_GNOME_CANVAS
  canvas      = GNOME_CANVAS (view);
#else
  drawing_area= GTK_DRAWING_AREA (view);
#endif
  widget      = GTK_WIDGET (view);
  object      = GTK_OBJECT (view);

#ifndef USE_GNOME_CANVAS

  gtk_widget_set_events (widget, gtk_widget_get_events (widget) | GAME_EVENTS);
  
  gtk_widget_set_size_request (GTK_WIDGET (drawing_area), 
                               GAME_COLS * STONE_SIZE,
                               GAME_ROWS * STONE_SIZE);

  gtk_widget_show (widget);

#else

  gtk_widget_push_colormap (gdk_rgb_get_colormap ());

  view->canvas= gtk_drawing_area_new ();
  canvas= gnome_canvas_new ();

  gtk_widget_pop_colormap ();
  
  gtk_widget_set_events (view->canvas, gtk_widget_get_events (view->canvas) | GAME_EVENTS);

  gtk_drawing_area_size (GTK_DRAWING_AREA (view->canvas),
			 GAME_COLS * STONE_SIZE,
			 GAME_ROWS * STONE_SIZE);

  /* Now for some experimantal gnome canvas stuff.  */
  gtk_widget_set_usize (canvas, GAME_COLS*STONE_SIZE, GAME_ROWS*STONE_SIZE);
  gnome_canvas_set_scroll_region (GNOME_CANVAS (canvas),
				  0, 0,
				  GAME_COLS*STONE_SIZE,
				  GAME_ROWS*STONE_SIZE);

  gtk_widget_show (canvas);

  gtk_widget_show (view->canvas);
#endif


  view->view_buffer = NULL;

  /* Initialize curtain stuff.  */
  gdk_pixbuf_render_pixmap_and_mask (curtain_image, &view->curtain_image, NULL, 127);
  
  view->curtain_display_mode= CURTAIN_DISPLAY_CLOSING;
  view->curtain             = 0;

  return GTK_WIDGET (view);
}



/*****************************************************************************/

/* we want some cute cutain :-)*/

#define CURTAIN_START_VALUE 31

static gboolean
curtain_visible_test (int x, int y, int curtain)
{
  int a = (2 * x * (x + y) + 3 * y * GAME_COLS);
  return ((a*a) % CURTAIN_START_VALUE)
    * CURTAIN_START_VALUE * CURTAIN_START_VALUE
    >= curtain * curtain * curtain;
}


/*****************************************************************************/



static gint
view_expose (GtkWidget *widget, GdkEventExpose *event)
{
  GStonesView  *view;
  GdkRectangle *area;
  int x1, y1, x2, y2, x, y;
  int curtain_frames = 1;
  if( cave && cave->animated_curtain )
    {
      curtain_frames=cave->animated_curtain->num_images;
    }


  view= GSTONES_VIEW (widget);

  /* Initialize image buffer */

  if (!view->view_buffer)
    view->view_buffer = 
      gdk_pixmap_new (widget->window, CAVE_MAX_WIDTH*STONE_SIZE,
		                      CAVE_MAX_HEIGHT*STONE_SIZE, -1 );
  area= &event->area;

  x1 = (area->x+view->x_offset)/STONE_SIZE;
  y1 = (area->y+view->y_offset)/STONE_SIZE;
  x2 = (area->x+area->width+view->x_offset+STONE_SIZE-1)/STONE_SIZE;
  y2 = (area->y+area->height+view->y_offset+STONE_SIZE-1)/STONE_SIZE;
  
  for (x = x1; x <= x2; x++)
    for (y = y1; y <= y2; y++)
      {
	GdkPixmap *image= NULL;
	
	if ((view->curtain_display_mode == CURTAIN_DISPLAY_CLOSING && 
	     curtain_visible_test (x, y, view->curtain)) ||
	    (view->curtain_display_mode == CURTAIN_DISPLAY_OPENING && 
	     !curtain_visible_test (x, y, view->curtain)))
	  {
	    if( cave && cave->animated_curtain )
	      {
	      image = 
		object_get_image (cave->animated_curtain, 
                      /*curtain_frames-1-*/(view->curtain)%curtain_frames);
	      }
	    else
	      image= view->curtain_image;
	  }
	else if (view->display_mode == DISPLAY_IMAGE)
	  {
	    image= NULL;
	  }
	else if (cave)
	  {
	    image= cave_get_image (cave, x+1, y+1);
	  }

	if (image)
	  {
	    if (image!=view->last_image[x][y])
	      {
		view->last_image[x][y]=image;
		gdk_draw_drawable (view->view_buffer,
                                   widget->style->black_gc, image,
                                   0, 0, 
                                   x*STONE_SIZE, 
                                   y*STONE_SIZE,
                                   STONE_SIZE, STONE_SIZE);
	      }
	  }
	else
	  {
	    
	    view->last_image[x][y]= view->image;
	    gdk_draw_drawable (view->view_buffer,
                               widget->style->black_gc, view->image,
                               x*STONE_SIZE-view->x_offset, 
                               y*STONE_SIZE-view->y_offset, 
                               x*STONE_SIZE, 
                               y*STONE_SIZE,
                               STONE_SIZE, STONE_SIZE);
	  }
 
     }

  gdk_draw_drawable (widget->window,
                     widget->style->black_gc, view->view_buffer,
                     area->x+view->x_offset, 
                     area->y+view->y_offset, 
                     area->x, 
                     area->y,
                     area->width, area->height);

  return TRUE;
}



/*****************************************************************************/

static gint
view_curtain_timeout (gpointer data)
{
  GStonesView     *view= (GStonesView *) data;

  if (view->curtain_display_mode == CURTAIN_DISPLAY_CLOSING)
    {
      if (view->curtain > 0)
	{
	  /* The curtain is not closed yet.  */
	  
	  view->curtain--;
	  gtk_widget_queue_draw_area (GTK_WIDGET (view), 0, 0,
				      GAME_COLS * STONE_SIZE,
				      GAME_ROWS * STONE_SIZE);
	  return TRUE;
	}

      if (view->curtain_func)
	view->curtain_func (VIEW_CURTAIN_CLOSED);
      
      view->curtain_display_mode= CURTAIN_DISPLAY_OPENING;
      view->curtain             = GAME_COLS+GAME_ROWS;
      
      return TRUE;
    }
  else
    {
      if (view->curtain > 0)
	{
	  /* The curtain is not closed yet.  */
	  
	  view->curtain--;
	  gtk_widget_queue_draw_area (GTK_WIDGET (view), 0, 0,
				      GAME_COLS * STONE_SIZE,
				      GAME_ROWS * STONE_SIZE);
	  return TRUE;
	}
      
      view->curtain_timeout= 0;

      if (view->curtain_func)
	view->curtain_func (VIEW_CURTAIN_OPEN);
      
      return FALSE;
    }
}


void
view_set_curtain_mode (GStonesView     *view,
		       ViewCurtainMode  mode, 
		       ViewCurtainFunc  func)
{
  g_return_if_fail (view);

  if (view->curtain_timeout)
    {
      gtk_timeout_remove (view->curtain_timeout);
      view->curtain_timeout= 0;
    }
  
  switch (mode)
    {
    case VIEW_CURTAIN_OPEN:
      view->curtain_display_mode= CURTAIN_DISPLAY_NONE;
      gtk_widget_queue_draw_area (GTK_WIDGET (view), 0, 0,
                                  GAME_COLS * STONE_SIZE,
                                  GAME_ROWS * STONE_SIZE);
      break;
      
    case VIEW_CURTAIN_ANIMATE:
      view->curtain_display_mode= CURTAIN_DISPLAY_CLOSING;
      view->curtain             = CURTAIN_START_VALUE;
      view->curtain_func        = func;


      view->curtain_timeout= 
	gtk_timeout_add (CURTAIN_DELAY, view_curtain_timeout, (gpointer) view);
      break;

    case VIEW_CURTAIN_CLOSED:
      view->curtain_display_mode= CURTAIN_DISPLAY_CLOSING;
      view->curtain             = 0;
      gtk_widget_queue_draw_area (GTK_WIDGET (view), 0, 0,
                                  GAME_COLS * STONE_SIZE,
                                  GAME_ROWS * STONE_SIZE);
      break;      
    }
}

void
view_display_image (GStonesView *view, GdkPixmap *image)
{
  g_return_if_fail (view);
  g_return_if_fail (GSTONES_IS_VIEW (view));
  
  if (image)
    {
      if (view->image)
	g_object_unref (view->image);
      
      view->image= image;
      g_object_ref (view->image);
    }

  view->display_mode= DISPLAY_IMAGE;
  gtk_widget_queue_draw_area (GTK_WIDGET (view), 0, 0,
                              GAME_COLS * STONE_SIZE,
                              GAME_ROWS * STONE_SIZE);
}


void
view_display_cave (GStonesView *view, GStonesCave *cave)
{
  g_return_if_fail (view);
  g_return_if_fail (cave);
  g_return_if_fail (GSTONES_IS_VIEW (view));
  
  view->display_mode= DISPLAY_CAVE;
  gtk_widget_queue_draw_area (GTK_WIDGET (view), 0, 0,
                              GAME_COLS * STONE_SIZE,
                              GAME_ROWS * STONE_SIZE);
}


void
atari_scroll (GStonesView *view, GStonesCave *cave)
{
  gint x_rel;
  gint y_rel;
  
  x_rel= cave->player_x-view->x_offset/STONE_SIZE;
  y_rel= cave->player_y-view->y_offset/STONE_SIZE;
  
  view->x_scrolling= view->x_scrolling || (x_rel < 3) || (x_rel > GAME_COLS+1-3) || 
    (view->x_offset+GAME_COLS*STONE_SIZE > cave->width*STONE_SIZE);
  view->y_scrolling= view->y_scrolling || (y_rel < 3) || (y_rel > GAME_ROWS+1-3) || 
    (view->y_offset+GAME_ROWS*STONE_SIZE > cave->height*STONE_SIZE);
  
  if (view->x_scrolling)
    {
      if (((x_rel < 7) || (view->x_offset+GAME_COLS*STONE_SIZE > cave->width*STONE_SIZE)))
	{
	  view->x_offset-=SCROLL_SPEED;
	  if( view->x_offset<0 ) view->x_offset=0;
	}
      else if ((x_rel > GAME_COLS+1-7))
	{
	  view->x_offset+=SCROLL_SPEED;

	  if( view->x_offset>(cave->width-GAME_COLS)*STONE_SIZE ) 
	    view->x_offset=(cave->width-GAME_COLS)*STONE_SIZE;
	}
      else
	view->x_scrolling= FALSE;
    }

  if (view->y_scrolling)
    {      
      if (((y_rel < 5) || (view->y_offset+GAME_ROWS*STONE_SIZE > cave->height*STONE_SIZE)))
	{
	  view->y_offset-=SCROLL_SPEED;
	  if( view->y_offset<0 ) view->y_offset=0;
	}
      else if ((y_rel > GAME_ROWS+1-5))
	{
	  view->y_offset+=SCROLL_SPEED;

	  if( view->y_offset>(cave->height-GAME_ROWS)*STONE_SIZE ) 
	    view->y_offset=(cave->height-GAME_ROWS)*STONE_SIZE;

	}
      else
	view->y_scrolling= FALSE;
    }
}









#define MAX_SCROLL_SPEED 0.9

#ifndef abs
#define abs(i) ((i)>0?(i):-(i))
#endif

void
smooth_scroll (GStonesView *view, GStonesCave *cave)
{
  gint x_rel;
  gint y_rel;
  gint x_mov;
  gint y_mov;
  gint x_mid=GAME_COLS*STONE_SIZE/2;
  gint y_mid=GAME_ROWS*STONE_SIZE/2;

  x_rel= (STONE_SIZE*cave->player_x-view->x_offset)-STONE_SIZE/2;
  x_mov= (x_rel-x_mid)*2/GAME_COLS;
  x_mov+=(x_rel>x_mid)?1:-1;

  y_rel= (STONE_SIZE*cave->player_y-view->y_offset)-STONE_SIZE/2;
  y_mov= (y_rel-y_mid)*2/GAME_ROWS;
  y_mov+=(y_rel>y_mid)?1:-1;

  
  view->x_scrolling= view->x_scrolling || abs(x_rel-x_mid) > 1;
  view->y_scrolling= view->y_scrolling || abs(y_rel-y_mid) > 1;
  

  if (view->x_scrolling)
    {

      if (x_mov<-MAX_SCROLL_SPEED*STONE_SIZE) 
        x_mov=-MAX_SCROLL_SPEED*STONE_SIZE;
      if (x_mov>MAX_SCROLL_SPEED*STONE_SIZE) 
        x_mov=MAX_SCROLL_SPEED*STONE_SIZE;
 
      view->x_offset+=x_mov;
      if (abs(x_rel-x_mid)<2) view->x_scrolling=FALSE;

      
      if( x_mov>0 && view->x_offset>(cave->width-GAME_COLS)*STONE_SIZE ) 
	view->x_offset=(cave->width-GAME_COLS)*STONE_SIZE;

      if( view->x_offset<0 ) view->x_offset=0;

    }

  if (view->y_scrolling)
    {
      if (y_mov<-MAX_SCROLL_SPEED*STONE_SIZE) 
        y_mov=-MAX_SCROLL_SPEED*STONE_SIZE;
      if (y_mov>MAX_SCROLL_SPEED*STONE_SIZE) 
        y_mov=MAX_SCROLL_SPEED*STONE_SIZE;
 
      view->y_offset+=y_mov;
      if (abs(y_rel-y_mid)<2) view->y_scrolling=FALSE;

      if( y_mov>0 && view->y_offset>(cave->height-GAME_ROWS)*STONE_SIZE ) 
	view->y_offset=(cave->height-GAME_ROWS)*STONE_SIZE;

      if( view->y_offset<0 ) view->y_offset=0;
      
    }



}


void
center_scroll (GStonesView *view, GStonesCave *cave)
{

  view->x_offset= 
     STONE_SIZE*cave->player_x-STONE_SIZE/2-GAME_COLS*STONE_SIZE/2;

  view->y_offset= 
     STONE_SIZE*cave->player_y-STONE_SIZE/2-GAME_ROWS*STONE_SIZE/2;



  if( view->x_offset>(cave->width-GAME_COLS)*STONE_SIZE ) 
    view->x_offset=(cave->width-GAME_COLS)*STONE_SIZE;

  if( view->x_offset<0 ) view->x_offset=0;
      



  if( view->y_offset>(cave->height-GAME_ROWS)*STONE_SIZE ) 
    view->y_offset=(cave->height-GAME_ROWS)*STONE_SIZE;

  if( view->y_offset<0 ) view->y_offset=0;
      


}




void
view_calculate_offset (GStonesView *view, GStonesCave *cave)
{
  view_scroll_method (view, cave); 
}
