/**************************************************************************
  Copyright (C) 1992 Guy Moreillon
  All rights reserved.

  This software may be freely copied, modified, and redistributed
  provided that this copyright notice is preserved on all copies.

  You may not distribute this software, in whole or in part, as part of
  any commercial product without the express consent of the authors.

  There is no warranty or other guarantee of fitness of this software
  for any purpose.  It is provided solely "as is".
**************************************************************************/
/**************************************************************************

			     PREPRAD
		     (Scene preparation program)
		    	    Version 1.0
			       1992		    	    
		    	    
		  
		    	  Guy Moreillon
		    	  
**************************************************************************/

/**************************************************************************
  Fichier	: popup.c
  Description	: Routines d'affichage des fenetres popup
**************************************************************************/

#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <gl.h>
#include <types.h>
#include <macros.h>
#include <load_image.h>
#include <soft_zoom.h>
#include <misc.h>
#include "prtype.h"
#include "preprad.h"
#include "forms.h"
#include "panels.h"
#include "picture.h"
#include "popup.h"

extern	SCENE_O	the_scene;

#define	EDIT_PAN_LABEL	"Edit Panel"
#define TEX_PAN_LABEL	"Texture Panel"

MAT	mat;
STR	tex_brow_path;

void set_material(MATP material)
/**************************************************************************
  But 	: Defini le materiau courant
  Entree: material  : le materiau
  Sortie: neant
**************************************************************************/
{
  mat = *material;
}

int add_find_tex(TEXTUREP tex, SCENE_OP scene)
/**************************************************************************
  But 	: Cherche une texture dans le tableau des textures deja chargee
	  et l'ajoute s'il ne la trouve pas
  Entree: tex	    : la texture
	  scene	    : la scene
  Sortie: neant
**************************************************************************/
{
  int i;

  for(i = 1; i < scene->state.nb_tex; i++)
    if (strcmp(get_short_name(tex->name),
	       get_short_name(scene->textures[i].name)) == 0)
      return i;

  texdef2d(scene->state.nb_tex,
	   4,
	   tex->width,
	   tex->height,
	   tex->image,
	   7,
	   texprops);

  scene->textures[scene->state.nb_tex++] = *tex;
  assert(scene->state.nb_tex <= MAX_TEX);

  return (scene->state.nb_tex - 1);
}

void apply_modif(SCENE_OP scene)
/**************************************************************************
  But 	: Applique une modification des caracteristiques a l'objet courant
  Entree: scene	    : la scene
  Sortie: neant
**************************************************************************/
{
  V_copy(mat.object->object.pure.color, mat.color);
  mat.object->object.pure.ro		= mat.ro;
  mat.object->object.pure.E		= mat.E;
  mat.object->object.pure.shadows	= mat.shadows;
  strcpy(mat.object->name, mat.name);
  if (mat.tex.texture.image != NULL)
    {
      mat.object->object.pure.texmap.index =
	add_find_tex(&(mat.tex.texture), scene);
      mat.object->object.pure.texmap.scale = mat.tex.map.scale;
      V_copy(mat.object->object.pure.texmap.s, mat.tex.map.s);
      V_copy(mat.object->object.pure.texmap.t, mat.tex.map.t);
    }
  else
    mat.object->object.pure.texmap.index = -1;
}

BOOLEAN r_unique(char *name, OBJECT_OP me, OBJECT_OP object)
/**************************************************************************
  But 	: Determine si un nom est unique parmi l'objet passe en param.
  Entree: name	    : le nom
	  me	    : l'object courant (a modifier)
	  object    : l'objet
  Sortie: TRUE si le nom existe deja, FALSE sinon
**************************************************************************/
{
  OBJECT_OP obj;

  if (me == object)
    return TRUE;

  switch(object->type) {
    case assembly_type:
      for(obj = object->object.assembly.sons; obj != NULL; obj = obj->next)
	if (!r_unique(name, me, obj))
	  return FALSE;
      break;
    case composite_type:
      for(obj = object->object.composite.sons; obj != NULL; obj = obj->next)
	if (!r_unique(name, me, obj))
	  return FALSE;
      break;
    case pure_type:
      if (strcmp(name, object->name) == 0)
	return FALSE;
      break;
    default:
      break;
  }

  return TRUE;
}

BOOLEAN unique_name(char *name, OBJECT_OP me, SCENE_OP scene)
/**************************************************************************
  But 	: Determine si un nom est unique parmi les objets pures
  Entree: name	    : le nom
	  me	    : l'object courant
	  scene	    : la scene
  Sortie: TRUE si le nom existe deja, FALSE sinon
**************************************************************************/
{
  return r_unique(name, me, scene->scene);
}

void update_edi_pan(MATP mat)
/**************************************************************************
  But 	: Met a jour la fenetre d'edition d'objet
  Entree: mat	    : le materiau courant
  Sortie: neant
**************************************************************************/
{
  STR	str;

  sprintf(str, "%f", mat->ro);
  fl_set_input(Edit_Reflectivity_Input, str);
  sprintf(str, "%f", mat->E);
  fl_set_input(Edit_Emissivity_Input, str);
  fl_mapcolor(Color_Box->col1,
	      (short)(mat->color[0]*255),
	      (short)(mat->color[1]*255),
	      (short)(mat->color[2]*255));
  fl_redraw_object(Color_Box);
  fl_set_slider_value(Red_Slider,   mat->color[0]);
  fl_set_slider_value(Green_Slider, mat->color[1]);
  fl_set_slider_value(Blue_Slider,  mat->color[2]);
  fl_set_input(Edit_Object_Name_Input, mat->name);
  fl_set_button(Edit_Shadows_Button, mat->shadows);
}

void turn_on_edi()
/**************************************************************************
  But 	: Affiche la fenetre d'edition et cache celle des textures
  Entree: neant
  Sortie: neant
**************************************************************************/
{
  if (Texture_Panel->visible)
    fl_hide_form(Texture_Panel);

  update_edi_pan(&mat);
  if (!Edit_Panel->visible)
    fl_show_form(Edit_Panel, FL_PLACE_POSITION, TRUE, EDIT_PAN_LABEL);
}

int sel_rgb(struct dirent *de)
/**************************************************************************
  But 	: Selectionne les noms se terminant par .rgb
  Entree: de	: entree de repertoire
  Sortie: neant
**************************************************************************/
{
  char *ext;

  ext = strrchr(de->d_name, '.');
  if (ext == NULL)
    return FALSE;

  if (strcmp(ext, ".rgb") != 0)
    return FALSE;

  return TRUE;
}

void update_tex_brow()
/**************************************************************************
  But 	: Met a jour le browser de texture
  Entree: neant
  Sortie: neant
**************************************************************************/
{
  int		i, nb_entry;
  struct dirent **namelist;

  fl_clear_browser(Texture_Name_Browser);

  nb_entry = scandir(tex_brow_path, &namelist, sel_rgb, alphasort);

  if (nb_entry < 0)
    {
      fl_show_message("Can't open", tex_brow_path, "for reading...");
      fl_qreset();
      return;
    }

  fl_freeze_object(Texture_Name_Browser);
  for(i = 0; i < nb_entry; i++)
    {
      char  *ext;
      STR   tmp;

      strcpy(tmp, namelist[i]->d_name);
      ext = strrchr(tmp, '.');
      ext[0] = '\0';
      fl_add_browser_line(Texture_Name_Browser, tmp);
      free(namelist[i]);
    }
  free(namelist);
  fl_unfreeze_object(Texture_Name_Browser);
}

void update_tex_pan(MATP mat)
/**************************************************************************
  But 	: Met a jour la fenetre de selection des textures
  Entree: mat	    : le materiau courant
  Sortie: neant
**************************************************************************/
{
  STR str;

  sprintf(str, "%f", mat->ro);
  fl_set_input(Tex_Reflectivity_Input, str);
  sprintf(str, "%f", mat->E);
  fl_set_input(Tex_Emissivity_Input, str);
  sprintf(str, "%f", 1.0/mat->tex.map.scale);
  fl_set_input(Scale_Input, str);
  fl_set_input(Tex_Object_Name_Input, mat->name);
  fl_set_button(Tex_Shadows_Button, mat->shadows);

  update_tex_brow();

  set_picture(mat->tex.texture.image,
	      mat->tex.texture.width,
	      mat->tex.texture.height);
  fl_redraw_object(Texture_Pict);
}

void turn_on_tex()
/**************************************************************************
  But 	: Affiche la fenetre des textures et cache celle d'edition
  Entree: neant
  Sortie: neant
**************************************************************************/
{
  if (Edit_Panel->visible)
    fl_hide_form(Edit_Panel);

  update_tex_pan(&mat);
  if (!Texture_Panel->visible)
    fl_show_form(Texture_Panel, FL_PLACE_POSITION, TRUE, TEX_PAN_LABEL);
}

void edit_pan_cb(FL_OBJECT *fl_obj, SCENE_OP scene)
/**************************************************************************
  But 	: Traite les evenements generes par la fenetre d'edition
  Entree: fl_obj    : l'objet qui a emis l'evenement
	  scene	    : la scene
  Sortie: neant
**************************************************************************/
{
  if (fl_obj == Red_Slider)
    mat.color[0] = fl_get_slider_value(Red_Slider);
  else if (fl_obj == Green_Slider)
    mat.color[1] = fl_get_slider_value(Green_Slider);
  else if (fl_obj == Blue_Slider)
    mat.color[2] = fl_get_slider_value(Blue_Slider);
  else if (fl_obj == Edit_Reflectivity_Input)
    {
      sscanf(fl_get_input(Edit_Reflectivity_Input),
	     "%f", &(mat.ro));
      mat.ro = MIN(1.0, mat.ro);
      mat.ro = MAX(0.0, mat.ro);
    }
  else if (fl_obj == Edit_Emissivity_Input)
    {
      sscanf(fl_get_input(Edit_Emissivity_Input),
	     "%f", &(mat.E));
      mat.E = MAX(0.0, mat.E);
    }
  else if (fl_obj == Edit_Object_Name_Input)
    {
      STR   name;

      strcpy(name, fl_get_input(Edit_Object_Name_Input));
      if (unique_name(name, mat.object, &the_scene))
	strcpy(mat.name, name);
      else
	{
	  fl_show_message("This name is not unique :", name,
			  "Please choose another one");
	  fl_set_input(Edit_Object_Name_Input, mat.name);
	}
    }
  else if (fl_obj == Edit_Shadows_Button)
    mat.shadows = fl_get_button(Edit_Shadows_Button);
  else if (fl_obj == Edit_Ok_Button)
    mat.tex.texture.image = NULL;
  else if (fl_obj == Add_Texture_Button)
    {
      mat.tex.map.index = -1;
      mat.tex.texture.image = NULL;
      turn_on_tex();
    }

  if ((fl_obj == Red_Slider) ||
      (fl_obj == Green_Slider) ||
      (fl_obj == Blue_Slider))
    {	
      fl_mapcolor(Color_Box->col1,
		  (short)(mat.color[0]*255),
		  (short)(mat.color[1]*255),
		  (short)(mat.color[2]*255));

      fl_redraw_object(Color_Box);
    }
}

void tex_pan_cb(FL_OBJECT *fl_obj, SCENE_OP scene)
/**************************************************************************
  But 	: Traite les evenements generes par la fenetre des textures
  Entree: fl_obj    : l'objet qui a emis l'evenement
	  scene	    : la scene
  Sortie: neant
**************************************************************************/
{
  static unsigned long	*image = NULL;

  int i;

  if (fl_obj == Texture_Name_Browser)
    {
      STR   tmp;

      strcpy(tmp, tex_brow_path);
      strcat(tmp, "/");
      strcat(tmp, fl_get_browser_line(Texture_Name_Browser,
				      fl_get_browser(Texture_Name_Browser)));
      strcat(tmp, ".rgb");
      strcpy(mat.tex.texture.name, tmp);

      for(i = 1; i < scene->state.nb_tex; i++)
	if (strcmp(get_short_name(mat.tex.texture.name),
		   get_short_name(scene->textures[i].name)) == 0)
	  image = scene->textures[i].image;

      if (i >= scene->state.nb_tex)
	image = load_image(mat.tex.texture.name,
			   &(mat.tex.texture.width),
			   &(mat.tex.texture.height),
			   mat.tex.texture.meancolor);

      if (image)
	set_picture(image,
		    mat.tex.texture.width,
		    mat.tex.texture.height);

      fl_redraw_object(Texture_Pict);
    }
  else if (fl_obj == Scale_Input)
    {
      sscanf(fl_get_input(Scale_Input), "%f", &(mat.tex.map.scale));
      mat.tex.map.scale = MAX(0.0001, 1.0/mat.tex.map.scale);	
    }
  else if (fl_obj == Tex_Reflectivity_Input)
    {
      sscanf(fl_get_input(Tex_Reflectivity_Input),
	     "%f", &(mat.ro));
      mat.ro = MIN(1.0, mat.ro);
      mat.ro = MAX(0.0, mat.ro);
    }
  else if (fl_obj == Tex_Emissivity_Input)
    {
      sscanf(fl_get_input(Tex_Emissivity_Input),
	     "%f", &(mat.E));
      mat.E = MAX(0.0, mat.E);
    }
  else if (fl_obj == Tex_Object_Name_Input)
    {
      STR   name;

      strcpy(name, fl_get_input(Tex_Object_Name_Input));
      if (unique_name(name, mat.object, &the_scene))
	strcpy(mat.name, name);
      else
	{
	  fl_show_message("This name is not unique :", name,
			  "Please choose another one");
	  fl_set_input(Tex_Object_Name_Input, mat.name);
	}
    }
  else if (fl_obj == Tex_Shadows_Button)
    mat.shadows = fl_get_button(Tex_Shadows_Button);
  else if (fl_obj == Tex_Ok_Button)
    {
      VECTOR dum, v;

      if (image != NULL)
	mat.tex.texture.image = image;
      if (mat.tex.texture.image != NULL)
	V_copy(mat.color, mat.tex.texture.meancolor);

      V_init(dum, 1.0, 0.0, 0.0);
      M4D_transform_point(v, dum, scene->state.visu.iviewing);
      V_sub(v, v, scene->state.visu.iviewing[3]);
      V_normalize(mat.tex.map.s, v);
      mat.tex.map.s[3] = -V_dot(mat.tex.map.s,
				mat.object->bbox[0]);
      V_init(dum, 0.0, 1.0, 0.0);
      M4D_transform_point(v, dum, scene->state.visu.iviewing);
      V_sub(v, v, scene->state.visu.iviewing[3]);
      V_normalize(mat.tex.map.t, v);
      mat.tex.map.t[3] = -V_dot(mat.tex.map.t,
				mat.object->bbox[0]);
    }
  else if (fl_obj == No_Texture_Button)
    {
      set_picture(NULL, 0, 0);
      turn_on_edi();
    }
}

void start_popup(SCENE_OP scene)
/**************************************************************************
  But 	: Ouvre la fenetre de popup necessaire
  Entree: scene	    : la scene
  Sortie: neant
**************************************************************************/
{
  fl_deactivate_all_forms();

  if (mat.tex.texture.image == NULL)
    turn_on_edi();
  else
    turn_on_tex();
}

void end_popup()
/**************************************************************************
  But 	: Ferme la fenetre de popup courante
  Entree: neant
  Sortie: neant
**************************************************************************/
{
  if (Edit_Panel->visible)
    fl_hide_form(Edit_Panel);
  if (Texture_Panel->visible)
    fl_hide_form(Texture_Panel);
  fl_activate_all_forms();
  fl_qreset();
}

BOOLEAN popup_caract(MATP mat, SCENE_OP scene)
/**************************************************************************
  But 	: Gere la fenetre de popup d'edition des caracteristiques
  Entree: mat	    : le materiau a afficher
	  scene	    : la scene
  Sortie: neant
**************************************************************************/
{
  FL_OBJECT *fl_obj;

  set_material(mat);

  start_popup(scene);

  do {
    fl_obj = fl_do_only_forms();
    fl_qreset();

    if (fl_obj->form == Edit_Panel)
      edit_pan_cb(fl_obj, scene);
    else if (fl_obj->form == Texture_Panel)
      tex_pan_cb(fl_obj, scene);
  } while ((fl_obj != Edit_Ok_Button) &&
	   (fl_obj != Edit_Cancel_Button) &&
	   (fl_obj != Tex_Ok_Button) &&
	   (fl_obj != Tex_Cancel_Button));

  end_popup();

  if ((fl_obj == Edit_Ok_Button) ||
      (fl_obj == Tex_Ok_Button))
    return TRUE;
  else
    return FALSE;
}
