// simpledraw.cpp
// combination of code from Harriet Fell and Jon Baudanza

#include <iostream.h>
#include <stdlib.h>


#include "simpledraw.h"

// Default constructor
SimpleDraw::SimpleDraw (int x=0, int y=0,
			int width=200, int height=200) {

  char title [] = "COM 1370 Window";  // for window title

  // connect to X server
  if (!(mydisplay = XOpenDisplay(""))) {
    cerr << "SimpleDraw:  Error Connecting to X Server" << endl;
    exit(1);
  }

  myscreen     = DefaultScreen (mydisplay);
  myBackground = WhitePixel (mydisplay,myscreen);
  myForeground = BlackPixel (mydisplay, myscreen);

  myhint.x     = x; myhint.y      = y;
  myhint.width = width; myhint.height = height;
  myhint.flags = PPosition | PSize;

  mywindow = XCreateSimpleWindow (mydisplay,
    XDefaultRootWindow(mydisplay),
    myhint.x, myhint.y, myhint.width, myhint.height, 5,
    myForeground, myBackground);
  // Set current position to (0, 0);
  cx = 0; cy = 0;

  // Find the Black and White colors
  black = BlackPixel (mydisplay, myscreen);
  white = WhitePixel (mydisplay, myscreen);

  char **argv;  // to pass in lieu of command line info
  int argc = 0;

  XSetStandardProperties (mydisplay, mywindow, title, title,
    None, argv, argc, &myhint);

  mygc = XCreateGC (mydisplay, mywindow, 0, 0);
  XSetBackground (mydisplay, mygc,  myBackground);
  XSetForeground (mydisplay, mygc,  myForeground);

  XSelectInput (mydisplay, mywindow,
     ButtonPressMask | KeyPressMask | ExposureMask);

  XMapRaised (mydisplay, mywindow);

  XNextEvent (mydisplay, &myevent);//make sure the drawing shows

  gMx = 256, bMx = 256; // for 24-bit color
 
}

// Destructor
SimpleDraw::~SimpleDraw() {

  // Free the graphics context
  XFreeGC(mydisplay, mygc);

  // Destroy the window
  XDestroyWindow(mydisplay, mywindow);
 
  // Close the connection to the X server
  XCloseDisplay (mydisplay);
}


// Event Routines

// Wait for a mouse click in the window - any button
void SimpleDraw::AwaitClick(){
  while (1) {
    XNextEvent (mydisplay, &myevent);
    if (myevent.type == ButtonPress) return;
  }
}

// Returns the mouse position on release of any button
void SimpleDraw::GetPosition(int& x, int& y){
  while (1) {
    XNextEvent (mydisplay, &myevent);
    if (myevent.type == ButtonPress){
      x = myevent.xbutton.x;
      y = myevent.xbutton.y;
      return;
    }
  }
}

// Read a key - only returns regular char values
char SimpleDraw::GetKey(){
  while (1) {
    XNextEvent (mydisplay, &myevent);
    switch (myevent.type){
      case MappingNotify:
        XRefreshKeyboardMapping (&myMappingEvent);  
        break;
      
      case KeyPress:
        int   charcount; 
        char  text[2];        // for char and \0
        charcount = XLookupString (&(myevent.xkey), text, 2, &myKey, NULL);
        if (charcount == 1) return text[0];  
     } //end switch      
  } //end while
}


// Basic Graphic Routines
// ======================

// Clears the mywindow
void SimpleDraw::Clear(void) {

  // Clear mywindow
  XClearArea(mydisplay, mywindow, 0, 0, 0, 0, False);  

  // Make sure the command is sent to server
  XFlush(mydisplay);

}

//Draw point
void SimpleDraw::DrawPoint(int x, int y) {
  
  // Draw the point
  XDrawPoint (mydisplay, mywindow, mygc, x, y);

  //Make sure the command is sent to the server
  XFlush(mydisplay);
}

//Fill rectangle
void SimpleDraw::FillRectangle(int x, int y, 
			       unsigned int width, unsigned int height){

  // Draw the filled rectangle
  XFillRectangle (mydisplay, mywindow, mygc, x, y, width, height);

  //Make sure the command is sent to the server
  XFlush(mydisplay);
}

// Color Routines
//===============
void SimpleDraw::SetForeColor(short r, short g, short b) {
  unsigned long pixval;
  pixval = (unsigned long)(r)*gMx*bMx + (g)*bMx + (b);
  XSetForeground (mydisplay, mygc, pixval);
}

void SimpleDraw::SetForeColor( unsigned long pixval) {
  XSetForeground (mydisplay, mygc, pixval);
}

void SimpleDraw::SetBackColor(short r, short g, short b) {
  unsigned long pixval;
  pixval = (unsigned long)(r)*gMx*bMx + (g)*bMx + (b);
  XSetBackground (mydisplay, mygc, pixval);
}

void SimpleDraw::SetBackColor( unsigned long pixval) {
  XSetBackground (mydisplay, mygc, pixval);
}

unsigned long SimpleDraw::White(){
   return WhitePixel (mydisplay,myscreen);
}

unsigned long SimpleDraw::Black(){
   return BlackPixel (mydisplay,myscreen);
}

// Pixel Routines
//===============
void SimpleDraw::SetPixel(int x_cord, int y_cord){ // Set to current ForeColor
 XDrawPoint(mydisplay, mywindow, mygc, x_cord, y_cord);
 XFlush(mydisplay);
}

// Line and Move Routines
//==============
void SimpleDraw::DrawLine(int x1, int y1, int x2, int y2){
 XDrawLine(mydisplay, mywindow, mygc, x1, y1, x2, y2);
 XFlush(mydisplay);
 cx = x2; cy = y2;
}

void SimpleDraw::Move(int dx, int dy){
 cx += dx;  cy += dy;
}

void SimpleDraw::MoveTo(int x, int y){
 cx = x;  cy = y;
}

void SimpleDraw::Line(int dx, int dy){
 XDrawLine(mydisplay, mywindow, mygc, cx, cy, cx + dx, cy + dy);
 XFlush(mydisplay);
 cx += dx;  cy += dy;
}

void SimpleDraw::LineTo(int x, int y){
 XDrawLine(mydisplay, mywindow, mygc, cx, cy, x, y);
 XFlush(mydisplay);
 cx = x;  cy = y;
}

void SimpleDraw::SetLineWidth(int w){
 XGCValues values; 
// int line_style = XGetGCValues(mydisplay, mygc, GCLineStyle, values);
 values.line_width = w;
 XChangeGC(mydisplay, mygc,GCLineWidth, &values);
}

// Rectangle Routines
//===================
void SimpleDraw::FrameRect(int left, int top, int right, int bottom){
  XDrawRectangle(mydisplay, mywindow, mygc, left, top, right-left, bottom-top);
  XFlush(mydisplay);
}

void SimpleDraw::PaintRect(int left, int top, int right, int bottom){
  XFillRectangle(mydisplay, mywindow, mygc, left, top, right-left+1, 
		 bottom-top+1); 
// +1 to include the entire boundary that would be drawn by FrameRect
  XFlush(mydisplay);
}

void SimpleDraw::InvertRect(int left, int top, int right, int bottom){
  XSetFunction(mydisplay, mygc, GXinvert);
  XFillRectangle(mydisplay, mywindow, mygc, left, top, right-left+1, 
		 bottom-top+1); 
// +1 to include the entire boundary that would be drawn by FrameRect
  XFlush(mydisplay);
  XSetFunction(mydisplay, mygc, GXcopy);
}

void SimpleDraw::EraseRect(int left, int top, int right, int bottom){
  unsigned long fcolor = myForeground;
  SetForeColor(myBackground);
  XFillRectangle(mydisplay, mywindow, mygc, left, top, right-left+1, 
		 bottom-top+1); 
// +1 to include the entire boundary that would be drawn by FrameRect
  XFlush(mydisplay);
  SetForeColor(fcolor);
}


// Oval Routines
//==============
void SimpleDraw::FrameOval(int left, int top, int right, int bottom){
  XDrawArc(mydisplay, mywindow, mygc, left, top, right-left, bottom-top, 0, 
	   360*64);
  XFlush(mydisplay);
}

void SimpleDraw::PaintOval(int left, int top, int right, int bottom){
  XFillArc(mydisplay, mywindow, mygc, left, top, right-left+1, bottom-top+1, 
	   0, 360*64); 
// +1 to include the entire boundary that would be drawn by FrameOval
  XFlush(mydisplay);
}

void SimpleDraw::InvertOval(int left, int top, int right, int bottom){
  XSetFunction(mydisplay, mygc, GXinvert);
  XFillArc(mydisplay, mywindow, mygc, left, top, right-left+1, bottom-top+1, 
	   0, 360*64); 
// +1 to include the entire boundary that would be drawn by FrameOval
  XFlush(mydisplay);
  XSetFunction(mydisplay, mygc, GXcopy);
}

void SimpleDraw::EraseOval(int left, int top, int right, int bottom){
  unsigned long fcolor = myForeground;
  SetForeColor(myBackground);
  XFillArc(mydisplay, mywindow, mygc, left, top, right-left+1, bottom-top+1, 
	   0, 360*6483);
// +1 to include the entire boundary that would be drawn by FrameOval
  XFlush(mydisplay);
  SetForeColor(fcolor);
}

// Circle Routines
//================
void SimpleDraw::FrameCircle(int x, int y, int r){
  XDrawArc(mydisplay, mywindow, mygc, x - r, y - r, 2*r, 2*r, 0, 360*64);
  XFlush(mydisplay);
}

void SimpleDraw::PaintCircle(int x, int y, int r){
  XFillArc(mydisplay, mywindow, mygc, x - r, y - r, 2*r+1, 2*r+1, 0, 360*64); 
// +1 to include the entire boundary that would be drawn by FrameCircle
  XFlush(mydisplay);
}

void SimpleDraw::InvertCircle(int x, int y, int r){
  XSetFunction(mydisplay, mygc, GXinvert);
  XFillArc(mydisplay, mywindow, mygc, x - r, y - r, 2*r+1, 2*r+1, 0, 360*64); 
// +1 to include the entire boundary that would be drawn by FrameCircle
  XFlush(mydisplay);
  XSetFunction(mydisplay, mygc, GXcopy);
}

void SimpleDraw::EraseCircle(int x, int y, int r){
  unsigned long fcolor = myForeground;
  SetForeColor(myBackground);
  XFillArc(mydisplay, mywindow, mygc, x - r, y - r, 2*r+1, 2*r+1, 0, 360*64); 
// +1 to include the entire boundary that would be drawn by FrameCircle
  XFlush(mydisplay);
  SetForeColor(fcolor);
}

// Text Routines
//==============
void SimpleDraw::DrawString(char *s, int x, int y){
  XDrawString(mydisplay, mywindow, mygc, x, y, s, strlen(s));
  XFlush(mydisplay);
} 
