/* XtDrawContextWraps - Drawing context wraps for Xlib/Xt interface.

   Copyright (C) 1995 Free Software Foundation, Inc.

   Written by:  Adam Fedor <fedor@boulder.colorado.edu>
   Date: Nov 1995
   
   This file is part of the GNU Objective C User Implementation library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.
   
   This library 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
   Library General Public License for more details.
   
   You should have received a copy of the GNU Library General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   */

#include "XtDrawContext.h"
#include "DrawContextWraps.h"
#include "XtDrawPrivate.h"
#include "XtGStateOps.h"
#include "xttools.h"
#include <Foundation/NSString.h>
#include <Foundation/NSArray.h>
#include <Foundation/NSValue.h>

/* Keep track of what windows we control and which ones are on-screen */
static NSMutableArray *screenlist;
static NSMutableArray *windowlist;

/* This is a dummy function so that we can force the methods in this
   file to be loaded by non-NeXT linkers. */
const char *
dpsclientXtDrawContextWraps()
{
    return "dpsclientXtDrawContextWraps";
}

@implementation XtDrawContext (DPSMouseKey)

- (void) DPShidecursor
{
}

- (void) DPSshowcursor
{
}

- (void) DPSobscurecursor
{
}

- (void) DPSrevealcursor
{
}

- (void) DPSsetcursor: (float) x : (float)y : (float)hotx : (float)hoty
{
}

- (void) DPSadjustcursor: (float)dx : (float)dy
{
}

- (void) DPScurrentmouse: (int)winNum : (float *)x : (float *)y
{
}

- (void) DPSsetmouse: (float)x : (float)y
{
}

- (void) DPSbuttondown: (int *)truth
{
}

- (void) DPSrightbuttondown: (int *)truth
{
}

- (void) DPSstilldown: (int)eNum : (int *)truth
{
}

- (void) DPSrightstilldown: (int)eNum : (int *)truth
{
}

@end

@implementation XtDrawContext (DPSEvent)

- (void) DPSprinteventtimes
{
}

- (void) DPSiniteventtimes
{
}

- (void) DPSposteventbycontext: (int)type : (float)x : (float)y : (int)time : (int)flags : (int)win : (int)subtype : (int)data1 : (int)data2 : (int)context : (int *)success
{
}

- (void) DPSsetflushexposures: (int)flag
{
}

- (void) DPSsetwaitcursorenabled: (int)flag
{
}

- (void) DPScurrentwaitcursorenabled: (int *)flag
{
}

- (void) DPSsetactiveapp: (int)cntxt
{
}

- (void) DPScurrentactiveapp: (int *)cntxt
{
}

@end

@implementation XtDrawContext (DPSMisc)

- (void) DPSosname: (int)size : (char *)str
{
}

- (void) DPSostype: (int *)result
{
}

- (void) DPSnextrelease: (int)size : (char *)result
{
}

- (void) DPScurrentrusage: (float *)now : (float *)uTime : (float *)sTime : (int *)msgSend : (int *)msgRcv : (int *)nSignals : (int *)nVCSw : (int *)nIvCSw
{
}

- (void) DPSplaysound: (const char *)name : (int)priority
{
}

- (void) DPScleardictstack
{
}

- (void) DPScurrentuser: (int *)uid : (int *)gid
{
}

- (void) DPSsetwriteblock: (int)flag
{
}

- (void) DPScurrentwriteblock: (int *)flag
{
}

- (void) DPSmachportdevice: (int)w : (int)h : (const int *)bbox : (int)bboxSize : (const float *)matrix : (const char *)host : (const char *)port : (const char *)pixelDict
{
}

- (void) DPScountframebuffers: (int *)count
{
}

- (void) DPSframebuffer: (int)index : (int)nameLen : (char *)name : (int *)slot : (int *)unit : (int *)ROMid : (int *)x : (int *)y : (int *)w : (int *)h : (int *)depth
{
}

- (void) DPSsetframebuffertransfer
{
}

- (void) DPScurrentframebuffertransfer
{
}

@end

@implementation XtDrawContext (DPSWindow)

/* Create the window and screen list if necessary, add the root window to
   the window list as window 0 */
- (void) _checkWindowlist
{
  window_device_t window;
  int x, y;
  unsigned width, height;

  if (windowlist)
    return;

  windowlist = [[NSMutableArray allocWithZone: globalZone] 
	      initWithCapacity:8];
  screenlist = [[NSMutableArray allocWithZone: globalZone] 
	      initWithCapacity:8];

  window.ident  = RootWindow(xwctxt.display, XDefaultScreen(xwctxt.display));
  window.type   = NX_NONRETAINED;
  window.number = 0;
  if (window.ident)
    XGetGeometry(xwctxt.display, window.ident, &window.root, 
		 &x, &y, &width, &height,
		 &window.border, &window.depth);

  window.frame = NSMakeRect(x, y, width, height);
  [windowlist addObject: [NSValue value: &window 
			withObjCType: @encode(window_device_t)]];
  

}

/* Sets up a backing pixmap when a window is created or resized.  This is
   only done if the Window is buffered or retained.  Note that this routine
   will likely not get called until it's absolutely needed (like when
   we create a gstate for the window). */
- (void) _createBuffer: (window_device_t *)window resize: (BOOL)resize
{
  GC gc;
  XGCValues gcv;

  if (window->type == NX_NONRETAINED)
    return;
  if (!resize && window->buffer)
    return;

  /* If this was a resize, then create a new pixmap and free the old one.
     I assume a resize event will trigger a redisplay of the data, and we
     don't need to keep any information from the old pixmap. */
  if (resize && window->buffer)
    XFreePixmap(xwctxt.display, window->buffer);

  if (window->depth == 0)
    window->depth = DefaultDepth(xwctxt.display,DefaultScreen(xwctxt.display));
  if (NSWidth(window->frame) == 0 && NSHeight(window->frame) == 0)
    {
      fprintf(stderr, "DPS: Cant get window frame to create buffer\n");
      window->buffer = 0;
      return;
    }

  window->buffer = XCreatePixmap(xwctxt.display, window->root,
				 NSWidth(window->frame),
				 NSHeight(window->frame),
				 window->depth);
  if (!window->buffer)
    {
      fprintf(stderr, "DPS: Unable to create buffer\n");
      return;
    }

  /* Fill the pixmap with white */
  gcv.function = GXcopy;
  gcv.plane_mask = AllPlanes;
  gcv.foreground = xtGrayToPixel(&xwctxt, 1.0);

  /* Evidently, you can't create a GC using an unrealized window */
  if (window->ident)
    gc = XCreateGC(xwctxt.display, window->ident,
		   GCFunction | GCForeground | GCPlaneMask , &gcv);
  else
    gc = XCreateGC(xwctxt.display, window->root,
		   GCFunction | GCForeground | GCPlaneMask , &gcv);
  
  XFillRectangle(xwctxt.display,
		 window->buffer,
		 gc,
		 0, 0, 
		 NSWidth(window->frame),
		 NSHeight(window->frame));
  XFreeGC(xwctxt.display, gc);
}

/* Fill in various window parameters. This could be called several times if
   the window is part of an Alternate widget set (Motif), since there is
   no associated window for the widget until the widget is realized */
- (window_device_t) _windowParameters: (window_device_t)window
{
  int x, y;
  unsigned width, height;
  
  if (window.ident)
    return window;

  window.ident  = XtWindow((Widget)window.widget);
  if (window.ident)
    {
      XGetGeometry(xwctxt.display, window.ident, &window.root, 
		 &x, &y, &width, &height,
		 &window.border, &window.depth);
      window.frame = NSMakeRect(x, y, width, height);
    }
  else
    {
      window.root = DefaultRootWindow(xwctxt.display);
      window.depth = DefaultDepth(xwctxt.display, 
				  DefaultScreen(xwctxt.display));
    }
  return window;
}

/* Not a DPS function - used by programs that have their own widget set but
   still want to use DrawContexts to do drawing (aka Motif). */
- (void) addwindow: (Widget)win : (float)x : (float)y : (float)w : (float)h 
                  : (int)type : (int *)num
{
  window_device_t window;

  window.ident  = XtWindow(win);
  window.widget = win;
  window.type   = type;
  window.buffer = 0;
  window.frame  = NSMakeRect(x, y, w, h);
  window = [self _windowParameters: window];

  CHECK_NULL_OUTPUT(num);
  [self _checkWindowlist];
  window.number = [windowlist count];
  *num = window.number;
  [windowlist addObject: [NSValue value: &window 
			withObjCType: @encode(window_device_t)]];
}

- (void) DPSwindow: (float)x : (float)y : (float)w : (float)h : (int)type : (int *)num
{
  window_device_t window, root;

  [self _checkWindowlist];
  [[windowlist objectAtIndex: 0] getValue: &root];

  window.frame = NSMakeRect(x, NSHeight(root.frame)-y, w, h);
  window.type  = type;
  window.ident = XCreateSimpleWindow(xwctxt.display, root.ident,
				     x, NSHeight(window.frame), w, h,
				     0, 
				     xtGrayToPixel(&xwctxt, 0.0),
				     xtGrayToPixel(&xwctxt, 1.0)
				     );
  window.buffer = 0;
  CHECK_NULL_OUTPUT(num);
  [self _checkWindowlist];
  window.number = [windowlist count];
  *num = window.number;
  [windowlist addObject: [NSValue value: &window 
			withObjCType: @encode(window_device_t)]];
}

- (void) DPStermwindow: (int)num
{
}

- (window_device_t) _windowdevice: (int)num
{
  window_device_t window;

  [self _checkWindowlist];
  if (num >= [windowlist count])
    {
      window.number = -1;
      INVOKE_ERROR_VAL(DPSinvalidparam, @"Invalid window number", window);
    }

  [[windowlist objectAtIndex: num] getValue: &window];

  if (window.ident == 0)
    {
      window = [self _windowParameters: window];
/* FIXME: This is just for compatibility with libobjects-0.1.14, take it
	out when a new version comes out */
#if 0
      [windowlist replaceObjectAtIndex: num
          withObject: [NSValue value: &window 
			withObjCType: @encode(window_device_t)]];
#else
      [windowlist replaceObjectAtIndex: num
          with: [NSValue value: &window 
			withObjCType: @encode(window_device_t)]];
#endif
    }
  if (window.buffer == 0)
    {
      [self _createBuffer: &window resize: NO];
#if 0
      [windowlist replaceObjectAtIndex: num
          withObject: [NSValue value: &window 
			withObjCType: @encode(window_device_t)]];
#else
      [windowlist replaceObjectAtIndex: num
          with: [NSValue value: &window 
			withObjCType: @encode(window_device_t)]];
#endif
    }
  return window;
}

- (void) DPSwindowdevice: (int)num
{
  [gstate setWindowDevice: [self _windowdevice: num]];
  // FIXME: should set the default matrix
  [gstate DPSinitmatrix];
}

/* Not a DPS function - used by programs that have their own widget set but
   still want to use DrawContexts to do drawing (aka Motif). */
- (void) DPSwindowdeviceview: (int)num : (void *)view
{
  [gstate setWindowDevice: [self _windowdevice: num] andView: (View *)view];
  // FIXME: should set the default matrix
  [gstate DPSinitmatrix];
}


- (void) DPSwindowdeviceround: (int)num
{
  [gstate setWindowDevice: [self _windowdevice: num]];
  // FIXME: should set the default matrix
  [gstate DPSinitmatrix];
}

- (void) DPScurrentwindow: (int *)num
{
  window_device_t window;

  window = [gstate windowDevice];
  // FIXME: check for invalid window device?
  *num = window.number;
}

- (void) DPSflushgraphics
{
  [gstate flushRectIfNeeded:NULL];
}

- (void) DPSorderwindow: (int)op : (int)otherWin : (int)winNum
{
  id win_obj;
  window_device_t window;
  
  window = [self _windowdevice: winNum];
  if (window.number < 0)
    return;
  
  win_obj = [windowlist objectAtIndex: winNum];
  if (op == NX_OUT)
    {
      XUnmapWindow(xwctxt.display, window.ident);
      if (![screenlist indexOfObject: win_obj])
	{
	  /* FIXME: should this be an error? */
	  return;
	}
      [screenlist removeObject: win_obj];
      return;
    }
  
  /* FIXME: Should raise/lower window before mapping. What's the best way? */
  if (![screenlist indexOfObject: win_obj])
    {
      XMapWindow(xwctxt.display, window.ident);
      [screenlist addObject: win_obj];
    }
}

- (void) DPSmovewindow: (float)x : (float)y : (int)num
{
}

- (void) DPSplacewindow: (float)x : (float)y : (float)w : (float)h : (int)win
{
}

- (void) DPSfrontwindow: (int *)num
{
}

- (void) DPSfindwindow: (float)x : (float)y : (int)op : (int)otherWin : (float *)lx : (float *)ly : (int *)winFound : (int *)didFind
{
}

- (void) DPScurrentwindowbounds: (int)num : (float *)x : (float *)y : (float *)w : (float *)h
{
}

- (void) DPSseteventmask: (int)mask : (int)num
{
}

- (void) DPScurrenteventmask: (int)num : (int *)mask
{
}

- (void) DPSsetexposurecolor
{
}

- (void) DPSsetsendexposed: (int)truth : (int)num
{
}

- (void) DPSsetautofill: (int)truth : (int)num
{
}

- (void) DPSsetwindowdict: (int)num
{
}

- (void) DPScurrentwindowdict: (int)num
{
}

- (void) DPScurrentwindowalpha: (int)win : (int *)alpha
{
}

- (void) DPScountscreenlist: (int)context : (int *)count
{
}

- (void) DPSscreenlist: (int)context : (int)count : (int *)windows
{
}

- (void) DPScurrentdeviceinfo: (int)win : (int *)minbps : (int *)maxbps : (int *)color
{
}

- (void) DPSsetowner: (int)owner : (int)win
{
}

- (void) DPScurrentowner: (int)win : (int *)owner
{
}

- (void) DPSbasetoscreen: (float)x : (float)y : (float *)xp : (float *)yp
{
}

- (void) DPSbasetocurrent: (float)x : (float)y : (float *)xp : (float *)yp
{
}

- (void) DPSscreentocurrent: (float)x : (float)y : (float *)xp : (float *)yp
{
}

- (void) DPSscreentobase: (float)x : (float)y : (float *)xp : (float *)yp
{
}

- (void) DPScurrenttoscreen: (float)x : (float)y : (float *)xp : (float *)yp
{
}

- (void) DPScurrenttobase: (float)x : (float)y : (float *)xp : (float *)yp
{
}

- (void) DPSdumpwindow: (int)level : (int)win
{
}

- (void) DPSdumpwindows: (int)level : (int)context
{
}

- (void) DPSsetwindowtype: (int)type : (int)win
{
}

- (void) DPSsetwindowlevel: (int)level : (int)win
{
}

- (void) DPScurrentwindowlevel: (int)win : (int *)level
{
}

- (void) DPScountwindowlist: (int)context : (int *)count
{
}

- (void) DPSwindowlist: (int)context : (int)count : (int *)windows
{
}

- (void) DPSsetwindowdepthlimit: (int)limit : (int)win
{
}

- (void) DPScurrentwindowdepthlimit: (int)win : (int *)limit
{
}

- (void) DPScurrentwindowdepth: (int)win : (int *)depth
{
}

- (void) DPSsetdefaultdepthlimit: (int)limit
{
}

- (void) DPScurrentdefaultdepthlimit: (int *)limit
{
}

- (void) DPSsetshowpageprocedure: (int)win
{
}

- (void) DPScurrentshowpageprocedure
{
}

@end

@implementation XtDrawContext (DPSGraphics)

- (void) DPSsettrackingrect: (float)x : (float)y : (float)w : (float)h : (int)leftreqd : (int)rightreqd : (int)in1 : (int)userData : (int)tNum : (int)gstateNum
{
}

- (void) DPScleartrackingrect: (int)nrectnum : (int)gstateNum
{
}

- (void) DPSnewinstance
{
}

- (void) DPShideinstance: (float)x : (float)y : (float)w : (float)h
{
}

- (void) DPSsetinstance: (int)truth
{
}

- (void) DPScomposite: (float)x : (float)y : (float)w : (float)h 
                     : (int)gstateNum : (float)dx : (float)dy : (int)op
{
  NSRect rect;
  NSPoint p;
  Drawable source;

  if (gstateNum >= [glist count])
    INVOKE_ERROR(DPSinvalidid, @"Invalid gstate");

  source = [(XtGState *)[glist objectAtIndex: gstateNum] drawable];
  if (!source) 
    INVOKE_ERROR(DPSundefined, @"Composite drawable not realized\n");

  rect = NSMakeRect(x, y, w, h);
  p    = NSMakePoint(dx, dy);

  switch (op)
    {
    case NX_CLEAR:
      break;
    case NX_COPY:
    case NX_SOVER:
      [gstate copyDrawable:source fromRect: &rect toPoint: &p];
      break;
    case NX_SIN:
    case NX_SOUT:
    case NX_SATOP:
    case NX_DOVER:
    case NX_DIN:
    case NX_DOUT:
    case NX_DATOP:
    case NX_XOR:
    case NX_PLUSD:
    case NX_HIGHLIGHT:
    case NX_PLUSL:
    default:
      break;
    }
}

- (void) DPScompositerect: (float)x : (float)y : (float)w : (float)h : (int)op
{
}

- (void) DPSdissolve: (float)x : (float)y : (float)w : (float)h : (int)gstateNum : (float)dx : (float)dy : (float)delta
{
}

- (void) DPSsizeimage: (float)x : (float)y : (float)w : (float)h : (int *)dw : (int *)dh : (int *)depth : (float *)m : (int *)multiproc : (int *)numColors
{
}

- (void) DPSreadimage
{
}

- (void) DPSalphaimage
{
}

- (void) DPSsetalpha: (float)a
{
}

- (void) DPScurrentalpha: (float *)alpha
{
}

@end

