/*
 *	Computação Gráfica - Visualizacao interativa de um modelo 
 *                      de Pontos com : ArcBall
 *	Professor Marcelo Gattass
 *
 *	@file mainIUP.c 
 *
 *	@author 
 *			- Marcelo Gattass
 *
 *	@date
 *			criacao:		20 de novembro de 2005
 *       modificado: 09 de outubro de 2007
 *
 *	@version 1.1
 *       
 *
 */

#include <stdio.h>
#include <time.h>
#include <math.h>

/*- Inclusao das bibliotecas IUP e CD: ------------------------------------*/
#include "iup.h"                    /* interface das funcoes IUP */
#include "iupgl.h"                  /* interface da funcao do canvas do OpenGL no IUP */
#include "iupcontrols.h"            /* interface para imagens dos botoes */
#include <windows.h>                /* inclui as definicoes do windows para o OpenGL */
#include <gl/gl.h>                  /* prototypes do OpenGL */
#include <gl/glu.h>                 /* prototypes do OpenGL */

#include "algebra.h"
#include "arcball.h"
#include "colorcube.h"
#include "scene.h"

Arcball* arcball;
Matrix Rot;  /* matriz de rotacao oriunda do arcball */

int color_cube=1,scene_loaded=0; /* inicia desenhando o cubo e sem cena carregada */


/* camera */
struct camera {
   double eyex,eyey,eyez;
   double atx,aty,atz;
   double upx,upy,upz;
   double fovy;
   double zNear,zFar;
   double left,right,top,bottom;
   double width,height;
   double radius;  /* nao e' da camera mas sim da esfera que envolve a cena */
} cam = {0.,0.,2*1.73, 0.,0.,0., 0.,1.,0., 60., 1.,10., 600.,600., 1.73} ;


int flag_ortho=0;       /* define se a projecao e' ortografica ou conica */
int flag_draw_axis=0;   /* desenha ou nao os eixos principais dos pontos */

Ihandle  ;  /* ponteiro IUP dos botoes on/off para controlar as flags acima */
Ihandle* canvas;        /* ponteiro IUP dos canvas */
Ihandle* label;         /* ponteiro IUP do label para colocar mensagens para usuario */


int old_x=300,old_y=300;  /* posicoes iniciais do mouse */



/* Dialogo de selecao de arquivo  */
static char * get_file_name( void )
{
  Ihandle* getfile = IupFileDlg();
  char* filename = NULL;

  IupSetAttribute(getfile, IUP_TITLE, "Abertura de arquivo"  );
  IupSetAttribute(getfile, IUP_DIALOGTYPE, IUP_OPEN);
  IupSetAttribute(getfile, IUP_FILTER, "*.txt");
  IupSetAttribute(getfile, IUP_FILTERINFO, "Arquivo de pontos (*.txt)");
  IupPopup(getfile, IUP_CENTER, IUP_CENTER); 

  filename = IupGetAttribute(getfile, IUP_VALUE);
  return filename;
}


/* - Callback de mudanca de tamanho no canvas (mesma para ambos os canvas) */
int resize_cb(Ihandle *self, int width, int height)
{
 IupGLMakeCurrent(self);  /* torna o foco do OpenGL para este canvas */

 /* define a area do canvas que deve receber as primitivas do OpenGL */
 glViewport(0,0,width,height);

 glClearColor(1.f, 1.f, 1.0f, 1.0f);
 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

 cam.width =(double)width;
 cam.height=(double)height;

 return IUP_DEFAULT;
}



int repaint_cb(Ihandle *self )
{
   IupGLMakeCurrent(self);
   glClearColor(1.f, 1.f, 1.0f, 1.0f);
   glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

   glPointSize(3.f);
   glBegin(GL_POINTS);
     glColor3f(0.6f,0.6f,0.6f);
     glVertex3d(cam.atx,cam.aty,cam.atz);
   glEnd();
   arcballDraw(arcball,(int)cam.width,(int)cam.height);


   if (color_cube )
         ccDraw();
      else
         if (scene_loaded) scnDraw(flag_draw_axis,cam.atx,cam.aty,cam.atz,cam.radius);

   glFlush();

   return IUP_DEFAULT;
}



/* captura um nome para um arquivo */
static char * get_new_file_name( void )
{
  Ihandle* getfile = IupFileDlg();
  char* filename = NULL;

  IupSetAttribute(getfile, IUP_TITLE, "Salva arquivo"  );
  IupSetAttribute(getfile, IUP_DIALOGTYPE, IUP_SAVE);
  IupSetAttribute(getfile, IUP_FILTER, "*.bmp");
  IupSetAttribute(getfile, IUP_FILTERINFO, "Arquivo de imagem (*.bmp)");
  IupPopup(getfile, IUP_CENTER, IUP_CENTER);  /* o posicionamento nao esta sendo respeitado no Windows */

  filename = IupGetAttribute(getfile, IUP_VALUE);
  return filename;
}


/* define a transformacao de ModelView */
static setCamera(double xc, double yc, double zc, double radius)
{
   Vector xe,ye,ze;

  cam.atx=xc; 
  cam.aty=yc;
  cam.atz=zc;

  cam.radius=radius;

  cam.eyex = cam.atx ;
  cam.eyey = cam.aty;
  cam.eyez = cam.atz + 2*radius;

  cam.upx=0.;
  cam.upy=1.;
  cam.upz=0.;

  cam.zNear = 0.8*radius;
  cam.zFar  = 10*radius;

  cam.fovy=60.;

  cam.top    =  cam.zNear*tan(cam.fovy*3.1416/180);
  cam.bottom = -cam.top;
  cam.right  =  cam.width*cam.top/cam.height;
  cam.left   = -cam.right;
  
  /* determina a posicao dos eixos da camera para tornar este trecho mais geral */
  ze=algUnit(algVector(cam.eyex-cam.atx,cam.eyey-cam.aty,cam.eyez-cam.atz,1.));
  xe=algUnit(algCross(algVector(cam.upx,cam.upy,cam.upz,1.),ze));
  ye=algUnit(algCross(ze,xe));
  arcball = arcballInit(300,300,300,xe,ye,ze);

  IupGLMakeCurrent(canvas);  /* torna o foco do OpenGL para este canvas */

   /* define a area do canvas que deve receber as primitivas do OpenGL */
   glViewport(0,0,(int)cam.width,(int)cam.height);

   /* transformacao de instanciacao dos objetos no sistema de coordenadas da camera */
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();           /* identidade,  ou seja nada */
   gluLookAt(cam.eyex,cam.eyey,cam.eyez,cam.atx,cam.aty,cam.atz,cam.upx,cam.upy,cam.upz);
   glPushMatrix();
 
  /* transformacao de projecao */
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  if (flag_ortho)
     //glOrtho(cam.left,cam.right,cam.bottom,cam.top,cam.zNear,cam.zFar);
     glOrtho(-cam.radius,cam.radius,-cam.radius,cam.radius,cam.zNear,cam.zFar);
  else
     gluPerspective(cam.fovy,cam.width/cam.height,cam.zNear,cam.zFar);
  
  repaint_cb(canvas);

  IupSetFocus(canvas);
  return IUP_DEFAULT;
}


/* carrega uma nova cena */
int load_cb(void) {
  char* filename = get_file_name();  /* chama o dialogo de abertura de arquivo */
  double xc=0,yc=0,zc=0,radius=sqrt(3.);
  
  if (filename==NULL) return IUP_DEFAULT;

  scnLoad(filename, &xc,&yc,&zc,&radius);


  color_cube=0;
  scene_loaded=1;
  setCamera(xc,yc,zc,radius);
  IupSetfAttribute(label, "TITLE", "%s ", strrchr(filename,'\\')+1);
  return IUP_DEFAULT;
}



/* Callback que trata de eventos de teclado */
int keypress_cb( Ihandle *self, int c, int press ) 
{
	/* se está recebendo evento de soltar a tecla, retorna */
	if( press == 0 )
		return IUP_DEFAULT;

	switch( c )
	{

     case K_c:
         color_cube=(color_cube==0)?1:0;
         if (color_cube) 
            setCamera(0.,0.,0.,1.73);
         break;

      case K_SP:
         glMatrixMode(GL_MODELVIEW);
         glPopMatrix();
         glPushMatrix();

         Rot=algMatrixIdent();
         setCamera(cam.atx,cam.aty,cam.atz,cam.radius);
         arcballInit(300,300,300,algVector(1,0,0,1),algVector(0,1,0,1),algVector(0,0,1,1));
         break;

		case K_ESC:
         return IUP_CLOSE;
	}

   repaint_cb(canvas);
	return IUP_DEFAULT;
}

/* captura o evento de motion quando o botao da esquerda do mouse estiver precionado */
int motion_cb(Ihandle *self, int x, int y, char *r)
{
   int new_x=x;
   int new_y=(int)cam.height-y;
   if (new_x!=old_x||new_y!=old_y) {
       Quat q=arcballRotQuat(arcball, old_x, old_y, new_x, new_y);
       Rot=algQuatToMatrix(q);
     
       glMatrixMode(GL_MODELVIEW);
       glTranslated(cam.atx,cam.aty,cam.atz);
       glMultMatrixd(algGetOGLMatrix(&Rot));
       glTranslated(-cam.atx,-cam.aty,-cam.atz);

       old_x = new_x;
       old_y = new_y;
       }
   repaint_cb(canvas);
  return IUP_DEFAULT;
}


/* trata o evento do mouse, ligando e desligando a callback de motion */
int mouse_cb(Ihandle *self, int b, int e, int x, int y, char *r)
{
 
   if (b==IUP_BUTTON1)
   {
      if (e==1) 
       IupSetFunction("motion_cb", (Icallback)motion_cb);
      else
       IupSetFunction("motion_cb", (Icallback)NULL);
   }
   old_x = x;
   old_y = (int)cam.height-y;

  return IUP_DEFAULT;
}

int ortho_cb(Ihandle *self, int v)
{
    if (v==1) {
        flag_ortho=1;
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(-cam.radius,cam.radius,-cam.radius,cam.radius,cam.zNear,cam.zFar);
    } else {
        flag_ortho=0;
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluPerspective(cam.fovy,cam.width/cam.height,cam.zNear,cam.zFar);
    }
    repaint_cb(canvas);
    return IUP_DEFAULT;
}

int axis_cb(Ihandle *self, int v)
{
    if (v==1) 
        flag_draw_axis=1;
    else
        flag_draw_axis=0;

    repaint_cb(canvas);
    return IUP_DEFAULT;
}

/* inicializa o programa */
int init(void)
{
  Ihandle *dialog, *statusbar,  *box;

  Ihandle *toolbar, *load,*proj, *axis;

  /* cria a barra de ferramentas e seus botoes */
  load = IupButton("", "load_cb");
  IupSetAttribute(load,"TIP","Carrega uma imagem.");
  IupSetAttribute(load,"IMAGE","IUP_IMGBUT_OPEN");
  IupSetFunction("load_cb", (Icallback)load_cb);

  /* cria os botoes de toglle que controlam a visualizacao */
  proj=IupToggle("Ortho/Persp","ortho_cb");
  axis=IupToggle("Axis on/off","axis_cb");

  IupSetFunction("ortho_cb", (Icallback)ortho_cb);
  IupSetFunction("axis_cb", (Icallback)axis_cb);

  toolbar = IupHbox( load,  IupFill(), proj, axis, IupFill(), NULL);
  IupSetAttribute(toolbar, "ALIGNMENT", "ACENTER");
 

  /* cria um canvas */
  canvas = IupGLCanvas("repaint_cb"); 
  IupSetAttribute(canvas,IUP_RASTERSIZE,"600x600");
  IupSetAttribute(canvas, "RESIZE_CB", "resize_cb");
  IupSetAttribute(canvas, IUP_KEYPRESS_CB, "keyPress" ); 
  IupSetAttribute(canvas, IUP_BUTTON_CB, "mouse_cb");
  IupSetAttribute(canvas, IUP_MOTION_CB, "motion_cb");

  IupSetFunction( "keyPress", (Icallback) keypress_cb );


  /* associa eventos a callbacks */
  IupSetFunction("repaint_cb", (Icallback) repaint_cb);
  IupSetFunction("resize_cb", (Icallback) resize_cb);
  IupSetFunction("mouse_cb", (Icallback) mouse_cb);
  IupSetFunction (IUP_IDLE_ACTION, (Icallback) NULL);


  /* the status bar is just a label to put some usefull information in run time */
  label = IupLabel("status");
  IupSetAttribute(label, "EXPAND", "HORIZONTAL");
  IupSetAttribute(label, "FONT", "COURIER_NORMAL_10");
  statusbar = IupSetAttributes(IupHbox(
                IupFrame(IupHbox(label, NULL)), 
                NULL), "MARGIN=5x5");

  /* this is the most external box that puts together
     the toolbar, the two canvas and the status bar */
  box = IupVbox(
          toolbar,
          canvas, 
          statusbar, 
          NULL);

  /* create the dialog and set its attributes */
  dialog = IupDialog(box);
  IupSetAttribute(dialog, "CLOSE_CB", "app_exit_cb");
  IupSetAttribute(dialog, "TITLE", "Arc Ball");

  arcball = arcballInit(300,300,300,algVector(1,0,0,1),algVector(0,1,0,1),algVector(0,0,1,1));
  Rot=algMatrixIdent( );


  IupShowXY(dialog, IUP_CENTER, IUP_CENTER);

  setCamera(0.,0.,0.,1.73); /* camera para o colorCube */
  ccInit( );                /* inicializa o colorCube */

  repaint_cb(canvas);
  return 1;
}

/* Funcao principal */
void main(void) {
    IupOpen();
    IupGLCanvasOpen();
    IupImageLibOpen(); 
    
    if ( init() )
		IupMainLoop();
    
    IupImageLibClose(); 
    IupClose();
}