/*
*  A Simple Eased Drawing Program
*  By Nicholas Zambetti
*
*  left mouse  - used for drawing
*  'q' key     - exits
*  'c' key     - clears page
*
*  To Compile On Mac OS X
*  gcc -ansi -Wall drawEasedGL.c -o drawEasedGL -lobjc -framework OpenGL -framework GLUT
*
*/

#pragma mark ---- Includes ----

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <GLUT/glut.h>
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>

#pragma mark ---- Definitions ----

#define FPS 80
#define WINDOW_WIDTH 560
#define WINDOW_HEIGHT 380
#define SPEED 0.05

#pragma mark ---- Type Declarations ----

typedef struct {
    float x;
    float y;
} vector;

#pragma mark ---- Variable Declarations ----

int redrawDelay = 1000 / FPS;
int windowWidth;
int windowHeight;
int leftButtonDown = 0;
vector mouse;
vector filteredMouse;
vector prevFilteredMouse;

#pragma mark ---- Functions ----

#pragma mark ---- GLUT Callbacks ----

void onDisplay()
{
    if(leftButtonDown) {
        glBegin(GL_LINE_STRIP);
        glVertex2f(prevFilteredMouse.x, prevFilteredMouse.y);
        glVertex2f(filteredMouse.x, filteredMouse.y);
        glEnd();
    }
    glutSwapBuffers();
}
    
void onResize(int width, int height)
{
    windowWidth = width;
    windowHeight = height;
    glViewport(0, 0, windowWidth, windowHeight);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, windowWidth, 0, windowHeight, -1, 1);
    glScalef(1, -1, 1);
    glTranslatef(0, -windowHeight, 0);
    glMatrixMode(GL_MODELVIEW);
    glClear(GL_COLOR_BUFFER_BIT);
    glutPostRedisplay();
}

void onMouseButton(int button, int state, int xPos, int yPos)
{
    if(button == GLUT_LEFT_BUTTON) {
        switch(state) {
            case GLUT_UP:
            leftButtonDown = 0;
            break;
        case GLUT_DOWN:
            leftButtonDown = 1;
            break;
        }
    }
}

void onMouseMove(int x, int y)
{
    mouse.x = (float) x;
    mouse.y = (float) y;
}

void onTick(int timerId)
{
    if(leftButtonDown) {
        prevFilteredMouse.x = filteredMouse.x;
        prevFilteredMouse.y = filteredMouse.y;
        filteredMouse.x += (mouse.x - filteredMouse.x) * SPEED;
        filteredMouse.y += (mouse.y - filteredMouse.y) * SPEED;
    } else {
        prevFilteredMouse.x = filteredMouse.x = mouse.x;
        prevFilteredMouse.y = filteredMouse.y = mouse.y;
    }
    glutPostRedisplay();
    glutTimerFunc(redrawDelay, onTick, 0);
}

void onKeyChar(unsigned char inKey, int x, int y)
{
    switch(inKey) {
        case 27:
        case 'q':
        case 'Q':
            exit(0);
            break;
        case 'c':
        case 'C':
            glClear(GL_COLOR_BUFFER_BIT);
            break;
    }
}

#pragma mark ---- main ----

int main(int argc, const char *argv[])
{
    /* glut init */
    glutInit(&argc, (char **) argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowPosition(50, 50);
    glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
    glutCreateWindow("Draw");

    /* gl init */
    glShadeModel(GL_FLAT);
    glClearColor(0.6, 0.2, 0.0, 1.0);
    onResize(WINDOW_WIDTH, WINDOW_HEIGHT);
    glColor3f(1.0, 1.0, 1.0);

    /* register glut event callbacks */
    glutReshapeFunc(onResize);
    glutDisplayFunc(onDisplay);
    glutKeyboardFunc(onKeyChar);
    glutMouseFunc(onMouseButton);
    glutMotionFunc(onMouseMove);
    glutPassiveMotionFunc(onMouseMove);
    glutTimerFunc(redrawDelay, onTick, 0);

    /* begin event loop */
    glutMainLoop();

    return 0;
}
