

/*
 * triangle.c -- A simple example of OpenGL and GLUT.
 *
 * I have hacked this from the original triangle.c program to check that a few
 * more of the capabilities that we will be needing are easy to
 * implement--Kobus.
 *
 * IMPORTANT--this is a proof of concept hack. It is not scalable. Student
 * programs are expected to better structured! 
 *
 */

#include <stdio.h>       
#include <stdlib.h>       

/*
// Sometimes glu.h includes glut.h--sometimes not.
*/
#ifdef MAC_OSX
#    include <OpenGL/glu.h>
#    include <GLUT/glut.h>
#else 
#    include <GL/glu.h>       
#    include <GL/glut.h>       
#endif



/*
// Some debugging macros. Normally I make macros upper case; these are
// exceptions. 
*/

#define dbc(X)                                                                 \
    if (debug_level >= 1)                                                      \
    {                                                                          \
        fprintf(stderr,                                                        \
                "dbc: "#X" is ->%c<- (0x%x, %d) on source line %d of %s\n",    \
                (int)(X), (int)(X), (int)(X), __LINE__, __FILE__);             \
    }   

#define dbp(X) \
    if (debug_level >= 1)                                                      \
    {                                                                          \
        fprintf(stderr, "%s\n", (X));                                          \
    }   

#define dbi(X)                                                                 \
    if (debug_level >= 1)                                                      \
    {                                                                          \
        fprintf(stderr,                                                        \
                "dbi: "#X" is %ld on source line %d of %s\n", (long)(X),       \
               __LINE__, __FILE__);                                            \
    }   

#define  dbw()                                                                 \
    if (debug_level >= 1)                                                      \
    {                                                                          \
        fprintf(stderr,                                                        \
                "At line %d of %s\n", __LINE__, __FILE__);                     \
    }   


/* -------------------------------------------------------------------------- */

#define NOT_SET  (-99)     /* Something negative. */

/* -------------------------------------------------------------------------- */

/*
// Watch name clashes. I'm sticking my initials in front of these so that when
// I include someone elses code, I won't have to worry if, say, "SQUARE" is in
// their namespace.
*/

typedef enum KJB_object
{
    KJB_TRIANGLE, 
    KJB_SQUARE
} 
KJB_object;


typedef enum KJB_color
{
    KJB_RED, 
    KJB_GREEN,
    KJB_BLUE,
    KJB_WHITE
}
KJB_color;

/* -------------------------------------------------------------------------- */

/* 
// Some prefer to write their programs backwards, so we don't need to declare
// functions up front--I prefer to write top down, so I have added function
// declarations to the original program. 
*/

static void display_CB(void);            /* Called whenever redisplay needed */
static void key_CB(unsigned char key, int x, int y);     /* Called on key press */
static void mouse_CB(int button, int state, int x, int y);  /* Called on mouse click */
static void mouse_move_CB(int x, int y);  /* Called on mouse click */
static void select_triangle_color(int value);  /* For menu */
static void add_object_CB(int value);  /* For submenu */

/* -------------------------------------------------------------------------- */

static int debug_level = 1;  /* Used by the macros above. */

/*
// For a real program you will need to store position, color, etc. of each item
// relavent to your world as part of a carefully thought out data structure.
// But here we have only one object and this is a "test" program, so I have
// used global variables for my sole object. 
*/
static int displacement_x;  /* Displacement from the original position. */
static int displacement_y;  /* Initialized at 0 because static. */
static int drag_start_x = NOT_SET;  
static int drag_start_y = NOT_SET;
static float triangle_red = 1.0; 
static float triangle_green = 1.0; 
static float triangle_blue = 1.0; 

/* -------------------------------------------------------------------------- */

int main(int argc, char *argv[])
{
    int win;
    int submenu;


    glutInit(&argc, argv);                /* initialize GLUT system */

    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);


    glutInitWindowSize(400, 500); 

    win = glutCreateWindow("Triangle");   /* create window */

    /* set background to black */
    glClearColor((GLclampf)0.0,(GLclampf)0.0,(GLclampf)0.0,(GLclampf)0.0);  

    gluOrtho2D(0.0, 400.0, 0.0, 500.0); /* how object is mapped to window */
    glutDisplayFunc(display_CB);        /* set window's display callback */
    glutKeyboardFunc(key_CB);           /* set window's key callback */
    glutMouseFunc(mouse_CB);            /* set window's mouse callback */
    glutMotionFunc(mouse_move_CB);      /* set window's mouse move with button pressed callback */

    /* Create a menu which is accessed by the right button. */
    submenu = glutCreateMenu(select_triangle_color);
    glutAddMenuEntry("Red", KJB_RED);
    glutAddMenuEntry("Green", KJB_GREEN);
    glutAddMenuEntry("Blue", KJB_BLUE);
    glutAddMenuEntry("White", KJB_WHITE);
    glutCreateMenu(add_object_CB);
    glutAddMenuEntry("Triangle", KJB_TRIANGLE);
    glutAddMenuEntry("Square", KJB_SQUARE);
    glutAddSubMenu("Color", submenu);
    glutAttachMenu(GLUT_RIGHT_BUTTON);

    glutMainLoop();                       /* start processing events... */

    /* Execution never reaches this point, so return value is failure */

    return EXIT_FAILURE;
}

/*  /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\   */

/*
// Function called whenever redisplay needed (try hiding the window and then
// exposing it. The debug macro should print something.)
*/
static void display_CB(void)
{
    dbw();  /* Print debugging stuff on stderr. */

    glClear(GL_COLOR_BUFFER_BIT);         /* clear the display */

    /* set current color */
    glColor3d(triangle_red, triangle_green, triangle_blue);

    /* draw filled triangle */
    glBegin(GL_POLYGON);

    /* Specify each vertex of triangle */
    glVertex2i(200 + displacement_x, 125 - displacement_y);
    glVertex2i(100 + displacement_x, 375 - displacement_y);
    glVertex2i(300 + displacement_x, 375 - displacement_y);

#ifdef WANT_EXACT
    /*
     * It seems that OpenGL may handle floating point arithmetic badly, or, at
     * least in a hard to describe way, and thus, if you really want a point to
     * be displayed exactly where you say, it is best to use a floating point
     * and add 0.5 to gaurd against roundoff problems. Note that the integer
     * version (glVertex2i()), just maps the integers to floating point, and
     * then subsequent calculations can lose precision, putting the points a bit
     * of from what you expect. 
     *
     * (At least, this is the latest theory. Thanks to David Forester for
     * pointing out that the original explanation, based on a suspected off by
     * one problem in gluOrtho2D(), is likely to be incorrect, and that loss of
     * precision was likely the problem).  
    */
    glVertex2d(0.5 + (double)(200 + displacement_x), 
               0.5 + (double)(125 - displacement_y));
    glVertex2d(0.5 + (double)(100 + displacement_x), 
               0.5 + (double)(375 - displacement_y));
    glVertex2d(0.5 + (double)(300 + displacement_x), 
               0.5 + (double)(375 - displacement_y));
#endif 

    glEnd();           /* OpenGL draws the filled triangle */
    glFlush();         /* Complete any pending operations */

    glutSwapBuffers(); /* Make the drawing buffer the frame buffer 
                          and vice versa */

}

/*  /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\   */

/* Function called on key press */
static void key_CB(unsigned char key, int x, int y) 
{
    dbc(key);   /* Print debugging stuff on stderr. */
    dbi(x);     /* Print debugging stuff on stderr. */
    dbi(y);     /* Print debugging stuff on stderr. */

    if( key == 'q' ) exit(0);
}

/*  /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\   */

/* Function called on mouse click */
static void mouse_CB(int button, int state, int x, int y)  
{
    dbi(button);  /* Print debugging stuff on stderr. */
    dbi(state);   /* Print debugging stuff on stderr. */
    dbi(x);       /* Print debugging stuff on stderr. */
    dbi(y);       /* Print debugging stuff on stderr. */

    if (button == 0)
    {
        if (state == 0)
        {
            dbp("Press"); 
            drag_start_x = x;
            drag_start_y = y;
        }
        else if (state == 1)
        {
            dbp("Release"); 
            drag_start_x = NOT_SET;
            drag_start_y = NOT_SET;
        }
    }
}

/*  /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\   */

/* Function called on mouse move while depressed. */
static void mouse_move_CB(int x, int y)  
{
    dbi(x);             /* Print debugging stuff on stderr. */
    dbi(y);             /* Print debugging stuff on stderr. */
    dbi(drag_start_x);  /* Print debugging stuff on stderr. */
    dbi(drag_start_y);  /* Print debugging stuff on stderr. */

    if ((drag_start_x >= 0) && (drag_start_y >= 0))
    {
        displacement_x += (x - drag_start_x);
        displacement_y += (y - drag_start_y);

        display_CB(); 

        drag_start_x = x;
        drag_start_y = y; 
    }
}

/*  /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\   */

static void add_object_CB(int value)
{
    dbi(value);   /* Print debugging stuff on stderr. */

    if (value == KJB_TRIANGLE)
    {
        fprintf(stderr, "We only do one triangle, and you already have it.\n");
    }
    else if (value == KJB_SQUARE)
    {
        fprintf(stderr, "We only do triangles; you can't have a square.\n");
    }
    else 
    {
        dbp("\n!!!  Can't happen bug  !!!\n");
        abort();
    }
}

/*  /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\   */

static void select_triangle_color(int value)
{
    dbi(value);    /* Print debugging stuff on stderr. */

    if (value == KJB_RED)
    {
        triangle_red = 1.0;
        triangle_green = 0.0;
        triangle_blue = 0.0;
       
    }
    else if (value == KJB_GREEN)
    {
        triangle_red = 0.0;
        triangle_green = 1.0;
        triangle_blue = 0.0;
       
    }
    else if (value == KJB_BLUE)
    {
        triangle_red = 0.0;
        triangle_green = 0.0;
        triangle_blue = 1.0;
       
    }
    else if (value == KJB_WHITE)
    {
        triangle_red = 1.0;
        triangle_green = 1.0;
        triangle_blue = 1.0;
       
    }
    else 
    {
        dbp("\n!!!  Can't happen bug  !!!\n");
        abort();
    }

    display_CB(); 
}



