/**
 *
 * ChoX11: XLib replacement for RISC OS
 *
 * Graphics context handling
 *
 * Copyright 2003 by Peter Naulls
 * Written by Peter Naulls and Alan Buckley
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation. No representations are made about the suitability of this
 * software for any purpose.  It is provided "as is" without express or
 * implied warranty.
 *
 */


#define XLIB_ILLEGAL_ACCESS
#define NeedFunctionPrototypes 1
#define NeedNestedPrototypes   1

#include <X11/Xlib.h>
#include <X11/Xutil.h> 

#include <stdio.h>
#include <ctype.h>
#include <kernel.h>
#include <assert.h>

#include "chox11.h"

extern Chox_Font *Chox11_basefont;

void Chox11ClipRects_AddRef(Chox_ClipRects *rects) {
  rects->ref_count++;
}

void Chox11ClipRects_Release(Chox_ClipRects *rects) {
  if (--rects->ref_count == 0) {
    free(rects->rects);
    free(rects);
  }
}

Chox_ClipRects *Chox11ClipRects_Create(int num_rects, XRectangle *rects) {
  Chox_ClipRects *cr = calloc(1,sizeof(Chox_ClipRects));
  int rect;

  cr->ref_count = 1;
  cr->rect_count = num_rects;
  cr->rects = (XRectangle *)calloc(num_rects, sizeof(XRectangle));
  cr->xmin = cr->rects[0].x = rects->x;
  cr->ymin= cr->rects[0].y = rects->y;
  /* Bounds are inclusive */
  cr->xmax = cr->xmin + (cr->rects[0].width = rects->width) - 1;
  cr->ymax = cr->ymin + (cr->rects[0].height = rects->height) - 1;

  for (rect = 1; rect < num_rects; rect++) {
    rects++;
    cr->rects[rect].x = rects->x;
    cr->rects[rect].y = rects->y;
    cr->rects[rect].width = rects->width;
    cr->rects[rect].height = rects->height;
    if (rects->x < cr->xmin) cr->xmin = rects->x;
    if (rects->x + rects->width - 1 > cr->xmax) cr->xmax = rects->x + rects->width - 1;
    if (rects->y < cr->xmin) cr->ymin = rects->y;
    if (rects->y + rects->height - 1 > cr->ymax) cr->ymax = rects->y + rects->height - 1;
  }

  return cr;
}

/* Compare to clip rects and return 1 if equal.
 * If equal will also discard cr2 and make it a equal to cr1 to
 * speed up future comparisons.
 */
int Chox11ClipRects_EqualRects(Chox_ClipRects *cr1, Chox_ClipRects **pcr2) {
  Chox_ClipRects *cr2 = *pcr2;
  int rect;

  if (cr1 == cr2) return 1;
  if (cr1 == NULL || cr2 == NULL) return 0;

  if (cr1->rect_count != cr2->rect_count) return 0;
  if (cr1->xmin != cr2->xmin || cr1->ymin != cr2->ymin
     || cr1->xmax != cr2->xmax || cr1->ymax != cr2->ymax) return 0;

  for (rect = 0; rect < cr1->rect_count; rect++) {
    if (cr1->rects[rect].x != cr2->rects[rect].x
        || cr1->rects[rect].y != cr2->rects[rect].y
        || cr1->rects[rect].width != cr2->rects[rect].width
        || cr1->rects[rect].height != cr2->rects[rect].height) {
      return 0;
    }
  }

  /* If we get here they are equal so make them reference the same structure */
  Chox11ClipRects_Release(*pcr2);
  Chox11ClipRects_AddRef(cr1);
  *pcr2 = cr1;

  return 1;
}

extern GC XCreateGC(

    Display*		 display,
    Drawable		 d,
    unsigned long	 valuemask,
    XGCValues*		 values

) {
  GC gc = calloc(sizeof(struct _XGC), 1);
  XGContext *context;

  /* Need to take note of valuemask */
  context = calloc(sizeof(XGContext), 1);
  assert(context);

  printf("XCreateGC: context = %p valuemask = %d values = %p\n",
          context, valuemask, values);

  if (valuemask & GCFunction)
    context->values.function    = values->function % 16;
  else
    context->values.function    = GXcopy;

  if (valuemask & GCForeground)
    context->values.foreground = values->foreground;
  else
    context->values.foreground = display->screens->black_pixel;

  if (valuemask & GCBackground)
    context->values.background = values->background;
  else
    context->values.background = display->screens->white_pixel;

/* No processing yet for values below here */

  if (valuemask & GCPlaneMask)
    context->values.plane_mask = values->plane_mask;
  else
    context->values.plane_mask = AllPlanes;

  if (valuemask & GCLineWidth)
    context->values.line_width = values->line_width;

  if (valuemask & GCLineStyle)
    context->values.line_style = values->line_style;
  else
    context->values.line_style = LineSolid;

  if (valuemask & GCCapStyle)
    context->values.cap_style = values->cap_style;
  else
    context->values.cap_style = CapButt;

  if (valuemask & GCJoinStyle)
    context->values.join_style = values->join_style;
  else
    context->values.join_style = JoinMiter;

  if (valuemask & GCFillStyle)
    context->values.fill_style = values->fill_style;
  else
    context->values.fill_style = FillSolid;

  if (valuemask & GCFillRule) 
    context->values.fill_rule = values->fill_rule;
  else
    context->values.fill_rule = EvenOddRule;

  if (valuemask & GCTile)
    context->values.tile = values->tile;
  else
    context->values.tile = 0; /* TODO: Should be a pixmap */

  if (valuemask & GCStipple)
    context->values.stipple = values->stipple;
  else
    context->values.stipple = 0; /*TODO: Should be a pixmap filled with 1s*/

  if (valuemask & GCTileStipXOrigin)
    context->values.ts_x_origin = values->ts_x_origin;

  if (valuemask & GCTileStipYOrigin)
    context->values.ts_y_origin = values->ts_y_origin;

  if (valuemask & GCFont)
    context->values.font = values->font;
  else
    context->values.font = (Font)Chox11_basefont;
  Chox11Font_AddRef(Chox11_basefont);  

  if (valuemask & GCSubwindowMode)
    context->values.subwindow_mode = values->subwindow_mode;
  else
    context->values.subwindow_mode = ClipByChildren;

  if (valuemask & GCGraphicsExposures)
    context->values.graphics_exposures = values->graphics_exposures;
  else
    context->values.graphics_exposures = True;

  if (valuemask & GCClipXOrigin)
    context->values.clip_x_origin = values->clip_x_origin;

  if (valuemask & GCClipYOrigin)
    context->values.clip_y_origin = values->clip_y_origin;

  if (valuemask & GCClipMask)
    context->values.clip_mask = values->clip_mask;
  else
    context->values.clip_mask = None;

  if (valuemask & GCDashOffset)
    context->values.dash_offset = values->dash_offset;

  if (valuemask & GCDashList)
    context->values.dashes = values->dashes;
  else
    context->values.dashes = 0; /*TODO: should be list [4,4] */

  if (valuemask & GCArcMode)
    context->values.arc_mode = values->arc_mode;
  else
    context->values.arc_mode = ArcPieSlice;


  printf("XCreateGC: created gc %x\n", (int)gc);

  gc->gid = (GContext)context;
  return gc;
}

extern XFreeGC(

    Display*		 display,
    GC			 gc

) {
  Chox_ClipRects *clip_rects = ((XGContext *)gc->gid)->clip_rects;
  if (clip_rects) Chox11ClipRects_Release(clip_rects);

  free((XGContext *)gc->gid);
  free((XGContext *)gc);
}

extern GContext XGContextFromGC(

    GC			 gc

) {
  printf("XGContextFromGC: getting GContext from gc %x\n", (int)gc);
  return (GContext)gc->gid;
}

extern void XFlushGC(

    Display*		 display,
    GC			 gc

);

extern XChangeGC(

    Display*		 display,
    GC			 gc,
    unsigned long	 valuemask,
    XGCValues*		 values

) {
  XGContext *xgc = (XGContext *)gc->gid;

  printf("XChangeGC: gc = %p, valuemask = %d, values = %p\n",
           gc, valuemask, values);

  if (valuemask & GCFunction)
    xgc->values.function    = values->function % 16;

  if (valuemask & GCForeground)
    xgc->values.foreground = values->foreground;

  if (valuemask & GCBackground)
    xgc->values.background = values->background;

/* No processing yet for values below here */

  if (valuemask & GCPlaneMask)
    xgc->values.plane_mask = values->plane_mask;

  if (valuemask & GCLineWidth)
    xgc->values.line_width = values->line_width;

  if (valuemask & GCLineStyle)
    xgc->values.line_style = values->line_style;

  if (valuemask & GCCapStyle)
    xgc->values.cap_style = values->cap_style;

  if (valuemask & GCJoinStyle)
    xgc->values.join_style = values->join_style;

  if (valuemask & GCFillStyle)
    xgc->values.fill_style = values->fill_style;

  if (valuemask & GCFillRule) 
    xgc->values.fill_rule = values->fill_rule;

  if (valuemask & GCTile)
    xgc->values.tile = values->tile;

  if (valuemask & GCStipple)
    xgc->values.stipple = values->stipple;

  if (valuemask & GCTileStipXOrigin)
    xgc->values.ts_x_origin = values->ts_x_origin;

  if (valuemask & GCTileStipYOrigin)
    xgc->values.ts_y_origin = values->ts_y_origin;

  if (valuemask & GCFont)
    xgc->values.font = values->font;

  if (valuemask & GCSubwindowMode)
    xgc->values.subwindow_mode = values->subwindow_mode;

  if (valuemask & GCGraphicsExposures)
    xgc->values.graphics_exposures = values->graphics_exposures;

  if (valuemask & GCClipXOrigin)
    xgc->values.clip_x_origin = values->clip_x_origin;

  if (valuemask & GCClipYOrigin)
    xgc->values.clip_y_origin = values->clip_y_origin;

  if (valuemask & GCClipMask) {
    xgc->values.clip_mask = values->clip_mask;
    /* Setting clip mask clear clip rect */
    if (xgc->clip_rects) {
      Chox11ClipRects_Release(xgc->clip_rects);
      xgc->clip_rects = NULL;
    }
  }

  if (valuemask & GCDashOffset)
    xgc->values.dash_offset = values->dash_offset;

  if (valuemask & GCDashList)
    xgc->values.dashes = values->dashes;

  if (valuemask & GCArcMode)
    xgc->values.arc_mode = values->arc_mode;
}

extern XCopyGC(

    Display*		 display,
    GC			 src,
    unsigned long	 valuemask,
    GC			 dest

) {
  puts("XCopyGC (not implemented)");
}

extern Status XGetGCValues(

    Display*		 display,
    GC			 gc,
    unsigned long	 valuemask,
    XGCValues*		 values_return

) {
//  printf("XGetGCValues: gc = %x valuemask = %x\n", gc, valuemask);
  if (valuemask & ~(GCForeground|GCBackground|GCFunction))
    printf("XGetGCValues: Requesting unimplemented value, gc = %x valuemask = %x\n", gc, valuemask);

  if (valuemask & GCForeground)
    values_return->foreground = ((XGContext *)gc->gid)->values.foreground;

  if (valuemask & GCBackground)
    values_return->background = ((XGContext *)gc->gid)->values.background;

  if (valuemask & GCFunction)
   values_return->function = ((XGContext *)gc->gid)->values.function;

  return 0;  
}

extern XSetArcMode(

    Display*		 display,
    GC			 gc,
    int			 arc_mode

) {
  puts("XSetArcMode (not implemented)");
}

extern XSetBackground(

    Display*		 display,
    GC			 gc,
    unsigned long	 background

) {
//  printf("XSetBackground: %x\n", background);

  ((XGContext *)gc->gid)->values.background = background;

  return Success;
}

extern XSetClipMask(

    Display*		 display,
    GC			 gc,
    Pixmap		 pixmap

) {
  XGContext *xgc = (XGContext *)gc->gid;

  printf("XSetClipmask gc = %p, pixmap = %p\n", gc, pixmap);
  xgc->values.clip_mask = pixmap;
  /* Setting clip mask clear clip rect */
  if (xgc->clip_rects) {
    Chox11ClipRects_Release(xgc->clip_rects);
    xgc->clip_rects = NULL;
  }
}

extern XSetClipOrigin(

    Display*		 display,
    GC			 gc,
    int			 clip_x_origin,
    int			 clip_y_origin

) {
//  puts("XSetClipOrigin (not implemented)");
  ((XGContext *)gc->gid)->values.clip_x_origin = clip_x_origin;
  ((XGContext *)gc->gid)->values.clip_y_origin = clip_y_origin;
}

extern XSetClipRectangles(

    Display*		 display,
    GC			 gc,
    int			 clip_x_origin,
    int			 clip_y_origin,
    XRectangle*		 rectangles,
    int			 n,
    int			 ordering

) {
  XGContext *xgc = (XGContext *)gc->gid;

  printf("XSetClipRectangles: gc = %p clipx = %d clipy = %d n = %d ordering = %d\n",
         gc, clip_x_origin, clip_y_origin, n, ordering);

  {
    int i = n;
    while (i-- > 0) {
      printf("%d %d %d %d\n",
           rectangles[i].x, rectangles[i].y,
           rectangles[i].width, rectangles[i].height);
    }
  }

  /* Setting clip rects clears clip mask */
  xgc->values.clip_mask = None;

  if (xgc->clip_rects) Chox11ClipRects_Release(xgc->clip_rects);
  xgc->clip_rects = Chox11ClipRects_Create(n, rectangles);

  xgc->values.clip_x_origin = clip_x_origin;
  xgc->values.clip_y_origin = clip_y_origin;
}

extern XSetDashes(

    Display*		 display,
    GC			 gc,
    int			 dash_offset,
    _Xconst char*	 dash_list,
    int			 n

) {
  puts("XSetDashes (not implemented)");
}

extern XSetFillRule(

    Display*		 display,
    GC			 gc,
    int			 fill_rule

) {
  puts("XSetFillRule (not implemented)");
}

extern XSetFillStyle(

    Display*		 display,
    GC			 gc,
    int			 fill_style

) {
  printf("XSetFileStyle %x\n", fill_style);

  ((XGContext *)gc->gid)->values.fill_style = fill_style;
}

extern XSetForeground(

    Display*		 display,
    GC			 gc,
    unsigned long	 foreground

) {
  printf("XSetForeground: %x\n", foreground);

  ((XGContext *)gc->gid)->values.foreground = foreground;

  return Success;
}


extern XSetFunction(

    Display*		 display,
    GC			 gc,
    int			 function

) {
  printf("XSetFunction: %d\n", function);
  ((XGContext *)gc->gid)->values.function = (function & 15);

}

extern XSetGraphicsExposures(

    Display*		 display,
    GC			 gc,
    Bool		 graphics_exposures

) {
  puts("XSetGraphicsExposures (not implemented)");
}

extern XSetLineAttributes(

    Display*		 display,
    GC			 gc,
    unsigned int	 line_width,
    int			 line_style,
    int			 cap_style,
    int			 join_style

) {
  printf("XSetLineAttributes (not implemented)");
}

extern XSetPlaneMask(

    Display*		 display,
    GC			 gc,
    unsigned long	 plane_mask

) {
  puts("XsetPlaneMask (not implemented)");
}


extern XSetState(

    Display*		 display,
    GC			 gc,
    unsigned long 	 foreground,
    unsigned long	 background,
    int			 function,
    unsigned long	 plane_mask

) {
  puts("XSetState (not implemented)");
}

extern XSetStipple(

    Display*		 display,
    GC			 gc,
    Pixmap		 stipple

) {
  XGContext *xgc = (XGContext *)gc->gid;
  puts("XSetStipple\n)");

  xgc->values.stipple = stipple;
}

extern XSetTSOrigin(

    Display*		 display,
    GC			 gc,
    int			 ts_x_origin,
    int			 ts_y_origin

) {
  XGContext *xgc = (XGContext *)gc->gid;
  puts("XSetTSOrigin\n");

  xgc->values.ts_x_origin = ts_x_origin;
  xgc->values.ts_y_origin = ts_y_origin;
}

extern XSetTile(

    Display*		 display,
    GC			 gc,
    Pixmap		 tile

) {
  XGContext *xgc = (XGContext *)gc->gid;
  puts("XSetTile\n");
  xgc->values.tile = tile;
}
