/*************************************************************************
 * Copyright (c) 2011 AT&T Intellectual Property 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors: Details at https://graphviz.org
 *************************************************************************/

#include "glutrender.h"
#include "viewport.h"
#include "arcball.h"
#include "appmouse.h"
#include "glexpose.h"
#include "glcompui.h"
#include <util/exit.h>

    /*call backs */

static float begin_x = 0.0;
static float begin_y = 0.0;
static float dx = 0.0;
static float dy = 0.0;

/*mouse mode mapping funvtion from glut to glcomp*/
static glMouseButtonType getGlCompMouseType(int n)
{
    switch (n) {
    case GLUT_LEFT_BUTTON:
	return glMouseLeftButton;
    case GLUT_RIGHT_BUTTON:
	return glMouseMiddleButton;
    case GLUT_MIDDLE_BUTTON:
	return glMouseRightButton;

    default:
	return glMouseLeftButton;
    }
}


static void cb_reshape(int width, int height)
{
    int vPort[4];
    float aspect;
    view->w = width;
    view->h = height;
    if (view->widgets)
	glcompsetUpdateBorder(view->widgets, view->w, view->h);
    glViewport(0, 0, view->w, view->h);
    /* get current viewport */
    glGetIntegerv(GL_VIEWPORT, vPort);
    /* setup various opengl things that we need */
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    init_arcBall(view->arcball, view->w, view->h);
    if (view->w > view->h) {
	aspect = (float) view->w / (float) view->h;
	glOrtho(-aspect * GL_VIEWPORT_FACTOR, aspect * GL_VIEWPORT_FACTOR,
		GL_VIEWPORT_FACTOR * -1, GL_VIEWPORT_FACTOR, -1500, 1500);
    } else {
	aspect = (float) view->h / (float) view->w;
	glOrtho(GL_VIEWPORT_FACTOR * -1, GL_VIEWPORT_FACTOR,
		-aspect * GL_VIEWPORT_FACTOR, aspect * GL_VIEWPORT_FACTOR,
		-1500, 1500);
    }

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
	/*** OpenGL END ***/

}

static void cb_display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    glexpose_main(view);	//draw all stuff
    glutSwapBuffers();
    if (view->initFile) {
	view->initFile = 0;
	if (view->activeGraph == 0)
	    close_graph(view);
	add_graph_to_viewport_from_file(view->initFileName);
    }

}

static void cb_mouseclick(int button, int state, int x, int y)
{
    if (view->g == 0) return;
    begin_x = (float) x;
    begin_y = (float) y;
    if(state==GLUT_DOWN)
    {
	view->widgets->base.common.functions.mousedown(&view->widgets->base,
	                                               (float)x, (float)y,
	                                               getGlCompMouseType(button));
	if (button ==  GLUT_LEFT_BUTTON)
	    appmouse_left_click_down(view,x,y);
        if (button ==  GLUT_RIGHT_BUTTON)
	    appmouse_right_click_down(view,x,y);
	if (button ==  GLUT_MIDDLE_BUTTON)
	    appmouse_middle_click_down(view,x,y);
    }
    else
    {
	view->arcball->isDragging = 0;
	view->widgets->base.common.functions.mouseup(&view->widgets->base,
	                                             (float)x, (float)y,
	                                             getGlCompMouseType(button));

	if (button ==  GLUT_LEFT_BUTTON)
	    appmouse_left_click_up(view,x,y);
	if (button ==  GLUT_LEFT_BUTTON)
	    appmouse_right_click_up(view,x,y);
	if (button ==  GLUT_MIDDLE_BUTTON)
	    appmouse_middle_click_up(view,x,y);
	dx = 0.0;
	dy = 0.0;
    }
    cb_display();

}

// mouse moving with a button clicked (dragging)
static void cb_drag(int X, int Y)
{

    float x = (float) X;
    float y = (float) Y;

    if (view->widgets)
	view->widgets->base.common.functions.mouseover(&view->widgets->base, x, y);

    dx = x - begin_x;
    dy = y - begin_y;
    view->mouse.dragX = dx;
    view->mouse.dragY = dy;
    appmouse_move(view,x,y);

    if((view->mouse.t==glMouseLeftButton) && (view->mouse.down)  )
	appmouse_left_drag(view,x,y);
    if((view->mouse.t==glMouseRightButton) && (view->mouse.down))
	appmouse_right_drag(view,x,y);
    if((view->mouse.t==glMouseMiddleButton) && (view->mouse.down))
	appmouse_middle_drag(view,x,y);
    begin_x = x;
    begin_y = y;
    cb_display();

}

static void cb_keyboard(unsigned char key, int x, int y)
{
    (void)x;
    (void)y;

    if (key==27)    /*ESC*/
	graphviz_exit(1);
    if(key=='3')
	switch2D3D(NULL, 0, 0,glMouseLeftButton);
    if(key=='c')
        menu_click_center(NULL, 0, 0,glMouseLeftButton);

    if(key=='+')
        menu_click_zoom_plus(NULL, 0, 0,glMouseLeftButton);
    if(key=='-')
        menu_click_zoom_minus(NULL, 0, 0,glMouseLeftButton);
    if(key=='p')
        menu_click_pan(NULL, 0, 0,glMouseLeftButton);
    

    appmouse_key_press(view,key);
}

static void cb_keyboard_up(unsigned char key, int x, int y)
{
    (void)key;
    (void)x;
    (void)y;

    appmouse_key_release(view);
}

static void cb_special_key(int key, int x, int y)
{
    (void)x;
    (void)y;

    if(key==GLUT_KEY_F1)
    {
	printf("Currently help is not available\n");
    }
    appmouse_key_press(view,key);

}

static void cb_special_key_up(int key, int x, int y)
{
    (void)x;
    (void)y;

    if(key==GLUT_KEY_F1)
    {
	printf("Currently help is not available\n");
    }
    appmouse_key_release(view);

}

static void cb_game_mode(char* optArg)
{
    
    glutGameModeString(optArg);
    if (glutGameModeGet(GLUT_GAME_MODE_POSSIBLE)) 
    {
    	glutEnterGameMode();
    }
    else 
    {
	printf("smyrna cannot initialize requested screen resolution and rate!\n");
	graphviz_exit(-1);

    }
}

int cb_glutinit(int w, int h, int *argcp, char *argv[], char *optArg) {
    /*
    w,h: width and height of the window in pixels
    argcp argv: main function's parameters, required for glutinit
    */



    glutInit(argcp,argv); //this is required by some OS.

	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
	// The Type Of Depth Testing To Do
        glDisable(GL_DEPTH);
	glClearDepth(1.0f);		// Depth Buffer Setup
	glEnable(GL_DEPTH_TEST);							// Enables Depth Testing
	glDepthFunc(GL_LEQUAL);								// The Type Of Depth Testing To 
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	// Really Nice Perspective Calculations


    cb_game_mode(optArg);

    /*register callbacks here*/
    glutDisplayFunc(cb_display);
    glutReshapeFunc(cb_reshape);
    glutKeyboardFunc(cb_keyboard);
    glutKeyboardUpFunc( cb_keyboard_up);
    glutMouseFunc(cb_mouseclick);
    glutMotionFunc(cb_drag);
    glutPassiveMotionFunc(NULL);
    glutVisibilityFunc(NULL);
    glutEntryFunc(NULL);
    glutSpecialFunc(cb_special_key);
    glutSpecialUpFunc(cb_special_key_up);

    //Attach extra call backs if needed in the future
/*  glutOverlayDisplayFunc
    glutSpaceballMotionFunc
    glutSpaceballRotateFunc
    glutSpaceballButtonFunc
    glutButtonBoxFunc
    glutDialsFunc
    glutTabletMotionFunc
    glutTabletButtonFunc
    glutMenuStatusFunc
    glutIdleFunc
    glutTimerFunc 
*/


    //pass control to glut
    cb_reshape(w,h);
    glutMainLoop ();


    return 0; //we should never reach here

}
