/* GraphicView.m -- The meaty part of the front end.
 *
 * Written By: Bill Bumgarner (Friday Software & Consulting)
 *             <wb1j+@andrew.cmu.edu>
 *             414 S.Craig, #119
 *             Pittsburgh, PA. 15213
 *             412-268-5378
 *
 *  This class drives the bitmap object-- it handles events, does
 *  initialization and provides the interface between the bitmap and
 *  the user interface.
 */

#import "GraphicView.h"
#import "TGIF.h"

#import "NXBitmapGraphicRep.h"
#import <appkit/Application.h>
#import <appkit/Slider.h>
#import <appkit/Window.h>
#import <appkit/color.h>
#import <appkit/graphics.h>
#import <math.h>
#import <libc.h>

#import "CmdVertex.h"
#import "CmdBgnpoly.h"
#import "CmdEndpoly.h"
#import "AbsPoly.h"

// Pre-defined Color Depths (NX_EightBitGrayDepth is not supported yet):
//    NX_DefaultDepth
//    NX_TwoBitGrayDepth
//    NX_EightBitGrayDepth
//    NX_TwelveBitRGBDepth
//    NX_TwentyFourBitRGBDepth

// NeXT Pre-defined Colors:
//    NX_COLORBLACK
//    NX_COLORWHITE
//    NX_COLORGRAY
//    NX_COLORLTGRAY
//    NX_COLORDKGRAY
//    NX_COLORRED
//    NX_COLORGREEN
//    NX_COLORBLUE
//    NX_COLORCYAN
//    NX_COLORYELLOW
//    NX_COLORMAGENTA
//    NX_COLORORANGE
//    NX_COLORPURPLE
//    NX_COLORBROWN
//    NX_COLORCLEAR

@implementation GraphicView
id tgif;
BOOL polyDrawing=NO;
BOOL lineDrawing=YES;
id curPoly;

- initFrame:(NXRect *) aRect
{
  [super initFrame:aRect];
  bitmap=[[NXBitmapGraphicRep alloc] initWithSize:&(aRect->size)
	  // change this to one of the predefined depths if you want to work
	  // in other than two bit gray mode.  As long as you use the
	  // plotPoint method (or write your own color-handling bit
			       // manipulation stuff)
	  // the NXBitmapGraphicRep class will deal w/the different color
	  // models automatically.  Two change the color model for an existing
	  // program, simply change the constant below and recompile.
	  // currently, changing the color model on the fly is not supported
	  // (though it is not that difficult to do!).
	  depth:NX_TwoBitGrayDepth
	  andColor:NX_COLORDKGRAY];
  [self setFlipped:NO];
  return self;
}

- infoPanel:sender
{
  if (infoPanel == nil) {
    infoPanel = [NXApp loadNibSection:"InfoPanel.nib" owner:self withNames:NO];
    [infoPanel center];
  }
  [infoPanel orderFront:sender];

  return self;
}

// this method is called immediately after all IB objects are created and
// initialized and immediately before applicaton receives a -run message.
-appDidInit:sender
{
  float gray;

  [bitmap setColor:NX_COLORLTGRAY];

  NXConvertColorToGray([bitmap color], &gray);
  [foreWell  setColor:[bitmap color]];
  [foreWell setContinuous:YES];
  [foreSlide setFloatValue:gray];

  [backWell setColor:[bitmap backColor]];
  [backWell setContinuous:YES];
  NXConvertColorToGray([bitmap backColor], &gray);
  [backSlide setFloatValue:gray];
  srandom(getpid());
  // start in line-drawing mode
  polyDrawing=NO;
  lineDrawing=YES;

  tgif=[[TGIF alloc] initWithBitmap:bitmap andView:self];
  return self;
}

- drawSelf:(const NXRect *)rects :(int)rectCount
{
  [bitmap draw];
  return self;
}

long randnum(min, max)
     long min, max;
{
  return (max-min)*(random()/(pow(2,31)-1))+min;
}

- draw:sender
{
  int i;
  // normally, the [self display] would be done after ALL drawing.  It is
  // done this way only as a throughput test.
  [bitmap setColor:NX_COLORBLACK];
  for(i=0;i<1000;i++) {
    [bitmap line:randnum(0,639) :randnum(0,479)
     :randnum(0,639) :randnum(0,479)];
  }
  [self display];
  
  return self;
}


- setFore:sender
{
  NXColor c=NXConvertGrayToColor([sender floatValue]);

  if (!NXEqualColor(c, [bitmap color])){
    [bitmap setColor:c];
    [foreWell setColor:c];
    [tgif cmdForeColor:c];
  }
    return self;
}

- setBack:sender
{
  NXColor c=NXConvertGrayToColor([sender floatValue]);
  
  if (!NXEqualColor(c, [bitmap backColor])){
    [bitmap setBackColor:c];
    [backWell setColor:c];
    [tgif cmdBackColor:c];
    [bitmap eraseFrame];
    [self display];
  }
  return self;
}

- startStopPolyDraw:sender
{
  if(polyDrawing){
    polyDrawing=NO;
    // initialize poly data struct
  } else {
    polyDrawing=YES;
    // draw the polygon
  }
  return self;
}

NXPoint start,end;
- mouseDown:(NXEvent *)theEvent
{
  start=theEvent->location;
  [self convertPoint:&start fromView:nil];
  if(!polyDrawing)
    [tgif cmdMove:(int)start.x :(int)start.y];
  return self;
}

- mouseUp:(NXEvent *)theEvent
{
  end=theEvent->location;
  [self convertPoint:&end fromView:nil];

  if(!polyDrawing){
    [bitmap line:(int)start.x :(int)start.y :(int)end.x :(int)end.y];
    [tgif cmdDraw:(int)end.x :(int)end.y];
    [self display];
  }
  else
    [curPoly addCommand:[[CmdVertex alloc] initCmd:end.x :end.y]];
  return self;
}

-eraseImage:sender
{
  // erase the bitmap to the current backColor
  [bitmap eraseFrame];
  [tgif cmdNewFrame];
  // redisplay bitmap
  [self display];
  return self;
}

- foreWell:sender
{
  float gray;
  NXColor c=[sender color];
  if(!NXEqualColor(c, [bitmap color])){
    [bitmap setColor:c];
    NXConvertColorToGray(c, &gray);
    [foreSlide setFloatValue:gray];
    [tgif cmdForeColor:c];
  }
  return self;
}

- backWell:sender
{
  float gray;
  NXColor c=[sender color];
  if(!NXEqualColor(c, [bitmap backColor])){
    [bitmap setBackColor:c];
    NXConvertColorToGray(c, &gray);
    [backSlide setFloatValue:gray];
    [bitmap eraseFrame];
    [tgif cmdBackColor:c];
    [self display];
  }
  return self;
}

- saveCommands:sender
{
  if(polyDrawing){
    [curPoly addCommand:[[CmdEndpoly alloc] initCmd]];
    [curPoly doCmd];
    [self display];
    polyDrawing=NO;
  }    
  [tgif saveToFile];
  return self;
}

- openCommands:sender
{
  if(polyDrawing){
    [curPoly addCommand:[[CmdEndpoly alloc] initCmd]];
    polyDrawing=NO;
  } 
  [tgif openFile];
  return self;
}

- newCommands:sender
{
  [tgif newFile];
  return self;
}

#define BTN_CROSS 0
#define BTN_ARROW 1
#define BTN_CIRCLE 2
#define BTN_LINE 3
#define BTN_POLY 4
- toolHit:sender
{
  int hitOn=[[sender selectedCell] tag];
  if(polyDrawing){
    // close polygon and do command
    [curPoly addCommand:[[CmdEndpoly alloc] initCmd]];
    [curPoly doCmd];
    [self display];
    polyDrawing=NO;
  }
  switch (hitOn){
  case BTN_CROSS:
  case BTN_ARROW:
    polyDrawing=NO;
    lineDrawing=NO;
    [toolStatus setStringValue:"Selection"];
    break;
  case BTN_LINE:
    lineDrawing=YES;
    [toolStatus setStringValue:"Line Drawing"];
    break;
  case BTN_POLY:
    polyDrawing=YES;
    [toolStatus setStringValue:"Poly Drawing"];
    curPoly=[tgif cmdNewPoly];
    [curPoly addCommand:[[CmdBgnpoly alloc] initCmd]];
    break;
  case BTN_CIRCLE:
    fprintf(stderr, "Circle Drawing not yet implemented\n");
    break;
  }
  return self;
}  
@end
