#include "gs.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <exec/memory.h>
#include <graphics/gfxbase.h>
#include <graphics/gfxmacros.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <intuition/gadgetclass.h>
#include <intuition/imageclass.h>
#include <intuition/icclass.h>
#include <intuition/cghooks.h>
#include <intuition/classes.h>
#include <clib/intuition_protos.h>
#include <clib/exec_protos.h>
#include <graphics/scale.h>
#include <clib/graphics_protos.h>
#include <clib/utility_protos.h>
#include <graphics/graphint.h>
#include <clib/alib_protos.h>
#include "gxdevice.h"
#include "gdevintui.h"
#include "gsmatrix.h"
#include <signal.h>
#include "rkmmodel.h"

#define LR_WIDTH 16
#define UD_HEIGHT 11

extern void gs_intui(void);

/* forward declarations */
private int i_open_device(gx_device *);
int i_close_device(gx_device *);
private int i_fill_rectangle(gx_device *,int,int,int,int,gx_color_index);
dev_proc_copy_mono(i_copy_mono);
private int i_draw_line(gx_device *,int,int,int,int,gx_color_index);
private void i_get_initial_matrix(gx_device *, gs_matrix *);
/*
private int i_copy_color(gx_device *, unsigned char *,int,int, \
			 gx_bitmap_id,int,int,int,int);
*/
private int i_sync_output(gx_device *);

struct Window *open_my_window(gx_device *);
void clean_win(struct Window **);
void close_my_window(struct Window *);
void disable_gadgets(struct Window *);
void enable_gadgets(struct Window *);
void start_render_state(struct Window *);
void stop_render_state(struct Window *);


/* Procedure records */
#ifdef BLACK_AND_WHITE
private gx_color_index i_bw_map_rgb_color(gx_device *, gx_color_value , \
				  gx_color_value , gx_color_value );
private int i_bw_map_color_rgb(gx_device *, gx_color_index , \
		       gx_color_value rgb[3]);
#define intui_procs {\
	i_open_device, gx_default_get_initial_matrix,\
	i_sync_output, gx_default_output_page, i_close_device,\
	i_bw_map_rgb_color, i_bw_map_color_rgb,\
	i_fill_rectangle, gx_default_tile_rectangle,\
	i_copy_mono, gx_default_copy_color, i_draw_line,\
	gx_default_get_bits, gx_default_get_props, gx_default_put_props}
#else
private gx_color_index i_map_rgb_color(gx_device *, gx_color_value , \
			       gx_color_value , gx_color_value );
private int i_map_color_rgb(gx_device *, gx_color_index , \
			    gx_color_value rgb[3]);
#define intui_procs {\
	i_open_device, gx_default_get_initial_matrix,\
	i_sync_output, gx_default_output_page, i_close_device,\
	i_map_rgb_color, i_map_color_rgb,\
	i_fill_rectangle, gx_default_tile_rectangle,\
	i_copy_mono, gx_default_copy_color, i_draw_line,\
	gx_default_get_bits, gx_default_get_props, gx_default_put_props}
#endif

private gx_device_procs gs_intui_procs=intui_procs;

/* device structure */
intui_device gs_intui_device={
  sizeof(intui_device),
  &gs_intui_procs,         /* procedures */
  "intui",                 /* name */
  PRE_WIDTH,               /* width */
  PRE_HEIGHT,              /* height */
  XPPI,                    /* x_pixels_per_inch */
  YPPI,                    /* y_pixels_per_inch */
  no_margins,              /* rectangle--margins around imageable area*/  
#ifdef BLACK_AND_WHITE
  dci_black_and_white,
#else
  {1,2,3,0,4,0},
#endif
  0,                       /* is open */
  NULL,                    /* window */
  NULL,                    /* screen */
  NULL,                    /* rast port */
  NULL,                    /* super bitmap */
  0,                       /* real depth */
  0,0,                     /* black and white */
  NULL                     /* screen's DrawInfo */
};

UBYTE *monoplane=NULL;
size_t mono_size=0;
struct BitMap *color_chipmap;
ULONG sbm_plane_size=0;
struct Remember *remkey=NULL;
struct Remember *reremkey=NULL;
struct Task *task;
struct Gadget *my_gad[6];
Object *my_rkm_model1;
Object *my_rkm_model2;
struct IClass *rkm;
struct Image *up_image;
struct Image *down_image;
struct Image *left_image;
struct Image *right_image;
struct Gadget *pg_h;
struct Gadget *bg_h1;
struct Gadget *bg_h2;
struct Gadget *pg_v;
struct Gadget *bg_v1;
struct Gadget *bg_v2;
struct TagItem *id2u_map;
struct TagItem *id2d_map;
struct TagItem *c2p_map;
struct TagItem *p2c_map;
char version_string[]="$VER: Ghostscript 2.4.1 Amiga Port 1.0b (11 Mar 1993)";
boolean render_active=FALSE;

/** procedures **/

/* tests if the pixel coordinates have valid values */
boolean is_valid_area(gx_device *dev,int x,\
		      int y, int width, int height)
{
  return x>=0 && y>=0 && width>=0 && height>=0 \
    && x+width<=idev->width && y+height<=idev->height ;
}

/* Amiga library bases */
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct UtilityBase *UtilityBase;
struct LayersBase *LayersBase;

#ifdef DEBUG
void ctrl_hook(int sig)
{
  i_close_device((gx_device *)&gs_intui_device);
  fprintf(stderr,"user aborted program\n");
  exit(0);
}
#endif

/* opens the intui device; makes allocations and initialations */
int i_open_device(gx_device *dev)
{
  int res=0;
  struct Screen *display_screen;
  int depth=idev->color_info.depth;
  int i;
  struct TagItem *win_tags;
  BOOL alloc_err=FALSE;
  struct BitMap *sbm;
  struct Window *display_window;
  
#ifdef DEBUG
  printf("open device\n");
  signal(SIGINT,ctrl_hook);
#endif
  /* Open the graphics library */
  if((GfxBase=(struct GfxBase *)OpenLibrary("graphics.library",\
					    LIB_REV))!=NULL)
    {
      /* Open the intuition library */
      if((IntuitionBase=(struct IntuitionBase *) \
	  OpenLibrary("intuition.library",LIB_REV))!=NULL)
	{
	  if((LayersBase=(struct LayersBase *)\
	      OpenLibrary("layers.library",LIB_REV))!=NULL)
	    {
	      if((UtilityBase=(struct UtilityBase *) \
		  OpenLibrary("utility.library",LIB_REV))!=NULL)
		{
		  /* alloc mem for color_chipmap structure */
		  if((color_chipmap=\
		      AllocRemember(&remkey,sizeof(*color_chipmap),\
				    MEMF_PUBLIC|MEMF_CLEAR|\
				    MEMF_ANY))\
		     !=NULL)
		    {
		      /* locks the standard public screen */
		      if((display_screen=LockPubScreen(NULL))!=NULL)
			{
			  idev->s=display_screen;
			  idev->di=GetScreenDrawInfo(display_screen);
			  if((sbm=(struct BitMap *)\
			      AllocRemember(&remkey,\
					    sizeof(*sbm),\
					    MEMF_PUBLIC|\
					    MEMF_CLEAR|\
					    MEMF_ANY))\
			     !=NULL)
			    {
			      idev->real_depth=idev->di->dri_Depth;
#ifdef DEBUG
			      fprintf(stderr,"depth: %d\n",idev->real_depth);
#endif
				InitBitMap(sbm,idev->real_depth,\
					 idev->width,idev->height);
			      idev->supermap=sbm;
			      for(i=0;i<idev->real_depth && !alloc_err;\
				  i++)
				{
				  sbm_plane_size=RASSIZE(idev->width,\
							 idev->height);
				  if((sbm->Planes[i]=\
				      AllocRemember(&remkey,\
						    sbm_plane_size,\
						    MEMF_CHIP|\
						    MEMF_CLEAR|\
						    MEMF_REVERSE|\
						    MEMF_PUBLIC))\
				     ==NULL) alloc_err=TRUE;
				}
			      if(!alloc_err)
				{
				  if((display_window=
				      open_my_window(dev))!=NULL)
				    {
				      idev->w=display_window;
				      idev->rp=\
					display_window->RPort;
				      idev->is_open=-1;
				      
				      /* information for B&w */
				      /* displaying */
				      idev->white=2;
/*
					idev->di->\
					  dri_Pens[BLOCKPEN];
*/
				      idev->black=1;
/*
					idev->di->\
					  dri_Pens[DETAILPEN];
*/
				      /* calculate scaling */
				      /* due to smaller */
				      /* window size */
				      if((task=\
					  CreateTask("ghostscript "
						     "Intuition",\
						     FindTask(NULL)\
						     ->tc_Node\
						     .ln_Pri,\
						     gs_intui,\
						     STACK_SIZE))\
					 ==NULL)
					{
					  res=-1;
#ifdef DEBUG
					  fprintf(stderr,\
						  "gs intui: "
						  "can' create "
						  "ghostscript "
						  "intuition "
						  "task\n");
#endif
					}
				    }
				  else 
				    {
				      res=-1;
#ifdef DEBUG
				      fprintf(stderr,"gs intui: "
					      "can't open "
					      "intuition "
					      "window.\n");
#endif
				    }
				}
			      else
				{
				  res=-1;
#ifdef DEBUG
				  fprintf(stderr,"gs intui: can't alloc mem "
					  "for super bitmap planes.\n");
#endif
				}
			    }
			  else 
			    {
			      res=-1;
#ifdef DEBUG
			      fprintf(stderr,"gs intui: can't alloc mem "
				      "for super bitmap structure.\n");
#endif
			    }
			}
		      else 
			{
			  res=-1;
#ifdef DEBUG
			  fprintf(stderr,"gs intui: can't lock standard "
				  "public screen.\n");
#endif
			}
		    }
		  else
		    {
		      res=-1;
#ifdef DEBUG
		      fprintf(stderr,"gs intui: can't allocate mem for "
			      "bitmap structure.\n");
#endif
		    }
		}
	      else
		{
		  res=-1;
#ifdef DEBUG
		  fprintf(stderr,"gs intui: can't open utility library "
			  "V%d.\n",LIB_REV);
#endif
		}
	    }
	  else
	    {
	      res=-1;
#ifdef DEBUG
	      fprintf(stderr,"gs intui: can't open layers library "
		      "V%d.\n",LIB_REV);
#endif
	    }
	}
      else 
	{
	  res=-1;
#ifdef DEBUG
	  fprintf(stderr,"gs intui: can't open intuition library "
		  "V%d.\n",LIB_REV);
#endif
	}
    }
  else 
    {
      res=-1;
#ifdef DEBUG
      fprintf(stderr,"gs intui: can't open graphics library "
	      "V%d.\n",LIB_REV);
#endif
    }
  return res;
}

/* closes intui device */
int i_close_device(gx_device *dev)
{
  int i;
  struct Window *w=idev->w;
  struct BitMap *sbm=idev->supermap;

  idev->is_open=0;
#ifdef DEBUG
  fprintf(stderr,"gs intui: close device\n");
#endif
  idev->is_open=FALSE;
  if(task!=NULL) DeleteTask(task);
  WaitBlit();
  for(i=0;i<idev->color_info.depth;i++)
    {
      if(color_chipmap->Planes[i]!=NULL)
	FreeVec(color_chipmap->Planes[i]);
    }
  FreeRemember(&reremkey,TRUE);
  FreeRemember(&remkey,TRUE);
  close_my_window(idev->w);
  FreeScreenDrawInfo(idev->s,idev->di);
  UnlockPubScreen(NULL,idev->s);
  CloseLibrary((struct Library *)UtilityBase);
  CloseLibrary((struct Library *)LayersBase);
  CloseLibrary((struct Library *)IntuitionBase);
  CloseLibrary((struct Library *)GfxBase);
  return 0;
}

int i_ctab[]={1,3,0,2};

#ifdef BLACK_AND_WHITE
gx_color_index i_bw_map_rgb_color(gx_device *dev, gx_color_value red,
				  gx_color_value green, \
				  gx_color_value blue)
{
  return red==gx_max_color_value?idev->white:idev->black;
}

int i_bw_map_color_rgb(gx_device *dev, gx_color_index color, \
		       gx_color_value rgb[3])
{
  rgb[0]=rgb[1]=rgb[2]=color==idev->white?gx_max_color_value:0;

  return 0;
}
#else
gx_color_index i_map_rgb_color(gx_device *dev, gx_color_value red,
			       gx_color_value green, gx_color_value blue)
{
  gx_color_value nred=red>>((sizeof(gx_color_value)<<3)-2);
 
  return i_ctab[nred];
}

int i_map_color_rgb(gx_device *dev, gx_color_index color, \
		    gx_color_value rgb[3])
{
  gx_color_value v=0;
  int res=0;

  while(i_ctab[v]!=color && v<4) v++;
  if(v<4)
    {
      v=-1&(v<<((sizeof(gx_color_value)<<3)-2));
      rgb[0]=rgb[1]=rgb[2]=v;
    }
  else res=-1;

  return res;
}
#endif

int i_fill_rectangle(gx_device *dev, int x, int y, \
			int width, int height, gx_color_index color)
{
  if(!render_active) 
    start_render_state(idev->w);

  if(is_valid_area(dev,x,y,width,height))
    {
      if(width>0 && height>0)
	{
	  SetDrMd(idev->rp,JAM1);
	  SetAPen(idev->rp,color);
	  LockLayerInfo(idev->rp->Layer->LayerInfo);
	  LockLayerRom(idev->rp->Layer);
	  RectFill(idev->rp,x,y,x+width-1,y+height-1);
	  UnlockLayerRom(idev->rp->Layer);
	  UnlockLayerInfo(idev->rp->Layer->LayerInfo);
	  SetAPen(idev->rp,1);
	}
    }
  return 0;
}

int i_draw_line(gx_device *dev, int x0, int y0, int x1, int y1, \
		gx_color_index color)
{
  if(!render_active) 
    start_render_state(idev->w);

  if(x0!=x1 || y0!=y1)
    {
      if(y1 < y0)
	{
	  int d;
	  
	  d=x1;
	  x1=x0;
	  x0=d;
	  d=y1;
	  y1=y0;
	  y0=d;
	}
      if(is_valid_area(dev,x0,y0,abs(x1-x0),y1-y0))
	{
	  SetDrMd(idev->rp,JAM1);
	  SetAPen(idev->rp,color);
	  LockLayerInfo(idev->rp->Layer->LayerInfo);
	  LockLayerRom(idev->rp->Layer);
	  Move(idev->rp,x0,y0);
	  Draw(idev->rp,x1-1,y1-1);
	  UnlockLayerRom(idev->rp->Layer);
	  UnlockLayerInfo(idev->rp->Layer->LayerInfo);
	}
    }
  return 0;
}

int i_copy_mono(gx_device *dev, const unsigned char *data, int data_x, \
		int raster, gx_bitmap_id id, int x, int y, \
		int width, int height, gx_color_index color0, \
		gx_color_index color1)
{
  boolean draw=TRUE;
  unsigned long x_words=data_x>>4;
  int x_bits=data_x&0xf;
  int res=0;
  static gx_bitmap_id old_id=gx_no_bitmap_id;
  
  if(!render_active) 
    start_render_state(idev->w);

  /* the drawmode and the back- and forground colors depend on
     combinations of color1 color0 if they equal gx_no_color_value  */
  if(is_valid_area(dev,x,y,width,height))
    {
      if(width>0 && height>0)
	{
	  if(color0!=gx_no_color_index)
	    {
	      if(color1!=gx_no_color_index)
		{
		  SetDrMd(idev->rp,JAM2);
		  SetAPen(idev->rp,color1);
		  SetBPen(idev->rp,color0);
		}
	      else
		{
		  SetDrMd(idev->rp,JAM1|INVERSVID);
		  SetAPen(idev->rp,color0);
		}
	    }
	  else
	    {
	      if(color1!=gx_no_color_index)
		{
		  SetDrMd(idev->rp,JAM1);
		  SetAPen(idev->rp,color1);
		}
	      else
		draw=FALSE;
	    }
	  
	  if(draw) 
	    {
	      /* first order optimization: 
		 if bitmap was already copied last
		 time, don't copy it again */
	      if(id!=old_id || id==gx_no_bitmap_id)
		{
		  WaitBlit();
		  if(mono_size<RASSIZE(raster<<3,height))
		    {
		      FreeRemember(&reremkey,TRUE);
		      mono_size=((raster*height+15)\
				 /16)*16;
		      monoplane=
			AllocRemember(&reremkey,mono_size,\
				      MEMF_CHIP|MEMF_REVERSE);
#ifdef DEBUG
		      fprintf(stderr,"alloc size: %ld\n",\
			      mono_size);
#endif
		    }
		  if(monoplane!=NULL)
		    {
/*
#ifdef DEBUG
		      fprintf(stderr,"copy size: %ld\n",\
			      RASSIZE((size_t)raster<<3,height));
#endif
*/
		      CopyMem(data,monoplane,\
			      (ULONG)RASSIZE((size_t)raster<<3,\
				      height));
		      old_id=id;
		    }
		}
	      if(monoplane!=NULL)
		{
		  /* do the blit */
		  LockLayerInfo(idev->rp->Layer->LayerInfo);
		  LockLayerRom(idev->rp->Layer);
		  BltTemplate((UBYTE *) \
			      (monoplane+(x_words<<1)), \
			      x_bits,raster,idev->rp,x,y,\
			      width,height);
		  UnlockLayerRom(idev->rp->Layer);
		  UnlockLayerInfo(idev->rp->Layer->LayerInfo);
		  /* reset the saved display id */
		  if(id!=gx_no_bitmap_id) old_id=id;
		}
	      else res=-1;
	    }
	}
    }
  else res=-1;
  return res;
}


/*
void i_get_initial_matrix(gx_device *dev, gs_matrix *m)
{
  
  m->xy=m->yx=m->tx=0;
  m->xx=idev->x_pixels_per_inch*SCALE*idev->wz_scale_x/72.0;
  m->yy=-idev->y_pixels_per_inch*SCALE*idev->wz_scale_y/72.0;
  m->ty=idev->height;
}
*/

/* clgorithm for composing a graphics bitmap from a ghostscript color bitmap */
int copy_converted_map(char *source, PLANEPTR dplane[], \
		       int raster,int height,int depth)
{
  unsigned long doffset=0;
  char *saddress=source;
  int planenr;
  char sbitmask;
  char dbitmask=1;
  unsigned long depth_bytes=(unsigned long)(depth+7>>3);
  unsigned long dbyte;
  PLANEPTR *plane_pp;

  /* saddress runs through the full range of source data */
  while(saddress<source+raster*height*8)
    {
      /* we start at graphics bitmalane zero */
      plane_pp=dplane;
      /* count the bytes for one graphics pixel. 
	 This is only valid for depths 8,16,24,32 => 2,4 not supported,
	 1 will be done by copy_mono */
      for(dbyte=0;dbyte<depth_bytes;dbyte++)
	{
	  /* shift the input bitmask */
	  for(sbitmask=1;sbitmask!=0;sbitmask<<=1)
	    {
	      /* set or not set */
	      if(*(saddress+dbyte)&sbitmask)
		*(*plane_pp+doffset)|=dbitmask;
	      /* next graphics bitplane */
	      plane_pp++;
	    }
	}
      /* next graphics pixel */
      if((dbitmask<<1)==0) doffset++;
      /* calculate new source address */
      saddress+=(unsigned long)depth_bytes;
    }
  return 0;
}

/*
int i_copy_color(gx_device *dev, unsigned char *data, int data_x,
  int raster, gx_bitmap_id id, int x, int y, int width, int height)
{
  int res=0;
  int conv_res=0;
  static gx_bitmap_id old_cid=gx_no_bitmap_id;
  int depth=idev->color_info.depth;
  int i;
  static size_t color_size=0;
  static int max_depth=0;

  if(id!=old_cid || id==gx_no_bitmap_id)
    {
      color_chipmap->BytesPerRow=raster;
      color_chipmap->Rows=height;
      color_chipmap->Flags=0;
      color_chipmap->Depth=depth;
      color_chipmap->pad=0;
      WaitBlit();
      if(color_size<(size_t)raster*(size_t)\
	 height||max_depth<depth)
	{
	  i=0;
	  while(color_chipmap->Planes[i]!=NULL&&i<8)
	    {
	      FreeVec(color_chipmap->Planes[i]);
	      color_chipmap->Planes[i++]=NULL;
	    }
	  for(i=0;i<depth,conv_res==0;i++)
	    {
	      if((color_chipmap->Planes[i]=\
		  AllocVec((size_t)raster*\
			   (size_t)height,\
			   MEMF_CHIP|MEMF_PUBLIC))
		 ==NULL)
		conv_res=-1;
	      else
		{
		  color_size=(size_t)raster*(size_t)height;
		  max_depth=depth;
		}
	    }
	}
      if(conv_res==0)
	{
	  if(copy_converted_map(data,color_chipmap->Planes,\
				raster,height,depth)!=0)
	    conv_res=-1;
	}
    }
  if(conv_res==0)
    {
      BltBitMapRastPort(color_chipmap,data_x,0,idev->rp,x,y,width, \
			     height,0xc0);
      if(id!=gx_no_bitmap_id) old_cid=id;
    }
  else 
    {
      res=-1;
#ifdef DEBUG
      fprintf(stderr,"gs intui: copy color: copy_converted() or out "
	      "of copy-mem error.\n");
#endif
    }
  return res;
}
*/

int i_sync_output(gx_device *dev)
{
  struct RastPort *rp=idev->w->RPort;

  WaitBlit();
  SyncSBitMap(rp->Layer);
  CopySBitMap(rp->Layer);
  stop_render_state(idev->w);
  return 0;
}

struct Window *open_my_window(gx_device *dev)
{
  struct Window *w=NULL;
  struct TagItem *win_tags;
  Object *icc_c2p_h;
  Object *icc_c2p_v;
  int tbs;
  int rbs;
  int bbs;
  int lbs;
  struct TagItem *pg_taglist;
  struct TagItem *bg_taglist;
  struct TagItem *im_taglist;
  struct TagItem prop_map[]=
    {
      {GA_RelBottom,GA_Top},
      {GA_Left,GA_RelRight},
      {GA_RelWidth,GA_Width},
      {GA_Height,GA_RelHeight},
      {GA_BottomBorder,GA_RightBorder},
      {TAG_END}
    };
  struct TagItem button_map[]=
    {
      {GA_BottomBorder,GA_RightBorder},
      {TAG_END}
    };
  struct TagItem *tag;
  struct TagItem *rkm_inittags;

  /* tags for window */
  if((win_tags=AllocateTagItems(19))\
     !=NULL)
    {
      /* set tag list for window */
      win_tags[0].ti_Tag=WA_PubScreen;
      win_tags[0].ti_Data=(ULONG)idev->s;
      win_tags[1].ti_Tag=WA_Width;
      win_tags[1].ti_Data=WIN_WIDTH;
      win_tags[2].ti_Tag=WA_Height;
      win_tags[2].ti_Data=WIN_HEIGHT;
      win_tags[3].ti_Tag=WA_Title;
      win_tags[3].ti_Data=(ULONG)"Ghostscript";
      win_tags[4].ti_Tag=WA_DragBar;
      win_tags[4].ti_Data=TRUE;
      win_tags[5].ti_Tag=WA_DepthGadget;
      win_tags[5].ti_Data=TRUE;
      win_tags[6].ti_Tag=WA_GimmeZeroZero;
      win_tags[6].ti_Data=TRUE;
      win_tags[7].ti_Tag=TAG_IGNORE;
      win_tags[7].ti_Data=TRUE;
      win_tags[8].ti_Tag=WA_SizeGadget;
      win_tags[8].ti_Data=TRUE;
      win_tags[9].ti_Tag=WA_SizeBRight;
      win_tags[9].ti_Data=TRUE;
      win_tags[10].ti_Tag=WA_SizeBBottom;
      win_tags[10].ti_Data=TRUE;
      win_tags[11].ti_Tag=WA_SuperBitMap;
      win_tags[11].ti_Data=(ULONG)idev->supermap;
      win_tags[12].ti_Tag=WA_MinWidth;
      win_tags[12].ti_Data=86;
      win_tags[13].ti_Tag=WA_MinHeight;
      win_tags[13].ti_Data=81;
      win_tags[14].ti_Tag=WA_MaxWidth;
      win_tags[14].ti_Data=idev->width;
      win_tags[15].ti_Tag=WA_MaxHeight;
      win_tags[15].ti_Data=idev->height;
      win_tags[16].ti_Tag=WA_IDCMP;
      win_tags[16].ti_Data=NULL;
      win_tags[17].ti_Tag=TAG_IGNORE;
      win_tags[18].ti_Tag=TAG_DONE;
      /* open window */
      if((w=OpenWindowTagList(NULL,win_tags))!=NULL)
	{
	  /* get border dimensions */
	  tbs=w->BorderTop; 
	  rbs=w->BorderRight;
	  bbs=w->BorderBottom;
	  lbs=w->BorderLeft;
	  /* allocate boopsi maps */
	  if((id2u_map=AllocateTagItems(2))!=NULL &&\
	     (id2d_map=AllocateTagItems(2))!=NULL &&\
	     (c2p_map=AllocateTagItems(2))!=NULL &&\
	     (p2c_map=AllocateTagItems(2))!=NULL)
	    {
	      /* set maps */
	      id2u_map[0].ti_Tag=GA_ID;
	      id2u_map[0].ti_Data=RKMMOD_Up;
	      id2u_map[1].ti_Tag=TAG_DONE;
	      id2d_map[0].ti_Tag=GA_ID;
	      id2d_map[0].ti_Data=RKMMOD_Down;
	      id2d_map[1].ti_Tag=TAG_DONE;
	      c2p_map[0].ti_Tag=RKMMOD_CurrVal;
	      c2p_map[0].ti_Data=PGA_Top;
	      p2c_map[0].ti_Tag=PGA_Top;
	      p2c_map[0].ti_Data=RKMMOD_CurrVal;

	      /* allocate tag lists */
	      if((pg_taglist=AllocateTagItems(14))!=NULL &&\
		 (bg_taglist=AllocateTagItems(10))!=NULL &&\
		 (im_taglist=AllocateTagItems(8))!=NULL)
		{
		  /* init taglist for prop gadgets */
		  pg_taglist[0].ti_Tag=GA_ID;
		  pg_taglist[0].ti_Data=0;
		  pg_taglist[1].ti_Tag=GA_RelBottom;
		  pg_taglist[1].ti_Data=-bbs+2;
		  pg_taglist[2].ti_Tag=GA_Left;
		  pg_taglist[2].ti_Data=lbs;
		  pg_taglist[3].ti_Tag=GA_RelWidth;
		  pg_taglist[3].ti_Data=-lbs-rbs-2*LR_WIDTH-2;
		  pg_taglist[4].ti_Tag=GA_Height;
		  pg_taglist[4].ti_Data=bbs-3;
		  pg_taglist[5].ti_Tag=GA_BottomBorder;
		  pg_taglist[5].ti_Data=TRUE;
		  pg_taglist[6].ti_Tag=GA_GZZGadget;
		  pg_taglist[6].ti_Data=TRUE;
		  pg_taglist[7].ti_Tag=PGA_Freedom;
		  pg_taglist[7].ti_Data=FREEHORIZ;
		  pg_taglist[8].ti_Tag=PGA_NewLook;
		  pg_taglist[8].ti_Data=TRUE;
		  pg_taglist[9].ti_Tag=PGA_Top;
		  pg_taglist[9].ti_Data=0;
		  pg_taglist[10].ti_Tag=PGA_Total;
		  pg_taglist[10].ti_Data=idev->width/SCROLL_UNIT;
		  pg_taglist[11].ti_Tag=PGA_Visible;
		  pg_taglist[11].ti_Data=w->GZZWidth/SCROLL_UNIT;
		  pg_taglist[12].ti_Tag=GA_Previous;
		  pg_taglist[12].ti_Data=NULL;
		  pg_taglist[13].ti_Tag=TAG_DONE;

		  /* init image taglist */
		  im_taglist[0].ti_Tag=IA_Left;
		  im_taglist[0].ti_Data=0;
		  im_taglist[1].ti_Tag=IA_Top;
		  im_taglist[1].ti_Data=0;
		  im_taglist[2].ti_Tag=IA_Width;
		  im_taglist[2].ti_Data=16;
		  im_taglist[3].ti_Tag=IA_Height;
		  im_taglist[3].ti_Data=10;
		  im_taglist[4].ti_Tag=SYSIA_Which;
		  im_taglist[4].ti_Data=LEFTIMAGE;
		  im_taglist[5].ti_Tag=SYSIA_Size;
		  im_taglist[5].ti_Data=SYSISIZE_MEDRES;
		  im_taglist[6].ti_Tag=SYSIA_DrawInfo;
		  im_taglist[6].ti_Data=(ULONG)idev->di;
		  im_taglist[7].ti_Tag=TAG_END;

		  /* init button gadget taglist */
		  bg_taglist[0].ti_Tag=GA_ID;
		  bg_taglist[0].ti_Data=2;
		  bg_taglist[1].ti_Tag=GA_RelBottom;
		  bg_taglist[1].ti_Data=-bbs+1;
		  bg_taglist[2].ti_Tag=GA_RelRight;
		  bg_taglist[2].ti_Data=-rbs-2*LR_WIDTH;
		  bg_taglist[3].ti_Tag=GA_Width;
		  bg_taglist[3].ti_Data=16;
		  bg_taglist[4].ti_Tag=GA_Height;
		  bg_taglist[4].ti_Data=10;
		  bg_taglist[5].ti_Tag=GA_Image;
		  bg_taglist[5].ti_Data=NULL;
		  bg_taglist[6].ti_Tag=GA_GZZGadget;
		  bg_taglist[6].ti_Data=TRUE;
		  bg_taglist[7].ti_Tag=GA_BottomBorder;
		  bg_taglist[7].ti_Data=TRUE;
		  bg_taglist[8].ti_Tag=GA_Previous;
		  bg_taglist[8].ti_Data=NULL;
		  bg_taglist[9].ti_Tag=TAG_END;
		  
		  /* create images */
		  left_image=(struct Image *)NewObjectA(NULL,"sysiclass",im_taglist);
		  tag=FindTagItem(SYSIA_Which,im_taglist);tag->ti_Data=RIGHTIMAGE;
		  right_image=(struct Image *)NewObjectA(NULL,"sysiclass",im_taglist);
		  tag=FindTagItem(IA_Width,im_taglist);tag->ti_Data=18;
		  tag=FindTagItem(IA_Height,im_taglist);tag->ti_Data=11;
		  tag=FindTagItem(SYSIA_Which,im_taglist);tag->ti_Data=UPIMAGE;
		  up_image=(struct Image *)NewObjectA(NULL,"sysiclass",im_taglist);
		  tag=FindTagItem(SYSIA_Which,im_taglist);tag->ti_Data=DOWNIMAGE;
		  down_image=(struct Image *)NewObjectA(NULL,"sysiclass",im_taglist);
		  if(left_image!=NULL && right_image!=NULL && \
		     up_image!=NULL && down_image!=NULL)
		    {
		      /* create prop gadgets */
		      pg_h=(struct Gadget *)NewObjectA(NULL,"propgclass",pg_taglist);
		      MapTags(pg_taglist,prop_map,TRUE);
		      tag=FindTagItem(GA_ID,pg_taglist);tag->ti_Data=1;
		      tag=FindTagItem(GA_Top,pg_taglist);tag->ti_Data=tbs;
		      tag=FindTagItem(GA_RelRight,pg_taglist);tag->ti_Data=-rbs+4;
		      tag=FindTagItem(GA_Width,pg_taglist);tag->ti_Data=rbs-6;
		      tag=FindTagItem(GA_RelHeight,pg_taglist);tag->ti_Data=\
			-tbs-bbs-2*UD_HEIGHT-1;
		      tag=FindTagItem(PGA_Freedom,pg_taglist);tag->ti_Data=FREEVERT;
		      tag=FindTagItem(GA_Previous,pg_taglist);
		      tag->ti_Data=(ULONG)pg_h;
		      tag=FindTagItem(PGA_Total,pg_taglist);
		      tag->ti_Data=idev->height/SCROLL_UNIT;
		      tag=FindTagItem(PGA_Visible,pg_taglist);
		      tag->ti_Data=w->GZZHeight/SCROLL_UNIT;
		      pg_v=(struct Gadget *)NewObjectA(NULL,"propgclass",pg_taglist);

		      /* create button gadgets */
		      tag=FindTagItem(GA_Image,bg_taglist);
		      tag->ti_Data=(ULONG)left_image;
		      tag=FindTagItem(GA_Previous,bg_taglist);
		      tag->ti_Data=(ULONG)pg_v;
		      bg_h1=(struct Gadget *)NewObjectA(NULL,"buttongclass",\
							bg_taglist);
		      tag=FindTagItem(GA_ID,bg_taglist);tag->ti_Data=3;
		      tag=FindTagItem(GA_RelRight,bg_taglist);
		      tag->ti_Data=-rbs-LR_WIDTH;
		      tag=FindTagItem(GA_Image,bg_taglist);
		      tag->ti_Data=(ULONG)right_image;
		      tag=FindTagItem(GA_Previous,bg_taglist);
		      tag->ti_Data=(ULONG)bg_h1;
		      bg_h2=(struct Gadget *)NewObjectA(NULL,"buttongclass",\
							bg_taglist);
		      MapTags(bg_taglist,button_map,TRUE);
		      tag=FindTagItem(GA_ID,bg_taglist);tag->ti_Data=4;
		      tag=FindTagItem(GA_RelBottom,bg_taglist);
		      tag->ti_Data=-bbs-2*UD_HEIGHT+1;
		      tag=FindTagItem(GA_RelRight,bg_taglist);tag->ti_Data=-rbs+1;
		      tag=FindTagItem(GA_Image,bg_taglist);
		      tag->ti_Data=(ULONG)up_image;
		      tag=FindTagItem(GA_Previous,bg_taglist);
		      tag->ti_Data=(ULONG)bg_h2;
		      bg_v1=(struct Gadget *)NewObjectA(NULL,"buttongclass",\
							bg_taglist);
		      tag=FindTagItem(GA_ID,bg_taglist);tag->ti_Data=5;
		      tag=FindTagItem(GA_RelBottom,bg_taglist);
		      tag->ti_Data=-bbs-UD_HEIGHT+1;
		      tag=FindTagItem(GA_Image,bg_taglist);
		      tag->ti_Data=(ULONG)down_image;
		      tag=FindTagItem(GA_Previous,bg_taglist);
		      tag->ti_Data=(ULONG)bg_v1;
		      bg_v2=(struct Gadget *)NewObjectA(NULL,"buttongclass",\
							bg_taglist);
		      if(pg_h!=NULL && pg_v!=NULL &&\
			 bg_h1!=NULL && bg_h2!=NULL &&\
			 bg_v1!=NULL && bg_v2!=NULL)
			{
			  /* add Gadgets to Window and refresh them */
			  AddGList(w,pg_h,-1,6,NULL);
			  RefreshGadgets(pg_h,w,NULL);

			  /* create rkm model class */
			  if((rkm_inittags=AllocateTagItems(5))!=NULL && \
			     (rkm=initRKMModClass())!=NULL)
			    {
			      /* set rkm init tags */
			      rkm_inittags[0].ti_Tag=RKMMOD_CurrVal;
			      rkm_inittags[0].ti_Data=0;
			      rkm_inittags[1].ti_Tag=RKMMOD_Limit;
			      rkm_inittags[1].ti_Data=\
				(idev->width-w->GZZWidth)/SCROLL_UNIT;
			      rkm_inittags[2].ti_Tag=RKMMOD_Id;
			      rkm_inittags[2].ti_Data=0;
			      rkm_inittags[3].ti_Tag=ICA_TARGET;
			      rkm_inittags[3].ti_Data=ICTARGET_IDCMP;
			      rkm_inittags[4].ti_Tag=TAG_END;
			      /* create two rkm models and two icc objects */
			      my_rkm_model1=NewObjectA(rkm,NULL,rkm_inittags);
			      tag=FindTagItem(RKMMOD_Id,rkm_inittags);tag->ti_Data=1;
			      tag=FindTagItem(RKMMOD_Limit,rkm_inittags);
			      tag->ti_Data=(idev->height-w->GZZHeight)\
				/SCROLL_UNIT;
			      my_rkm_model2=NewObjectA(rkm,NULL,rkm_inittags);
			      icc_c2p_h=NewObjectA(NULL,"icclass",NULL);
			      icc_c2p_v=NewObjectA(NULL,"icclass",NULL);
			      if(my_rkm_model1!=NULL && my_rkm_model2!=NULL && \
				 icc_c2p_h!=NULL && icc_c2p_v!=NULL)
				{
				  /* do boopsi-connection */
				  /* horizontal gadgets */
				  SetGadgetAttrs(pg_h,w,NULL,\
						 ICA_TARGET,my_rkm_model1,\
						 ICA_MAP,p2c_map);
				  SetGadgetAttrs(bg_h1,w,NULL,\
						 ICA_TARGET,my_rkm_model1,\
						 ICA_MAP,id2d_map);
				  SetGadgetAttrs(bg_h2,w,NULL,\
						 ICA_TARGET,my_rkm_model1,\
						 ICA_MAP,id2u_map);
				  DoMethod(my_rkm_model1,OM_ADDMEMBER,icc_c2p_h);
				  SetAttrs(icc_c2p_h,\
					   ICA_TARGET,pg_h,\
					   ICA_MAP,c2p_map);
				  
				  /* vertical gadgets */
				  SetGadgetAttrs(pg_v,w,NULL,\
						 ICA_TARGET,my_rkm_model2,\
						 ICA_MAP,p2c_map);
				  SetGadgetAttrs(bg_v1,w,NULL,\
						 ICA_TARGET,my_rkm_model2,\
						 ICA_MAP,id2d_map);
				  SetGadgetAttrs(bg_v2,w,NULL,\
						 ICA_TARGET,my_rkm_model2,\
						 ICA_MAP,id2u_map);
				  DoMethod(my_rkm_model2,OM_ADDMEMBER,icc_c2p_v);
				  SetAttrs(icc_c2p_v,\
					   ICA_TARGET,pg_v,\
					   ICA_MAP,c2p_map);
				  
				}
			      else 
				{
				  clean_win(&w);
#ifdef DEBUG
				  fprintf(stderr,"gs intui-open_my_window(): "
					  "can't create rkm model or ic class\n");
#endif
				}
			      
			      FreeTagItems(rkm_inittags);
			    }
			  else 
			    {
			      clean_win(&w);
#ifdef DEBUG
			      fprintf(stderr,"gs intui-open_my_window(): "
				      "can't create rkm model class or allocate "
				      "init tags for rkm model\n");
#endif
			    }
			}
		      else 
			{
			  clean_win(&w);
#ifdef DEBUG
			  fprintf(stderr,"gs intui-open_my_window(): "
				  "can't create gadget; pg_h: %lx; "
				  "pg_v: %lx; bg_h1: %lx; bg_h2: %lx;"
				  "bg_v1: %lx;M bg_v2: %lx\n",pg_h,\
				  pg_v,bg_h1,bg_h2,bg_v1,bg_v2);
#endif
			}
		    }
		  else 
		    {
		      clean_win(&w);
#ifdef DEBUG
		      fprintf(stderr,"gs intui-open_my_window(): can't "
			      "create image\n");
#endif
		    }

		  FreeTagItems(pg_taglist);
		  FreeTagItems(bg_taglist);
		  FreeTagItems(im_taglist);
		}
	      else 
		{
		  clean_win(&w);
#ifdef DEBUG
		  fprintf(stderr,"gs intui-open_my_window(): "
			  "can't allocate taglist for images or gadgets\n");
#endif
		}
	    }
	  else 
	    {
	      clean_win(&w);
#ifdef DEBUG
	      fprintf(stderr,"gs intui-open_my_window(): "
		      "can't allocate taglist for mapping\n");
#endif
	    }
	}
#ifdef DEBUG
      else
	fprintf(stderr,"gs intui-open_my_window(): can't open window\n");
#endif	
      FreeTagItems(win_tags);
    }
#ifdef DEBUG
  else
    fprintf(stderr,"gs intui-open_my_window(): can't allocate tag list"
	    "for window\n");
  fprintf(stderr,"window: %lx\n",w);
#endif
  return w;
}

void clean_win(struct Window **wp)
{
  CloseWindow(*wp);
  *wp=NULL;
}  

void close_my_window(struct Window *w)
{
  /* dispose models */
  DisposeObject(my_rkm_model1);
  DisposeObject(my_rkm_model2);
  /* free private rkm model class */
  freeRKMModClass(rkm);
  /* remove Gadgets from Window */
  RemoveGList(w,pg_h,6);
  DisposeObject(pg_h);
  DisposeObject(pg_v);
  DisposeObject(bg_h1);
  DisposeObject(bg_h2);
  DisposeObject(bg_v1);
  DisposeObject(bg_v2);
  DisposeObject(left_image);
  DisposeObject(right_image);
  DisposeObject(up_image);
  DisposeObject(down_image);
  FreeTagItems(id2u_map);
  FreeTagItems(id2d_map);
  FreeTagItems(c2p_map);
  FreeTagItems(p2c_map);
  CloseWindow(w);
}

void disable_gadgets(struct Window *w)
{
  struct Gadget *actual_gadget=w->FirstGadget;
  struct Gadget *NextGadget_gadget=actual_gadget->NextGadget;
  while(actual_gadget!=NULL)
    {
      OffGadget(actual_gadget,w,NULL);
      actual_gadget=NextGadget_gadget;
      NextGadget_gadget=actual_gadget->NextGadget;
    }
}

void enable_gadgets(struct Window *w)
{
  struct Gadget *actual_gadget=w->FirstGadget;
  struct Gadget *NextGadget_gadget=actual_gadget->NextGadget;
  while(actual_gadget!=NULL)
    {
      OnGadget(actual_gadget,w,NULL);
      actual_gadget=NextGadget_gadget;
      NextGadget_gadget=actual_gadget->NextGadget;
    }
}

void start_render_state(struct Window *w)
{
  SetWindowTitles(w,"Ghostscript rendering bitmap, please wait ...",(UBYTE *)-1);
  disable_gadgets(w);
  render_active=TRUE;
}

void stop_render_state(struct Window *w)
{
  SetWindowTitles(w,"Ghostscript",(UBYTE *)-1);
  enable_gadgets(w);
  render_active=FALSE;
}
