/*

  *CSE142, Autumn 2000
  Program based on:

 * CSE142, 97 Autumn
 * Homework 4
 * Sample solution, by Dan Boren 11/97
 *
 */

#include "gp142.h"
#include <stdio.h>
#include <math.h>
#include <assert.h>

#define TRUE                    1
#define FALSE                   0

/* Configure the appearance of the buttons on the menu bar */
/* Menu buttons: */

/* Top-left coordinates of button palette */
#define MENU_TOP                (GP142_YMAX - 5)
#define MENU_LEFT               (-GP142_XMAX + 5)

/* Height and width of buttons in pixels */
#define BUTTON_HEIGHT   30
#define BUTTON_WIDTH   100
 

/* Possible types of mouse clicks: */
/*   Mouse click in some button */
#define NOT_BUTTON              (-1)
#define BUTTON_CLICK    1

/*number of pumpkins that can be stored in the array*/
#define MAX_PUMPKINS    15
/*number of pumpkins initially visible*/
#define INITIAL_PUMPKIN_COUNT  3

#define PUMPKIN_MARGIN  10  /* Minimum initial spacing between pumpkins */

/*color of pumpkin body*/
#define BASIC_PUMPKIN_COLOR     ORANGE
#define HIGHLIGHT_COLOR         YELLOW

/*Minimum radius of any pumpkin*/
#define MIN_PUMPKIN_RADIUS      3
 

/* For many of the GP142 functions, a line width of
 * zero means to fill the shape being drawn.
 */
#define FILL 0

/*Growth and shrinkage amounts */
#define ENLARGE_INCREMENT       4
#define SHRINK_INCREMENT        4
 

/* Types of eyes we support */

#define BUTTON_NEXT_PUMPKIN     0
#define BUTTON_NEW_PUMPKIN      1
#define BUTTON_DELETE           2
#define BUTTON_EYES_CIRCLE 3
#define BUTTON_EYES_SQUARE 4
#define BUTTON_EYES_ANGRY 5

/* Types of noses we support */
#define BUTTON_NOSE_TRIANGLE  6
#define BUTTON_NOSE_PEAKY_NOSE 7
#define BUTTON_NOSE_SQUARE 8

/* Types of mouths */
#define BUTTON_MOUTH_SMILE 9
#define BUTTON_MOUTH_OHMY  10
#define BUTTON_MOUTH_GRIN  11

#define BUTTON_ENLARGE  12
#define BUTTON_SHRINK   13

/*IMPORTANT -- change the following if you add buttons.
The number of buttons should equal 1 + the last button number
*/
#define NUM_BUTTONS     (BUTTON_SHRINK+1)
 

/*The button labels need to correspond to the button types
Don't forget to also change the draw_buttons functin if buttons are added or removed.
*/
#define BUTTON_0_LABEL  "Next Pumpkin"
#define BUTTON_1_LABEL  "Create Pumpkin"
#define BUTTON_2_LABEL  "Delete Current"
#define BUTTON_3_LABEL  "Circle Eyes"
#define BUTTON_4_LABEL  "Square Eyes"
#define BUTTON_5_LABEL  "Angry Eyes"
#define BUTTON_6_LABEL  "Triangle Nose"
#define BUTTON_7_LABEL  "Peaky Nose"
#define BUTTON_8_LABEL  "Square Nose"
#define BUTTON_9_LABEL  "Smile"
#define BUTTON_10_LABEL "Oh-my!"
#define BUTTON_11_LABEL "Grin"
#define BUTTON_12_LABEL "Enlarge"
#define BUTTON_13_LABEL "Shrink"

/******************************************************************************

 PROTOTYPES

******************************************************************************/

/* Return kind of mouse click (in a button, etc.) */
int classify_mouse_click(int mouse_x, int mouse_y, int *buttonNumber);

void draw_buttons(void);
void handle_button(int buttonNum, int *currentPumpkin,
                                   int centerX[], int centerY[], int radiuses[],
                                   int eyes[], int noses[], int mouths[]);

void DrawEyes(int shapetype, int xCtr, int yCtr, int radius);
void DrawCircleEyes(int xCtr, int yCtr, int radius);
void DrawSquareEyes(int xCtr, int yCtr, int radius);
void DrawAngryEyes(int xCtr, int yCtr, int radius);

void DrawNose(int shapetype, int xCtr, int yCtr, int radius);
void DrawTriangleNose(int xCtr, int yCtr, int radius);
void DrawUglyNose(int xCtr, int yCtr, int radius);
void DrawSquareNose(int xCtr, int yCtr, int radius);

void DrawMouth(int shapetype, int xCtr, int yCtr, int radius);
void DrawOhMyMouth(int xCtr, int yCtr, int radius);
void DrawSmileMouth(int xCtr, int yCtr, int radius);
void DrawGrinMouth(int xCtr, int yCtr, int radius);
void DrawAllPumpkins(int currentPumpkin,
                                 int centerX[], int centerY[], int radiuses[], int eyes[], i
nt noses[], int mouths[]);

void DrawPumpkin(int whichP, int currentPumpkin,
                                 int centerX[], int centerY[], int radiuses[], int eyes[], i
nt noses[], int mouths[]);
void DrawPumpkinBody(int whichP, int currentPumpkin,
                                         int centerX[], int centerY[], int radiuses[]);

void InitializeOnePumpkin (int newx, int newy, int newwidth,
                                                   int *xp, int *yp, int *radiusp, int *eyes
p, int *nosep, int *mouthp);
void InitializePumpkins (int x[], int y[], int radius[],
                                                int eyes[], int nose[], int mouth[]);

int IsInsidePumpkin (int x, int y, int PCenterX[], int PCenterY[], int PRadius[], int *Punkn
um);
double distance (double XPoint1, double YPoint1, double XPoint2, double YPoint2);
int IsInsideCircle (double X, double Y, double XCenter, double YCenter, double radius);

int NextLivePumpkinNumber (int * pnum, int radius[]);

int CreateNewPumpkin(int *currentPumpkin, int centerX[], int centerY[], int radiuses[],
                   int eyes[], int noses[], int mouths[]);
/******************************************************************************

 MAIN

 Event loop for GP142
 Recognizes button clicks
 Contains arrays with all pumpkin parameters
 Calls functions to update drawing parameters
 Redraws the screen once each iteration

******************************************************************************/
int main(void)
{

  int currentPumpkin = 0;       /* Which pumpkin will recieve next draw command? */

  /* These arrays tell how the faces will look.  For example, noses[i] tells what
   * type of nose the ith pumpkin has
   */
        int centerX[MAX_PUMPKINS];
        int centerY[MAX_PUMPKINS];
        int radii[MAX_PUMPKINS]; /* a negative radius makes the pumpkin invisible */
                /*This convention is used to "add" and "delete" pumpkins."
                /*Actually, the pumpkins are simply visible or not visible*/

  int eyes[MAX_PUMPKINS];
  int noses[MAX_PUMPKINS];
  int mouths[MAX_PUMPKINS];

  /* These variable are used by the main even loop */
        int quit = FALSE;
        int mouse_x, mouse_y;
        char key_hit;
        int  click_kind;                /* kind of mouse click (button or not etc.)
        */
        int buttonNumber;
 

  /*
   * INITIALIZATIONS
   */
  GP142_open();           /* Open and init the GP142 Graphics Window      */
  GP142_logging(LOG_OFF); /* Change to LOG_ON if you want it.             */

  InitializePumpkins (centerX, centerY, radii, eyes, noses, mouths);

  /*
   * The main event loop:
   * --- ---- ----- -----
   * wait for the next user action, such as a mouse click or keyboard
   * keypress, or a periodic update event (allowing us to display the
   * next frame of an animation -- not used in this program)
   * and call the appropriate function to
   * handle it.  Repeat this until the user signals "quit".
   */
  while (!quit) {

        GP142_clear();                        /* Clear window & paint it white. */

        DrawAllPumpkins(currentPumpkin, centerX, centerY, radii, eyes, noses, mouths);

        draw_buttons();         /* Show the buttons...            */
 
    switch (GP142_await_event(&mouse_x, &mouse_y, &key_hit)) {
    case GP142_QUIT:
      quit = TRUE;           /* set flag to terminate loop           */
      break;
    case GP142_KBD:
      break;

    case GP142_MOUSE:
      /* handle appropriate button click, if any */

        /* Mouse click.  Determine if in a menu button  */
        /* or elsewhere and process accordingly.                */
                click_kind = classify_mouse_click(mouse_x, mouse_y, &buttonNumber);

                if (click_kind == BUTTON_CLICK)
                {
                        handle_button(buttonNumber, &currentPumpkin,
                                centerX, centerY, radii, eyes, noses, mouths);
                        /*This is likely to change some pumpkin in some way.
                        The change is recorded in one of the arrays, but the screen is not r
edrawn.
                        */
                }
                else
                {
                        if (IsInsidePumpkin (mouse_x, mouse_y, centerX, centerY, radii, &cur
rentPumpkin)) {
                                /*if true, a pumpkin has been clicked on;
                                current pumpkin has been updated.
                                Move that pumpkin to center it on the mouse click position.*
/
                                centerX[currentPumpkin] = mouse_x;
                                centerY[currentPumpkin] = mouse_y;
                        }

                }
                break;
 

    case GP142_PERIODIC:
      break;

    default:

      break;
 
    }

  }

  /*
   * Clean up and close graphics window
   */
  GP142_close();

  return 0;

}  /*end main */

/******************************************************************************

 FUNCTIONS

******************************************************************************/
/* Initialize All Pumpkins */
/*
 *
 * Input:
 * all the parallel pumpkin arrays
 * Output:
 * Default values for each pumpkin
 */
void InitializePumpkins (int x[], int y[], int radius[],
                                                 int eyes[], int nose[], int mouth[]) {
        int pnum;
        int left_edge = (MENU_LEFT + BUTTON_WIDTH + 1);
        int available_width = GP142_XMAX - left_edge;
        int pumpkin_width = (available_width -
                                INITIAL_PUMPKIN_COUNT * PUMPKIN_MARGIN) / INITIAL_PUMPKIN_CO
UNT;
        int newradius = pumpkin_width / 2;
        int newx, newy=0;

        for (pnum = 0; pnum < MAX_PUMPKINS; pnum++) {
                if (pnum < INITIAL_PUMPKIN_COUNT) {

                        newx = left_edge + pnum*(PUMPKIN_MARGIN + pumpkin_width) +
                                                        pumpkin_width/2;
                        InitializeOnePumpkin (newx, newy, newradius,
                                &x[pnum], &y[pnum], &radius[pnum], &eyes[pnum], &nose[pnum],
 &mouth[pnum]);
                }
                else { /*these will be invisible until "created" */
                        InitializeOnePumpkin (0,0, -1,
                                &x[pnum], &y[pnum], &radius[pnum], &eyes[pnum], &nose[pnum],
 &mouth[pnum]);

                }
        }
} /*end InitializePumpkins*/
 

/*Helper function: called when a "new" pumpkin is created*/
void InitializeOnePumpkin (int newx, int newy, int newradius,
                                                   int *xp, int *yp, int *radiusp, int *eyes
p, int *nosep, int *mouthp) {
        *yp = newy;
        *xp = newx;
        if (*radiusp < 0 || *radiusp > MIN_PUMPKIN_RADIUS)
                *radiusp = newradius;
        else
                *radiusp = MIN_PUMPKIN_RADIUS;
        *eyesp = 0;
        *nosep = 0;
        *mouthp = 0;
}
 

/* Classify mouse click at (mouse_x,mouse_y) and return kind of click*/
/* Note that buttons take "priority" over pumpkins in the sense that, should
        a pumpkin happen to overlap the button area, this function will still
        classify a click in that overlap area as a button.
        */
int classify_mouse_click(int mouse_x, int mouse_y, int *buttonNum)
{
        if ((mouse_x > MENU_LEFT) &&
                (mouse_x < MENU_LEFT + BUTTON_WIDTH) &&
                (mouse_y < MENU_TOP) &&
                (mouse_y > (MENU_TOP - NUM_BUTTONS * BUTTON_HEIGHT)))
        { /*it is a button -- figure out which one*/

  /* We know the mouse clicked in the column of buttons. */
                *buttonNum = (/*GP142_YMAX*/MENU_TOP - mouse_y)/BUTTON_HEIGHT;

                return BUTTON_CLICK;
        }
        else
                return NOT_BUTTON;
}
 

/*
 * Draws all the buttons
 *
 *
 */
void draw_buttons(void)
{

//  int yCtrText;

        int i;
        int text_offset = MENU_LEFT+10; /* offset of button text in button */
        int font_size = 12;                             /* size of button text */
 
        /* draw rectangles for each button */
        for(i = 0; i < NUM_BUTTONS; i++) {
                GP142_rectangleXY(BLACK,
                        MENU_LEFT,
                        MENU_TOP - i * BUTTON_HEIGHT,
                        MENU_LEFT + BUTTON_WIDTH,
                        MENU_TOP - (i + 1) * BUTTON_HEIGHT, 1);
        }
 
        /* display button text */
        i = MENU_TOP - (BUTTON_HEIGHT/2) - (font_size/2);
 

  GP142_printfXY(BLACK, text_offset, i, font_size, BUTTON_0_LABEL);
  i -= BUTTON_HEIGHT;
  GP142_printfXY(BLACK, text_offset, i, font_size, BUTTON_1_LABEL);
  i -= BUTTON_HEIGHT;
  GP142_printfXY(BLACK, text_offset, i, font_size,BUTTON_2_LABEL);
  i -= BUTTON_HEIGHT;
  GP142_printfXY(BLACK, text_offset, i, font_size, BUTTON_3_LABEL);
  i -= BUTTON_HEIGHT;
  GP142_printfXY(BLACK, text_offset, i, font_size, BUTTON_4_LABEL);
  i -= BUTTON_HEIGHT;
  GP142_printfXY(BLACK, text_offset, i, font_size, BUTTON_5_LABEL);
  i -= BUTTON_HEIGHT;
  GP142_printfXY(BLACK, text_offset, i, font_size, BUTTON_6_LABEL);
  i -= BUTTON_HEIGHT;
  GP142_printfXY(BLACK, text_offset, i, font_size, BUTTON_7_LABEL);
  i -= BUTTON_HEIGHT;
  GP142_printfXY(BLACK, text_offset, i, font_size, BUTTON_8_LABEL);
  i -= BUTTON_HEIGHT;
  GP142_printfXY(BLACK, text_offset, i, font_size, BUTTON_9_LABEL);
  i -= BUTTON_HEIGHT;
  GP142_printfXY(BLACK, text_offset, i, font_size, BUTTON_10_LABEL);
  i -= BUTTON_HEIGHT;
  GP142_printfXY(BLACK, text_offset, i, font_size, BUTTON_11_LABEL);
  i -= BUTTON_HEIGHT;
  GP142_printfXY(BLACK, text_offset, i, font_size, BUTTON_12_LABEL);
  i -= BUTTON_HEIGHT;
  GP142_printfXY(BLACK, text_offset, i, font_size, BUTTON_13_LABEL);

}

/* NextLivePumpkinNumber
Update the argument to be the next live (visible) pumpkin, wrapping back to 0
if needed.
The radius array is passed in, as this is how a pumpkin's visibility is determined.

Return 1 if there is a live pumpkin.
Return 0 if there is no live pumpkin at all.
If there is only one live pumpkin, or none, the p number will be unchanged.
*/
int NextLivePumpkinNumber (int * pnum, int radius[]) {
        int pcount;

        assert (*pnum >=0 && *pnum < MAX_PUMPKINS);

        for (pcount = 0; pcount < MAX_PUMPKINS; pcount++) {
                *pnum = (*pnum+1) % MAX_PUMPKINS;
                if (radius[*pnum] > 0)
                        return TRUE;
        }
        return FALSE;
} /*end nextLivePumpkinNumber */
 

/* handle_button
 * Takes mouse input, figures out which button was clicked. Dispatches user commands
 * as appropriate.
 *
 * SIDE EFFECT: can modify currentPumpkin, eyes, noses, and mouths in calling routine.
 * Can even "create" or "delete" a pumpkin
 *
 *
 */
void handle_button(int buttonNum, int *currentPumpkin, int centerX[], int centerY[], int rad
iuses[],
                   int eyes[], int noses[], int mouths[])
{
  int max_size = 80;

  assert (*currentPumpkin >= 0 && *currentPumpkin < MAX_PUMPKINS);

  switch(buttonNum) {
  case BUTTON_NEW_PUMPKIN:
          CreateNewPumpkin (currentPumpkin,
                  centerX, centerY,radiuses, eyes, noses, mouths);
          break;
  case BUTTON_DELETE:
          radiuses[*currentPumpkin] = -1; /*any neg. value make pumpkin invisible*/
          if (!NextLivePumpkinNumber (currentPumpkin, radiuses)) {
                  /* This must have been the last pumpkin */
                  radiuses[*currentPumpkin] = 3; /* make the pumpkin small instead */
          }
          break;
  case BUTTON_NEXT_PUMPKIN:
          if (!NextLivePumpkinNumber (currentPumpkin, radiuses)) { /*probably an error */
                  /* This must have been the last pumpkin */
                  radiuses[*currentPumpkin] = 3; /* make the pumpkin small instead */
          }
          break;
  case BUTTON_EYES_CIRCLE:
  case BUTTON_EYES_SQUARE:
  case BUTTON_EYES_ANGRY:
          eyes[*currentPumpkin] = buttonNum;
    break;
  case BUTTON_NOSE_TRIANGLE:
  case BUTTON_NOSE_PEAKY_NOSE:
  case BUTTON_NOSE_SQUARE:
          noses[*currentPumpkin] = buttonNum;
    break;
  case BUTTON_MOUTH_SMILE:
  case BUTTON_MOUTH_OHMY:
  case BUTTON_MOUTH_GRIN:
          mouths[*currentPumpkin] = buttonNum;
    break;
  case BUTTON_ENLARGE:
          radiuses[*currentPumpkin] += ENLARGE_INCREMENT;
          break;
  case BUTTON_SHRINK:
          if (radiuses[*currentPumpkin] >= 0)
                radiuses[*currentPumpkin] -= SHRINK_INCREMENT;
          /* a negative radius makes the pumpkin invisible*/
          /*But we don't want the radius to keep getting more an more negative*/

          /*Also, we don't want teensy tiny pumkins */
          if (radiuses[*currentPumpkin] < MIN_PUMPKIN_RADIUS)
                        radiuses[*currentPumpkin] = -1;
          break;
  default:
          assert(0);
    break;  /* Must have clicked below the buttons. */
  }
} /*end handle_button */
 

/*Create new pumpkin
If successful, returns true and sets currentPumpkin to the new one; sets
 the parallel arrays to appropriate default values.
If unsuccessful, returns false and leaves currentPumkin alone.
*/
int CreateNewPumpkin(int *currentPumpkin, int centerX[], int centerY[], int radiuses[],
                                         int eyes[], int noses[], int mouths[]) {
        int pcount;
        double verticalSpacing;
        int newy;

        for (pcount = 0; pcount < MAX_PUMPKINS; pcount ++) {
                *currentPumpkin = (1+*currentPumpkin) % MAX_PUMPKINS;
                if (radiuses[*currentPumpkin] <= 0.0) {
                        /* this entry is free to use */
                        verticalSpacing = (2.0*GP142_YMAX)/(2+MAX_PUMPKINS);
                        newy = (int) (GP142_YMAX - (*currentPumpkin +1)*verticalSpacing);
                        InitializeOnePumpkin (0, newy, 20,
                                &centerX[*currentPumpkin], &centerY[*currentPumpkin], &radiu
ses[*currentPumpkin],
                                &eyes[*currentPumpkin], &noses[*currentPumpkin], &mouths[*cu
rrentPumpkin]);
                        return TRUE;
                }
        }
        return FALSE;

}

/*
 *  EYES
 */

/*
 * Dispatch appropriate function to draw eyes on pumpkin selected by
 * parameter whichP.
 *
 */
void DrawEyes(int eyetype, int xCtr, int yCtr, int radius) {

  switch(eyetype) {
  case 0:
          /*no eye type has been set*/
          break;
  case BUTTON_EYES_CIRCLE:
    DrawCircleEyes(xCtr, yCtr, radius);
    break;
  case BUTTON_EYES_SQUARE:
    DrawSquareEyes(xCtr, yCtr, radius);
    break;
  case BUTTON_EYES_ANGRY:
    DrawAngryEyes(xCtr, yCtr, radius);
    break;
  default:
          assert(0);
    break;
  }
}
 

/*
 * Draw the circle eyes on the pumpkin centered at xCtr, yCtr.  Make
 * the eyes deltaXeyes apart.
 *
 */
void DrawCircleEyes(int xCtr, int yCtr, int radius)
{
 
        int deltaXeyes, yEyes;

        /* We'll draw these eyes about 1/3 of the way from the top of the
         * current pumpkin.  Since yCtr is 1/2 of the way down, we add 1/6
         * of the height of a pumpkin
         */
        yEyes = yCtr + (radius/3);

        /* We draw each eye at +- 1/6 of the width of a pumpkin */
        deltaXeyes = (radius/3);
        GP142_circleXY(BLACK, xCtr + deltaXeyes, yEyes, radius/6);
        GP142_circleXY(BLACK, xCtr - deltaXeyes, yEyes, radius/6);
}

/*
 * Draw the square eyes on the pumpkin centered at xCtr, yCtr.
 */
void DrawSquareEyes(int xCtr, int yCtr, int radius)
{
        int deltaXeyes, yEyes, eyesize;

        /* We'll draw these eyes about 1/3 of the way from the top of the
         * current pumpkin.  Since yCtr is 1/2 of the way down, we add 1/6
         * of the height of a pumpkin
         */
        yEyes = yCtr + (radius/3);

        /* We draw each eye at +- 1/6 of the width of a pumpkin */
        deltaXeyes = (radius/3);

        eyesize = radius/6;

        /* Draw left eye */
        GP142_rectangleXY(BLACK,
                                xCtr - deltaXeyes + eyesize, yEyes + eyesize,
                                xCtr - deltaXeyes - eyesize, yEyes - eyesize,
                                FILL);

        /* Draw right eye */
        GP142_rectangleXY(BLACK,
                                xCtr + deltaXeyes + eyesize, yEyes + eyesize,
                                xCtr + deltaXeyes - eyesize, yEyes - eyesize,
                                FILL);
}
 

/* Angry eyes
 * These eyes are made with triangles whose vertices are skewed.
 *
 */
void DrawAngryEyes(int xCtr, int yCtr, int radius)
{

        int deltaXeyes, yEyes;
        int eyesize;

        /*
         * I like these eyes drawn a bit lower on the face than the others.
         */
        yEyes = yCtr + (radius/4);

        /* Because these eyes are extra-wide, we'll use a smaller delta  */
        deltaXeyes = (radius/5);

        eyesize = radius/4;

        /* Draw left eye */
        GP142_triangleXY(BLACK,
                         xCtr - deltaXeyes + eyesize/2, yEyes + eyesize/2,
                         xCtr - deltaXeyes - 2*eyesize, yEyes + eyesize,
                         xCtr - deltaXeyes - eyesize/6, yEyes - eyesize/2,
                         FILL);

        /* Draw right eye */
        GP142_triangleXY(BLACK,
                         xCtr + deltaXeyes - eyesize/2, yEyes + eyesize/2,
                         xCtr + deltaXeyes + 2*eyesize, yEyes + eyesize,
                         xCtr + deltaXeyes + eyesize/6, yEyes - eyesize/2,
                         FILL);
}

/*
 * NOSES
 */

/*
 * Dispatch appropriate funtion to draw noses on pumpkin selected by
 * parameter whichP.
 */
void DrawNose(int shapetype, int xCtr, int yCtr, int radius)
{
  switch(shapetype) {
  case 0:
          /*no nose type has been set*/
          break;
  case BUTTON_NOSE_TRIANGLE:
    DrawTriangleNose(xCtr, yCtr, radius);
    break;
  case BUTTON_NOSE_SQUARE:
    DrawSquareNose(xCtr, yCtr, radius);
    break;
  case BUTTON_NOSE_PEAKY_NOSE:
    DrawUglyNose(xCtr, yCtr, radius);
    break;
  default: /*should never get here */
          assert(0);
    break;
  }
}

/*
 * Calls:
 *            GP142_triangleXY()
 *
 * Called by: DrawNose()
 */
void DrawTriangleNose(int xCtr, int yCtr, int radius)
{
        int nosesize = radius/3;

  GP142_triangleXY(BLACK,
                   xCtr - nosesize/2, yCtr - nosesize/2,
                   xCtr + nosesize/2, yCtr - nosesize/2,
                   xCtr, yCtr + nosesize/2,
                   FILL);
}
 

/*
 */
void DrawUglyNose(int xCtr, int yCtr, int radius)
{
        int nosesize = radius/3;

  /* Make this shape by drawing 3 overlapping triangles */

  /* Center */
  GP142_triangleXY(BLACK,
                   xCtr - nosesize/2, yCtr - nosesize/2,
                   xCtr + nosesize/2, yCtr - nosesize/2,
                   xCtr, yCtr + nosesize/2,
                   FILL);

  /* Left */
  GP142_triangleXY(BLACK,
                   xCtr, yCtr - nosesize/2,
                   xCtr - nosesize, yCtr - nosesize/2,
                   xCtr - nosesize/2, yCtr + nosesize/5,
                   FILL);

  /* Right */
  GP142_triangleXY(BLACK,
                   xCtr, yCtr - nosesize/2,
                   xCtr + nosesize, yCtr - nosesize/2,
                   FILL);
}
 

/* Square Nose
 * Calls:
 */
void DrawSquareNose(int xCtr, int yCtr, int radius)
{
        int nosesize = radius/3;

  GP142_rectangleXY(BLACK,
                    xCtr - nosesize/2, yCtr + nosesize/2,
                    xCtr + nosesize/2, yCtr - nosesize/2,
                    FILL);
}

/*
 * Dispatch appropriate funtion to draw mouth on pumpkin selected by
 * parameter whichP.
 */
void DrawMouth(int shapetype, int xCtr, int yCtr, int radius)
{
  switch(shapetype) {
  case 0:
          /*no mouth type has been set*/
          break;
  case BUTTON_MOUTH_SMILE:
    DrawSmileMouth(xCtr, yCtr, radius);
    break;
  case BUTTON_MOUTH_OHMY:
    DrawOhMyMouth(xCtr, yCtr, radius);
    break;
  case BUTTON_MOUTH_GRIN:
    DrawGrinMouth(xCtr, yCtr, radius);
    break;
  default:
          assert(0);
    break;
  }
}
 
 

/*
 * This is the simplest mouth to draw-- it is just an oval.
 */
void DrawOhMyMouth(int xCtr, int yCtr, int radius)
{
 
  int mouthheight = radius/2;
  int mouthwidth = radius;

        /* Draw the mouth in lower third of pumpkin */
  yCtr = yCtr - radius/3;

  /* The bounding box calculations are embedded right in the actual
   * parameters.
   */
  GP142_ovalXY(BLACK,
               xCtr - mouthwidth/2, yCtr,
               xCtr + mouthwidth/2, yCtr - mouthheight,
               FILL);
}
 

/*
 * Draw the mouths by connecting a series of lines.  This is a brute-force
 * method, whereby you just hard-code in a series of connecting lines.
 *
  */
void DrawSmileMouth(int xCtr, int yCtr, int radius)
{
        int mouthheight = radius/2;
        int mouthwidth = radius;
  /* Draw the mouth in lower third of pumpkin */
  yCtr = yCtr - radius/3;

  GP142_lineXY(BLACK,
               xCtr - mouthwidth/2, yCtr,
               xCtr - mouthwidth/3, yCtr - 2*mouthheight/3, 3);
  GP142_lineXY(BLACK,
               xCtr - mouthwidth/3, yCtr - 2*mouthheight/3,
               xCtr - mouthwidth/6, yCtr - mouthheight, 3);
  GP142_lineXY(BLACK,
               xCtr - mouthwidth/6, yCtr - mouthheight,
               xCtr + mouthwidth/6, yCtr - mouthheight, 3);
  GP142_lineXY(BLACK,
               xCtr + mouthwidth/6, yCtr - mouthheight,
               xCtr + mouthwidth/3, yCtr - 2*mouthheight/3, 3);
  GP142_lineXY(BLACK,
               xCtr + mouthwidth/3, yCtr - 2*mouthheight/3,
               xCtr + mouthwidth/2, yCtr, 3);
}
 

/*
 * This function demonstrates how you might use an array to
 * draw complex objects.  Two arrays are used to store the
 * various vertices of an object that will represent a mouth.
 * We'll connect these vertices with a series of straight
 * lines.  To make the function general, we values we store in
 * the array will be a series of offsets from the center of
 * the pumpkin whichP.  These vertices were developed by trial
 * and error.

 * Unfortunately, this method does not easily scale.  The solution doesn't fix this.
 *
 */
void DrawGrinMouth(int xCtr, int yCtr, int radius)
{
#define VERTICES 10

  int i;

  /* Offsets in x direction to be added to x center of pumpkin.  Each item in
   * this array defines a vertex of the polygon to be drawn.
   */
  int xVertices[VERTICES] = {-60, -30, -10, -10,  10,  10,  30,  60,
                                              30, -30};
  /* ... corresponding y offsetts */
  int yVertices[VERTICES] = {-10, -30, -30, -40, -40, -30, -30, -10,
                                              -60, -60};
  int x1, y1, x2, y2;

  /* Draw the mouth by drawing lines between all vertices except the
     one between the last vertex and the first one  */
  for (i = 0; i < (VERTICES - 1); i++)
    {
      /* For easy debugging, find the endpoint of the line */
      x1 = xCtr + xVertices[i];
      y1 = yCtr + yVertices[i];
      x2 = xCtr + xVertices[i + 1];
      y2 = yCtr + yVertices[i + 1];

      GP142_lineXY(BLACK,x1, y1, x2, y2, 3); /* Draw the line */

    }
  /* Now fill in the last segment, which is between the first one
     and the last */
  GP142_lineXY(BLACK,
               xCtr + xVertices[VERTICES - 1], yCtr + yVertices[VERTICES - 1],
               xCtr + xVertices[0], yCtr + yVertices[0], 3);

}

/*
 * Draw the main body of pumpkin selected by whichP.
 *
 */
void DrawPumpkinBody(int whichP, int currentPumpkin,
                                         int centerX[], int centerY[], int radiuses[])
{
  /* Find the center of this pumpkin */
  int xCtr = centerX[whichP], yCtr = centerY[whichP];
  int radius = radiuses[whichP];

  int PColor;

  /* Draw the body of the pumpkin. Start with a stem, which is an inverted
   * triangle.  Drawing the body afterwords will cause the tip of the
   * triangle to be covered up, giving the illusion of a trapezoid.
   */
  GP142_triangleXY(GREEN,
                   xCtr, yCtr + radius + 15,
                   xCtr + 8, yCtr + radius + 15,
                   xCtr, yCtr + radius - 10,
                   FILL);

  if (whichP == currentPumpkin)
          PColor = HIGHLIGHT_COLOR;
  else
          PColor = BASIC_PUMPKIN_COLOR;

  GP142_circleXY(PColor, xCtr, yCtr, radius);

}

/*Draw all the pumkins.
The order should be such that the current pumpkin is drawn last.
This way, the current pumpkin is on top.
Otherwise, the current pumpkin could be eclipsed.
*/
void DrawAllPumpkins(int currentPumpkin,
                                         int centerX[], int centerY[], int radiuses[], int e
yes[], int noses[], int mouths[]) {
        int count, i;

        for (count = 0; count <= MAX_PUMPKINS-1; count++) {
                i = (currentPumpkin+count+1) % MAX_PUMPKINS;
                DrawPumpkin(i, currentPumpkin, centerX, centerY, radiuses, eyes, noses, mout
hs);
        }
}
 

/*
 * Draw the a particular (whichP) pumpkin.  Valid values of whichP are from
 * 0 to MAX_PUMPKINS - 1
 *
 */
void DrawPumpkin(int whichP, int currentPumpkin,
                                 int centerX[], int centerY[], int radiuses[],
                                 int eyes[], int noses[], int mouths[])
{
        int xCtr, yCtr, radius;
        int eyetype, nosetype, mouthtype;

  /* Verify that this is a valid pumpkin to draw. */
        assert (whichP >= 0 && whichP < MAX_PUMPKINS);
  if (whichP < 0 || whichP > MAX_PUMPKINS - 1) return;

  radius = radiuses[whichP];
  if (radius < 0.0) {
          return; /*negative radius means pumpkin is not visible*/
  }

  xCtr = centerX[whichP];
  yCtr = centerY[whichP];
  eyetype = eyes[whichP];
  nosetype = noses[whichP];
  mouthtype = mouths[whichP];

  DrawPumpkinBody(whichP, currentPumpkin, centerX, centerY, radiuses);
  DrawEyes(eyetype, xCtr, yCtr, radius);
  DrawNose(nosetype, xCtr, yCtr, radius);
  DrawMouth(mouthtype, xCtr, yCtr, radius);
}

/* IsInsidePumpkin
/* Sets the pointer param to the number of a pumpkin which the given point is inside.
If a point is within more than one pumpkin, the pumpkin with the lowest number
(smallest array index) is chosen.

If not exactly inside any pumpkin, look again with a wider radius, and then
again with a slightly wider radius, until a limit has been reached.

  If the function returns true, the Pumknun parameter has been changed, to show which
  pumpkin the point is inside (or closest to being inside).
  If the function returns false, Pumknum has not been changed.

  Note that this function does NOT move any pumpkins.
  Movement is done by the function which calls this one.
*/
#define RADIUS_LIMIT            2.0
#define RADIUS_INCREMENT        0.1

int IsInsidePumpkin (int x, int y, int PCenterX[], int PCenterY[], int PRadius[],
                                         int *Punknum) {
        int pnum;
        double radiusFactor;

        for (radiusFactor = 1.0; radiusFactor < RADIUS_LIMIT; radiusFactor += RADIUS_INCREME
NT) {
                for (pnum = 0; pnum < MAX_PUMPKINS &&
                                        !(IsInsideCircle (x,y, PCenterX[pnum], PCenterY[pnum
], radiusFactor*PRadius[pnum])); pnum++)
                {}
                if (pnum < MAX_PUMPKINS) {
                        *Punknum = pnum;
                        return TRUE;
                }
        }
        return FALSE;
}
 

/*compute and return the usual Cartesian (Euclidean) distance
        between two points in the plane */
double distance (double XPoint1, double YPoint1, double XPoint2, double YPoint2) {
        double xdiff = XPoint1 - XPoint2;
        double ydiff = YPoint1 - YPoint2;

        return sqrt (xdiff*xdiff + ydiff*ydiff);
}

/* Return a Boolean to say whether point (X,Y) is inside the circle of
        given center and radius.  "inside" here includes on the boundary of the circle
        */
int IsInsideCircle (double X, double Y, double XCenter, double YCenter, double radius) {
        return (distance (X,Y, XCenter, YCenter) <= radius);
}