//El formato del fichero de entrada se especifica al finalizar el c¢digo

/*
El siguiente programa coge de un fichero de datos una colecci¢n de puntos
y l¡neas que (supuestamente) representan una figura geom‚trica. La muestra
por pantalla y permite visualizar rotaciones de ‚sta que se efect£an mediante
las teclas A, Z (rot. alrededor del eje x), FLECHA ARRIBA, FLECHA ABAJO
(eje y) y FLECHA IZQDA, FLECHA DCHA (eje z).
*/

#include <conio.h>
#include <graphics.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define SQ 0.70710678118

#define NUMARG 2 // N£mero de argumentos

typedef float vector2[2];
typedef float vector3[3];

vector2 d_center;             // Coordenadas de los p¡xeles donde se encuentra el (0,0,0)
vector3 d_scale= {10,10,10};  // Escala (p¡xeles por unidad de longitud)
int	d_radius=2;	      // Radio de los puntos
int centerofmass = 0;	      // Opci¢n centro de masas. Desactivada

float *puntos; //Puntero al conjunto de puntos
int *lineas;
int numpuntos=0; //N£mero de puntos
int numlineas=0;

void vec3to2(vector2 vec2, vector3 vec3);
void paint(float x, float y, float z, int radius);
void line3 (float x1,float x2,float x3, float y1,float y2,float y3);
void rotaX (vector3 dest, vector3 source, float angle);
void rotaY (vector3 dest, vector3 source, float angle);
void rotaZ (vector3 dest, vector3 source, float angle);

void lee_params (FILE *input);
int  leefichero (char *nombrefichero);
void cargapuntos (void);
void juega(void);

void inicio_graficos(void);
void fin(void);
void errorargs(void);

// ********************************************
// COMIENZO DEL PROGRAMA PRINCIPAL Y SUBRUTINAS
// ********************************************

void main(int argc, char **argv)
{
  if (argc==NUMARG)
    {
	if (leefichero(argv[1]))
	{
	  inicio_graficos();
	  cargapuntos();
	  juega();
	  fin();
	}
     }
     else
       errorargs();
}

void vec3to2(vector2 vec2, vector3 vec3)
{
  vec2[0]=d_center[0]+d_scale[1]*vec3[1]-d_scale[0]*SQ*vec3[0];
  vec2[1]=d_center[1]-d_scale[2]*vec3[2]+d_scale[0]*SQ*vec3[0];
}

void paint(float x, float y, float z, int radius)
{
  vector2 aux2;
  vector3 aux3;
  aux3[0]=x;
  aux3[1]=y;
  aux3[2]=z;
  vec3to2 (aux2, aux3);
  pieslice (aux2[0], aux2[1], 0, 360, radius);
}

void line3 (float x1,float x2,float x3, float y1,float y2,float
y3)
{
  vector3 aux31, aux32;
  vector2 aux21, aux22;
  aux31[0]=x1;
  aux31[1]=x2;
  aux31[2]=x3;
  aux32[0]=y1;
  aux32[1]=y2;
  aux32[2]=y3;
  vec3to2(aux21, aux31);
  vec3to2(aux22, aux32);
  line(aux21[0], aux21[1], aux22[0], aux22[1]);
}

void rotaX (vector3 dest, vector3 source, float angle)
{
  dest [0] =  source[0];
  dest [1] =  cos (angle) * source[1] + sin (angle) * source[2];
  dest [2] = -sin (angle) * source[1] + cos (angle) * source[2];
}

void rotaY (vector3 dest, vector3 source, float angle)
{
  dest[0] =  cos (angle) * source[0] + sin (angle) * source[2];
  dest[1] =  source[1];
  dest[2] = -sin (angle) * source[0] + cos (angle) * source[2];
}


void rotaZ (vector3 dest, vector3 source, float angle)
{
  dest[0] =  cos (angle) * source[0] + sin (angle) * source[1];
  dest[1] = -sin (angle) * source[0] + cos (angle) * source[1];
  dest[2] =  source[2];
}

void lee_params (FILE *input)
{
   char parametro[20]="";

   while ((strcmp (parametro, "puntos")!=0)&&(!feof(input)))
     {
	fscanf (input, "%s", &parametro);

	//RADIO
	if (!strcmp (parametro, "radio"))
	  {
	     fscanf (input, "%d", &d_radius);
	  }

	 //ESCALA
	if (!strcmp (parametro, "escala"))
	  {
	     fscanf (input, "%f %f %f", &d_scale[0], &d_scale[1], &d_scale[2]);
	  }

	  //CENTRO DE LA PANTALLA
	if (!strcmp (parametro, "centro"))
	  {
	     fscanf (input, "%f %f", &d_center[0], &d_center[1]);
	  }
	if (!strcmp (parametro, "cdm"))
	  {
	    centerofmass = 1;
	  }
    }//while
     // Si se ha llegado al final del fichero sin haber encontrado la
     // palabra "puntos", dar mesaje de error y salir del programa
     if feof(input)
     {
       printf ("El formato del archivo no es adecuado\n");
       exit (1);
     }
}

// Calcula el centro de masas de la distribuci¢n de puntos
// masa del punto = 1
void calcula_cdm (vector3 cdm)
{
  int i,j;
  cdm[0]=0.;  cdm[1]=0.;  cdm[2]=0.;    // Init!

  for (i=0; i<numpuntos;i++)
    for (j=0; j<3; j++)
	cdm[j]=cdm[j]+puntos[3*i+j];
  for (j=0; j<3; j++)
    cdm[j] /= numpuntos;
}

// Recalcula las coordenadas de los puntos tomando como or¡gen el vecton dado
void shift_origin (vector3 cdm)
{
  int i, j;
  for (i=0; i<numpuntos;i++)
    for (j=0; j<3; j++)
      puntos [3*i+j] = puntos [3*i+j] - cdm[j];
}

// Retorna 1 si ha le¡do con ‚xito y 0 si no
int leefichero (char *nombrefichero)
{
  FILE *archivo;
  char comando[12];
  int indexpunto=0;
  //auxiliares
  int i;
  int aux;
  float auxd;
  vector3 cdm;
  int result = 1;

  archivo = fopen (nombrefichero, "r");
  fscanf (archivo, "%d", &i);
  if (i==8314) //formato de archivo correcto
    {
      //Leer par metros (escala, centro, radio, etc)
      lee_params(archivo);

      //Leer n£mero de puntos
      fscanf (archivo, "%d", &numpuntos);

      //Asignar memoria para puntos y l¡neas
      puntos = (float *) calloc (numpuntos*3, sizeof(float)); //tres coordenadas x punto

      //Leer puntos
      for (indexpunto=0;indexpunto<numpuntos*3;indexpunto++)
	{
	  fscanf (archivo, "%f", &auxd);
	  *(puntos+indexpunto)=auxd;
	}
      // Si la opci¢n centro de masas est  activada, calcular
      // el cetro de masas y referir a ‚ste las coordenadas de todos
      // los puntos
      if (centerofmass)
	{
	  calcula_cdm (cdm);
	  shift_origin (cdm);
	}
      //Leer lineas
      fscanf (archivo, "%d", &numlineas);
      lineas = (int *) calloc (numlineas*2, sizeof(int));
      for (indexpunto=0; indexpunto < numlineas*2; indexpunto++)
	{
	  fscanf (archivo, "%d", &aux);
	  *(lineas+indexpunto)=aux;
	}
    }  //if i=8314
  else
   {
    printf ("Error al abrir el archivo\n");
    result = 0;
   }
  fclose(archivo);
  return result;
}

void cargapuntos()
{
 int i,aux=0,aux2=0;
   float x[3];
   for (i=0; i<numpuntos*3; i++)
     {
	x[aux]= *(puntos+i);
	aux++;
	if (aux==3)
	  {
	    aux = 0;
	    paint (x[0],x[1],x[2],d_radius);
	  }
     }
     for (i=0;i<numlineas;i++)
    {
      aux  = *(lineas+2*i)-1;
      aux2 = *(lineas+2*i+1)-1;
	    line3(
	    *(puntos+3*aux),
	    *(puntos+3*aux+1),
	    *(puntos+3*aux+2),
	    *(puntos+3*aux2),
	    *(puntos+3*aux2+1),
	    *(puntos+3*aux2+2)
	    );
    }
}

// Mira si la tecla pulsada est  permitida. Devuelve 1 si si y 0 si no
int is_allowed (const char *allowed, int numelems, char current)
{
  int result = 0;
  int i;

  for (i=0; i<numelems; i++)
    if (allowed[i]==current)
      {
	result = 1;
        break;
      }
  return result;
}

void juega()
{
  float *puntosmoviles;
  int j=0;
  char tecla;
  const char allowed[6] = {97,122,80,72,75,77};
  vector3 dest, source;

  //Volcar el contenido de puntos en puntosmoviles

      //Comienza el show
      do //while tecla != 27
	{
	  tecla = getch();
	  if (tecla==0) tecla = getch();
	  cleardevice();
	  if (is_allowed(allowed, 6, tecla))
	  {
	  for (j=0; j<numpuntos; j++)
	    {
	      source[0] = *(puntos+3*j);
	      source[1] = *(puntos+3*j+1);
	      source[2] = *(puntos+3*j+2);
	      switch (tecla)
		{
		  case  97 : rotaX (dest, source,  0.15); break;
		  case 122 : rotaX (dest, source, -0.15); break;
		  case  80 : rotaY (dest, source,  0.15); break;
		  case  72 : rotaY (dest, source, -0.15); break;
		  case  75 : rotaZ (dest, source,  0.15); break;
		  case  77 : rotaZ (dest, source, -0.15); break;
		}//switch
	      *(puntos+3*j)   = dest [0];
	      *(puntos+3*j+1) = dest [1];
	      *(puntos+3*j+2) = dest [2];
	    } //for j;
	    }// if is_allowed
	    cargapuntos();
	} while (tecla != 27);

}


void inicio_graficos()
{
  int gm=DETECT, gd;
  initgraph (&gm, &gd, "C:\\CPP\\BGI");
  setfillstyle (SOLID_FILL, WHITE);
  d_center[0] = getmaxx()/2;
  d_center[1] = getmaxy()/2;
}

void fin()
{
  closegraph();
  free (puntos);
  free (lineas);
}

void errorargs()
{
  printf ("\nError: Escribe: vector [fichero.pun]\n");
}

/*FORMATO DEL FICHERO DE ENTRADA
Es un fichero de texto.
Los par metros entre par‚ntesis son opcionales, y pueden colocarse en el
orden deseado, siempre que est‚n al principio del fichero. (Las palabras
"centro", "escala", etc tienen que escribirse (en min£sculas). Los datos entre
corchetes representan NéMEROS. [x1], [x1], [z1] son las 3 coordenadas del
punto 1. [l11] y [l12] son los puntos l¡mites de la l¡nea 1. Por ejemplo,
si definimos una l¡nea como
2 4
lo que haremos ser  dibujar una l¡nea entre el punto definido en 2§ lugar
y el punto definido en 4§ lugar.

(centro  [x], [y])
(escala [x] [y] [z])
(radio [r])
[N£mero de puntos]
[x1] [y1] [z1]
[x2] [y2] [z2]
...
[xn] [yn] [zn]
[N£mero de l¡neas]
[l11] [l12]
[l21] [l22]
...
[ln1] [ln2]
*/

/*
COMENTARIOS SOBRE C++

Tu puedes hacer
  char *comando;
  scanf ("%s", comando)
(que hasta aqu¡ estar¡a bien)
pero si vuelves a leer sobre comando a saco con scanf te da error de
windiws
*/
