
/*
 * 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! 
 *
 */

/*
// Sometimes glu.h includes glut.h--sometimes not.
*/
#include <GL/glut.h>       
#include <GL/glu.h>       

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


/*
// 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 themacros 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);          /* width=400pixels height=500pixels */
    win = glutCreateWindow("Triangle");   /* create window */

    /* From this point on the current window is win */

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

    /*
    // Note that the sizes of gluOrtho2D are one more than the window size
    // handed to glutInitWindowSize above. This is because (as far as I can
    // tell,  OpenGl treats the args in gluOrtho2D as absoulute sizes, and since
    // counting starts at 0, the actual size is one more. This of course assumes
    // that we want an exact correspondance from the object coords to the window
    // coords.  However, not having the exact correspondance confused a number
    // of people doing Assignment 1 in the Fall 2002 version of UA CS433. 
    */
    gluOrtho2D(0.0,401.0,0.0,501.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);

    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(); 
}



