#include "xlisp.h"
#include "xlstat.h"
#include "xlgraph.h"

/**************************************************************************/
/**                                                                      **/
/**                    Action Installation Functions                     **/
/**                                                                      **/
/**************************************************************************/

VOID StGWSetFreeMem(StGWWinInfo *gwinfo, void (*FreeMem)(IVIEW_WINDOW))
{
  if (gwinfo == nil) return;
  else gwinfo->FreeMem =  FreeMem;
}

int StGWIdleOn(StGWWinInfo *gwinfo)
{
  
  if (gwinfo == nil) return(FALSE);
  else return(gwinfo->idleOn);
}

VOID StGWSetIdleOn(StGWWinInfo *gwinfo, int on)
{
  if (gwinfo != nil) gwinfo->idleOn = on;
}

/**************************************************************************/
/**                                                                      **/
/**                      Window Management Functions                     **/
/**                                                                      **/
/**************************************************************************/

VOID StGWShowWindow(StGWWinInfo *gwinfo)
{
  WindowPtr w;
  
  if (gwinfo == nil || (w = gwinfo->window) == nil) return;
  else {
    MyShowWindow(w);
    if (! gwinfo->initialized)
      graph_update_action(gwinfo, TRUE);
  }
}

VOID StGWRemove(StGWWinInfo *gwinfo)
{
  WindowPtr w;
  
  if (gwinfo == nil || (w = gwinfo->window) == nil) return;
  else SkelRmveWind(w);
}

VOID StGWWhileButtonDown(StGWWinInfo *gwinfo, void (*action) (WindowPtr,int, int),int motionOnly)
{
  Point pt;
  GrafPtr savePort;
  WindowPtr w;
  
  if (gwinfo == nil || (w = gwinfo->window) == nil) return;
  
  GetPort(&savePort);
  SetPort(w);
  while (StillDown()) {
    SetPort(w);
    mac_do_cursor(gwinfo);
    GetMouse(&pt);
    if (gwinfo->mouse_x != pt.h || gwinfo->mouse_y != pt.v || ! motionOnly) {
      gwinfo->mouse_x = pt.h; gwinfo->mouse_y = pt.v;
      if (action != nil) (*action)(w, pt.h, pt.v);
    }
  }
  SetPort(savePort);
}

static TitleBarHeight(WindowPtr w)
{
  Rect r;
  Point pt;
  WindowPeek wind = (WindowPeek) w;
  GrafPtr savePort;
  
  GetPort(&savePort);
  SetPort(w);
  SetPt(&pt, 0, 0);
  LocalToGlobal(&pt);
  r = (*(wind->strucRgn))->rgnBBox;
  SetPort(savePort);
  return(pt.v - r.top);
}

VOID StWSetLocation(WindowPtr w, int left, int top, int frame)
{
  int adjust;
  
  if (w == nil) return;
  adjust = (frame) ? GetMBarHeight() + TitleBarHeight(w) : GetMBarHeight();
  MoveWindow(w, left, top + adjust, FALSE);
}

VOID StWGetLocation(WindowPtr w, int *left, int *top, int frame)
{
  GrafPtr savePort;
  Point pt;
  int adjust;
  
  if (w == nil) return;
  else {
    adjust = (frame) ? GetMBarHeight() + TitleBarHeight(w) : GetMBarHeight();
    GetPort(&savePort);
    SetPort(w);
    pt.h = w->portRect.left;
    pt.v = w->portRect.top;
    LocalToGlobal(&pt);
    if (left != nil) *left = pt.h;
    if (top != nil) *top = pt.v - adjust;
    SetPort(savePort);
  }
}

VOID StGWSetSize(StGWWinInfo *gwinfo, int width, int height, int frame)
{
  WindowPtr w;
  if (gwinfo == nil || (w = gwinfo->window) == nil) return;
  else StWSetSize(w, width, height, frame);
}

VOID StWSetSize(WindowPtr w, int width, int height, int frame)
{
  if (w == nil) return;
  SizeWindow(w, width, height - ((frame) ? TitleBarHeight(w) : 0), TRUE);
  SkelTriggerUpdate(w);
  SkelDoUpdates();
}

VOID StWGetSize(WindowPtr w, int *width, int *height, int frame)
{
  
  if (w == nil) return;
  else {
    if (width != nil) *width = w->portRect.right - w->portRect.left;
    if (height != nil) *height = w->portRect.bottom - w->portRect.top 
                               + ((frame) ? TitleBarHeight(w) : 0);
  }
}

/**************************************************************************/
/**                                                                      **/
/**             Window State Access and Mutation Functions               **/
/**                                                                      **/
/**************************************************************************/

VOID SetHardwareState(StGWWinInfo *gwinfo)
{
  GrafPtr savePort;
  WindowPtr w;
  
  if (gwinfo == nil || (w = gwinfo->window) == nil) return;

  GetPort(&savePort);
  SetPort(w);
  
  set_fore_color(gwinfo);
  set_back_color(gwinfo);
  if (gwinfo->drawMode == 0) w->pnMode = patCopy;
  else w->pnMode = patXor;
  if (gwinfo->drawMode == 0) {
    w->txMode = srcOr;
    gwinfo->symbolMode = srcCopy;
  }
  else {
    w->txMode = srcXor;
    gwinfo->symbolMode = srcXor;
  }
  
  /* set line type */
  if (gwinfo->lineType != 0 && gwinfo->drawColor != gwinfo->backColor)
    PenPat(&gray);
  else PenPat(&black);
  
  /* set pen size */
  PenSize(gwinfo->lineWidth, gwinfo->lineWidth);
  
  adjust_graph_workport(gwinfo);
  SetPort(savePort);
}

static int get_state(StGWWinInfo *gwinfo, int which)
{
  if (gwinfo == nil) return(0);
  switch (which) {
  case 'W': return(gwinfo->canvasWidth);
  case 'H': return(gwinfo->canvasHeight);
  case 'L': return(gwinfo->lineType);
  case 'M': return(gwinfo->drawMode);
  case 'D': return(gwinfo->drawColor);
  case 'B': return(gwinfo->backColor);
  case 'C': return(gwinfo->use_color);
  }
  return 0;
}

int StGWCanvasWidth(StGWWinInfo *gwinfo)  { return (get_state(gwinfo, 'W')); }
int StGWCanvasHeight(StGWWinInfo *gwinfo) { return (get_state(gwinfo, 'H')); }
int StGWLineType(StGWWinInfo *gwinfo)     { return (get_state(gwinfo, 'L')); }
int StGWDrawMode(StGWWinInfo *gwinfo)     { return (get_state(gwinfo, 'M')); }

ColorCode StGWDrawColor(StGWWinInfo *gwinfo)
{
  return ((ColorCode) get_state(gwinfo, 'D'));
}

ColorCode StGWBackColor(StGWWinInfo *gwinfo)
{
  return ((ColorCode) get_state(gwinfo, 'B'));
}

int StGWUseColor(StGWWinInfo *gwinfo) { return (get_state(gwinfo, 'C')); }

VOID StGWGetLineWidth(StGWWinInfo *gwinfo, int *width)
{
  if (gwinfo == nil) return;
  if (width != nil) *width = gwinfo->lineWidth;
}

static VOID set_state(StGWWinInfo *gwinfo, int which, int value)
{
  int changed;
  
  if (gwinfo == nil) return;
  switch (which) {
  case 'L': if ((changed = (value != gwinfo->lineType))) gwinfo->lineType = value;  break;
  case 'M': if ((changed = (value != gwinfo->drawMode))) gwinfo->drawMode = value;  break;
  case 'D': if ((changed = (value != gwinfo->drawColor))) gwinfo->drawColor = value; break;
  case 'B': if ((changed = (value != gwinfo->backColor))) gwinfo->backColor = value; break;
  case 'C': if ((changed = (value != gwinfo->use_color))) 
              gwinfo->use_color = (StScreenHasColor()) ? value : FALSE;
            break;
  }
  if (changed) SetHardwareState(gwinfo);
  if (changed && which == 'B') DrawGWGrowBox(gwinfo);
}

VOID StGWSetLineType(StGWWinInfo *gwinfo, int type)
{
  set_state(gwinfo, 'L', type);
}

VOID StGWSetDrawMode(StGWWinInfo *gwinfo, int mode)
{
  set_state(gwinfo, 'M', mode);
}

VOID StGWSetDrawColor(StGWWinInfo *gwinfo, ColorCode color)
{
  set_state(gwinfo, 'D', (ColorCode) color);
}

VOID StGWSetBackColor(StGWWinInfo *gwinfo, ColorCode color)
{
  set_state(gwinfo, 'B', (ColorCode) color);
}

VOID StGWSetUseColor(StGWWinInfo *gwinfo, int use)
{
  set_state(gwinfo, 'C', use);
}

VOID StGWSetLineWidth(StGWWinInfo *gwinfo, int width)
{
  int changed;
  
  if (gwinfo == nil) return;
  changed = (width != gwinfo->lineWidth);
  if (changed) {
    gwinfo->lineWidth = width;
    SetHardwareState(gwinfo);
  }
}

VOID StGWReverseColors(StGWWinInfo *gwinfo)
{
  ColorCode backColor, drawColor;
  LVAL object;
  
  object = StGWGetObject(gwinfo);

  backColor = StGWBackColor(gwinfo);
  drawColor = StGWDrawColor(gwinfo);
  if (backColor != drawColor) {
    StGWSetBackColor(gwinfo, drawColor);
    StGWSetDrawColor(gwinfo, backColor);
    StGWObRedraw(object);
  }
}

VOID StGWGetViewRect(StGWWinInfo *gwinfo, int *left, int *top, int *width, int *height)
{
  WindowPtr w;
  
  if (gwinfo != nil && (w = gwinfo->window) != nil) {
    if (left != nil) *left = w->portRect.left;
    if (top != nil) *top = w->portRect.top;
    if (width != nil) *width = w->portRect.right - w->portRect.left - 15;
    if (height != nil) *height = w->portRect.bottom - w->portRect.top;
    if (height != nil && StGWHasHscroll(gwinfo)) *height -= 15;
  }
  else {
    if (left != nil) *left = 0;
    if (top != nil) *top = 0;
    if (width != nil) *width = 1;
    if (height != nil) *height = 1;
  }
}

/**************************************************************************/
/**                                                                      **/
/**                       Window Scrolling Functions                     **/
/**                                                                      **/
/**************************************************************************/

static VOID set_has_scroll(StGWWinInfo *gwinfo, int which, int has, int size)
{
  WindowPtr w;
  GrafPtr savePort;
  int view_size, value, old_has;
  ControlHandle ctl;
  LVAL object;
  
  if (gwinfo == nil || (w = gwinfo->window) == nil) return;

  GetPort(&savePort);
  SetPort(w);
  
  if (has && size <= 0) xlfail("size must be positive");
  object = StGWGetObject(gwinfo);

  ClipRect(&w->portRect);

  ctl = (which == 'H') ? gwinfo->hscroll : gwinfo->vscroll;
   
  old_has = (which == 'H') ? gwinfo->hasHscroll : gwinfo->hasVscroll;
  if (which == 'H') gwinfo->hasHscroll = has;
  else gwinfo->hasVscroll = has;
    
  if (has) {
    if (w == FrontWindow()) HiliteControl(ctl, 0);
    else HiliteControl(ctl, 255);
    if (which == 'H') {
      if (! old_has) gwinfo->canvasHeight -= 15;
      if (gwinfo->hasVscroll) {
        StGWGetViewRect(gwinfo, nil, nil, nil, &view_size);
        value = (gwinfo->canvasHeight - view_size > 0)
              ? gwinfo->canvasHeight - view_size : 0;
        SetControlMaximum(gwinfo->vscroll, value);
      }
      gwinfo->canvasWidth = size;
      StGWGetViewRect(gwinfo, nil, nil, &view_size, nil);
    }
    else {
      gwinfo->canvasHeight = size;
      StGWGetViewRect(gwinfo, nil, nil, nil, &view_size);
    }
    value = (size - view_size > 0) ? size - view_size : 0;
    SetControlMaximum(ctl, value);
    ShowControl(ctl);
  }
  else {
    if (which == 'H') {
      StGWGetViewRect(gwinfo, nil, nil, &gwinfo->canvasWidth, nil);
      if (old_has) gwinfo->canvasHeight += 15;
      StGWSetScroll(gwinfo, 0, gwinfo->view_v, TRUE);
    }
    else {
      StGWGetViewRect(gwinfo, nil, nil, nil, &gwinfo->canvasHeight);
      StGWSetScroll(gwinfo, gwinfo->view_h, 0, TRUE);
    }
    HideControl(ctl);
    SetControlMaximum(ctl, 0);
  }
  InvalRect(&w->portRect);
  reset_clip_rect(gwinfo);
  SetPort(savePort);

  StGWObResize(object);
}

VOID StGWSetHasHscroll(StGWWinInfo *gwinfo, int has, int size)
{
  set_has_scroll(gwinfo, 'H', has, size);
}

VOID StGWSetHasVscroll(StGWWinInfo *gwinfo, int has, int size)
{
  set_has_scroll(gwinfo, 'V', has, size);
}

int StGWHasHscroll(StGWWinInfo *gwinfo)
{
  if (gwinfo == nil) return(FALSE);
  else return(gwinfo->hasHscroll);
}

int StGWHasVscroll(StGWWinInfo *gwinfo)
{
  if (gwinfo == nil) return(FALSE);
  else return(gwinfo->hasVscroll);
}

VOID StGWSetScroll(StGWWinInfo *gwinfo, int h, int v, int move)
{
  WindowPtr w;
  GrafPtr savePort;
  Rect r;
  
  if (gwinfo == nil || (w = gwinfo->window) == nil) return;
  gwinfo->view_h = (gwinfo->hasHscroll) ? h : 0;
  gwinfo->view_v = (gwinfo->hasVscroll) ? v : 0;
    
  GetPort(&savePort);
  SetPort(w);
  ClipRect(&w->portRect);
  SetControlValue(gwinfo->hscroll, h);
  SetControlValue(gwinfo->vscroll, v);
  gwinfo->view_h = GetControlValue(gwinfo->hscroll);
  gwinfo->view_v = GetControlValue(gwinfo->vscroll);
  SetOrigin(gwinfo->view_h, gwinfo->view_v);
  reset_clip_rect(gwinfo);
  if (move) {
    r = scroll_bar_bounds(w, 'H');
    MoveControl(gwinfo->hscroll, r.left, r.top);
    r = scroll_bar_bounds(w, 'V');
    MoveControl(gwinfo->vscroll, r.left, r.top);
  }
  SetPort(savePort);
}

VOID StGWGetScroll(StGWWinInfo *gwinfo, int *h, int *v)
{
  
  if (gwinfo != nil) {
    if (h != nil) *h = gwinfo->view_h;
    if (v != nil) *v = gwinfo->view_v;
  }
}

VOID StGWSetHscrollIncs(StGWWinInfo *gwinfo, int inc, int pageInc)
{
  if (gwinfo == nil) return;
  gwinfo->h_scroll_inc[0] = inc;
  gwinfo->h_scroll_inc[1] = pageInc;
}

VOID StGWGetHscrollIncs(StGWWinInfo *gwinfo, int *inc, int *pageInc)
{
  if (gwinfo == nil) return;
  if (inc != 0) *inc = gwinfo->h_scroll_inc[0];
  if (pageInc != 0) *pageInc = gwinfo->h_scroll_inc[1];
}

VOID StGWSetVscrollIncs(StGWWinInfo *gwinfo, int inc, int pageInc)
{
  if (gwinfo == nil) return;
  gwinfo->v_scroll_inc[0] = inc;
  gwinfo->v_scroll_inc[1] = pageInc;
}

VOID StGWGetVscrollIncs(StGWWinInfo *gwinfo, int *inc, int *pageInc)
{
  if (gwinfo == nil) return;
  if (inc != 0) *inc = gwinfo->v_scroll_inc[0];
  if (pageInc != 0) *pageInc = gwinfo->v_scroll_inc[1];
}

/**************************************************************************/
/**                                                                      **/
/**                    Graph Window RefCon Functions                     **/
/**                                                                      **/
/**************************************************************************/

VOID StGWSetRefCon(StGWWinInfo *gwinfo, long x)
{
  if (gwinfo == nil) return;
  else gwinfo->RefCon = x;
}

long StGWGetRefCon(StGWWinInfo *gwinfo)
{
  if (gwinfo == nil) return((long) nil);
  else return(gwinfo->RefCon);
}
 
VOID StGWSetObject(StGWWinInfo *gwinfo, LVAL x)
{
  if (gwinfo == nil) return;
  else gwinfo->Object = x;
}

LVAL IViewWindowGetObject(WindowPtr w)
{
  StGWWinInfo *gwinfo = (StGWWinInfo *) GetWRefCon(w);
  if (gwinfo == nil) return(NIL);
  else return(gwinfo->Object);
}

LVAL StGWGetObject(StGWWinInfo *gwinfo)
{
  return((gwinfo != nil) ? gwinfo->Object : NIL);
}


/**************************************************************************/
/**                                                                      **/
/**                    Graph Window Color Functions                      **/
/**                                                                      **/
/**************************************************************************/

#define NumBasicColors 8
#define NumRGBColors 256
# define MULTIPLIER 62535

static int NumColors;

typedef struct {
  union {
    long old;
    RGBColor rgb;
  } value;
  long refcon;
} ctab_entry;

static ctab_entry *ctable;

VOID init_mac_colors(void)
{
  NumColors = NumBasicColors;
  if (StScreenHasColor()) NumColors += NumRGBColors;
  ctable = (ctab_entry *) StCalloc(NumColors, sizeof(ctab_entry));
  
  ctable[0].value.old = whiteColor;
  ctable[1].value.old = blackColor;
  ctable[2].value.old = redColor;
  ctable[3].value.old = greenColor;
  ctable[4].value.old = blueColor;
  ctable[5].value.old = cyanColor;
  ctable[6].value.old = magentaColor;
  ctable[7].value.old = yellowColor;
}

int StGWMakeColor(double red, double green, double blue, long refcon)
{
  int index;
  
  for (index = NumBasicColors; 
       index < NumColors && StGWGetColRefCon(index) != (long) nil; 
       index++);
  if (index >= NumColors) return(-1);
  else {
    StGWSetColRefCon(index, refcon);
    ctable[index].value.rgb.red = MULTIPLIER * red;
    ctable[index].value.rgb.green = MULTIPLIER * green;
    ctable[index].value.rgb.blue = MULTIPLIER * blue;
    return(index);
  }
}

VOID StGWFreeColor(unsigned int index)
{
  if (index < NumColors && index >= NumBasicColors)
    StGWSetColRefCon(index, (long) nil);
  else xlfail("can't free standard color");
}

VOID StGWSetColRefCon(unsigned int index, long rc)
{
  if (index < NumColors) ctable[index].refcon = rc;
}

long StGWGetColRefCon(unsigned int index)
{	
  if (index < NumColors) return(ctable[index].refcon);
  else return((long) nil);
}

static long get_color(unsigned int index)
{
  if (index < NumBasicColors) return(ctable[index].value.old);
  else return(ctable[1].value.old);
}

static RGBColor *get_rgb_color(unsigned int index)
{
  if (index >= NumColors) xlfail("bad rgb color");
  return(&ctable[index].value.rgb);
}

VOID set_fore_color(StGWWinInfo *gwinfo)
{
  if (gwinfo->drawColor < NumBasicColors)
    ForeColor(get_color(gwinfo->drawColor));
  else RGBForeColor(get_rgb_color(gwinfo->drawColor));
}

VOID set_back_color(StGWWinInfo *gwinfo)
{
  if (gwinfo->backColor < NumBasicColors)
    BackColor(get_color(gwinfo->backColor));
  else RGBBackColor(get_rgb_color(gwinfo->backColor));
}


syntax highlighted by Code2HTML, v. 0.9.1