/**************************************************************************
  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".
**************************************************************************/
/**************************************************************************

				RAD
		       (Interactive Radiosity)
		    	    Version 1.0
			       1992		    	    
		    	    
		  
		    	  Guy Moreillon
		    	  
**************************************************************************/

/**************************************************************************
  Fichier	: ioload.c
  Description	: Fonctions utilisees pour la lecture de donnees sur fichier
**************************************************************************/

#include <stdio.h>
#include <math.h>
#include <assert.h>
#include <malloc.h>
#include "types.h"
#include "macros.h"
#include "parse.h"
#include "rad.h"
#include "rinit.h"
#include "ray.h"
#include "misc.h"
#include "io.h"
#include "load_image.h"
#include "ioload.h"
#include "free.h"

OBJECT_OP search_for_num(int num, OBJECT_OP object)
/**************************************************************************
  But	: Cherche le pure de numero num dans object et rend son adresse si
	  il le trouve, et NULL sinon, avec le numero du pure visite en
	  dernier
  Entree: num	    : le numero a rechercher
	  object    : l'objet
  Sortie: le numero de l'objet
**************************************************************************/
{
  OBJECT_OP obj, pure;

  switch(object->type) {
    case assembly_type:
      for(obj = object->object.assembly.sons; obj != NULL; obj = obj->next)
	if ((pure = search_for_num(num, obj)) != NULL)
	  return pure;
      return NULL;
    case composite_type:
      for(obj = object->object.composite.sons; obj != NULL; obj = obj->next)
	if ((pure = search_for_num(num, obj)) != NULL)
	  return pure;
      return NULL;
    case pure_type:
      if (num == object->object.pure.id)
	return object;
      else
	return NULL;
    default:
      break;
  }
}

TRIANGLEP get_patch_by_num(SCENE_OP scene, SHOOT_PP shoot)
/**************************************************************************
  But	: Determine l'adresse d'une patch dans une scene en fonction de
	  son numero d'objet et numero de patch
  Entree: scene	    : la scene
	  shoot	    : la description de la patch
  Sortie: l'adresse de la patch
**************************************************************************/
{
  OBJECT_OP pure;

  if ((pure = search_for_num(shoot->o, scene->scene)) == NULL)
    Bye_bye("Invalid object number !!\n");

  return (&(pure->object.pure.polys[shoot->p]));
}

BOOLEAN sim_fire_obj(SCENE_OP scene, OBJECT_OP object, int depth)
/**************************************************************************
  But	: Parcours la hierarchie a partir de l'objet object, jusqu'a 
	  trouver l'objet pure courant, tout en mettant a jour les
	  pointeurs adequats
  Entree: scene	    : la scene
	  object    : l'objet a parcourir
	  depth	    : la profondeur
  Sortie: TRUE si l'objet pure courant a ete trouve, FALSE sinon
**************************************************************************/
{
  OBJECT_OP obj;

  static int o = 0;

  switch(object->type) {
    case assembly_type:
      for(obj = object->object.assembly.sons; obj != NULL; obj = obj->next)
	if (sim_fire_obj(scene, obj, (depth+1)))
	  {
	    o = 0;
	    scene->state.complex[depth] = object;
	    return TRUE;
	  }
      return FALSE;
    case composite_type:
      for(obj = object->object.composite.sons; obj != NULL; obj = obj->next)
	if (sim_fire_obj(scene, obj, (depth+1)))
	  {
	    scene->state.complex[depth] = object;
	    return TRUE;
	  }
      return FALSE;
    case pure_type:
      o++;
      if (scene->state.o == o)
	{
	  scene->state.pure = object;
	  return TRUE;
	}
      return FALSE;
    default:
      break;
  }
}

void fix_current_pointers(SCENE_OP scene)
/**************************************************************************
  But	: Met a jour les pointeurs courants correspondants a la patch
	  receptrice courante
  Entree: scene	    : la scene
  Sortie: neant
**************************************************************************/
{
  if (!sim_fire_obj(scene, scene->scene, 0))
    Bye_bye("Can't fix current pointers !!\n");
}

BOOLEAN load_textures(FILE *statef, SCENE_OP scene)
/**************************************************************************
  But	: Relit les textures
  Entree: statef    : le fichier d'entree
	  scene	    : la scene
  Sortie: TRUE si l'operation a reussi, FALSE sinon
**************************************************************************/
{
  int i;

  if (fread(scene->textures, sizeof(TEXTURE), MAX_TEX, statef) != MAX_TEX)
    return FALSE;

  for(i = 1; i < scene->state.nb_tex; i++)
    {
      STR   tex_name;
      char  *sn;

      sn = get_short_name(scene->textures[i].name);
      strcpy(tex_name, tex_path);
      strcat(tex_name, "/");
      strcat(tex_name, sn);

      scene->textures[i].image =
	load_image(tex_name,
		   &(scene->textures[i].width),
		   &(scene->textures[i].height),
		   scene->textures[i].meancolor);
      if (scene->textures[i].image == NULL)
	return FALSE;

      texdef2d(i,
	       4,
	       scene->textures[i].width,
	       scene->textures[i].height,
	       scene->textures[i].image,
	       7,
	       texprops);
    }

  return TRUE;
}

BOOLEAN load_subdiv(TRIANGLE	**tri,
		    OBJECT_OP	 obj,
		    TRIANGLE	*dad,
		    FILE	*statef)
/**************************************************************************
  But	: Lit l'etat actuel d'une subdivision dans le fichier ouvert, avec
	  ses subdivisions eventuelles, dans un format binaire dedie.
	  Alloue la place en memoire pour la subdivision
  Entree: tri	    : handle sur la subdivision
	  obj	    : l'objet contenant la subdivision
	  dad	    : le polygone pere de la subdivision
	  statef   : le fichier d'entree
  Sortie: TRUE si l'operation a reussi, FALSE sinon
**************************************************************************/
{
  register int	i;
  int	    mark;
  TRIANGLE  *t;
  BOOLEAN   sons;

  *tri = NULL;
  t    = (TRIANGLE *)malloc(sizeof(TRIANGLE));
  assert(t);

  t->type = subdiv_type;
  t->dad  = obj;
  t->tri.subdiv.dad = dad;

  for(i = 0; i < 4; i++)
    {
      fread(&mark, sizeof(int), 1, statef);
      if (mark != START_SUBDIV)
	return FALSE;

      fread(&(t->tri.subdiv.elements[i]), sizeof(ELEMENT), 1, statef);

      t->tri.subdiv.elements[i].sons = NULL;
      sons = (BOOLEAN)getc(statef);
      if (sons)
        if (!load_subdiv(&(t->tri.subdiv.elements[i].sons), obj, dad, statef))
	  return FALSE;

      fread(&mark, sizeof(int), 1, statef);
      if (mark != END_SUBDIV)
	return FALSE;
    };

  *tri = t;
  return TRUE;
}

BOOLEAN load_poly(TRIANGLE  *tri,
		  OBJECT_OP obj,
		  FILE	    *statef)
/**************************************************************************
  But	: Lit l'etat actuel d'un polygone dans le fichier ouvert, avec ses
	  subdivisions eventuelles, dans un format binaire dedie.
	  Alloue la place en memoire pour les composantes dynamiques du
	  polygone
  Entree: tri	    : le polygone
	  obj	    : l'objet contenant le polygone
	  statef   : le fichier d'entree
  Sortie: TRUE si l'operation a reussi, FALSE sinon
**************************************************************************/
{
  BOOLEAN   sons;

  tri->type = poly_type;
  tri->dad  = obj;
  fread(&(tri->tri.poly), sizeof(POLY), 1, statef);
  tri->tri.poly.nb_tot = tri->tri.poly.nb_v*FILL_FACT;
  tri->tri.poly.vertex =
    (VERTEXP)malloc(sizeof(VERTEX)*tri->tri.poly.nb_tot);
  assert(tri->tri.poly.vertex);
  fread(tri->tri.poly.vertex, sizeof(VERTEX),
	tri->tri.poly.nb_v, statef);

  tri->tri.poly.sons = NULL;
  sons = (BOOLEAN)getc(statef);
  if (sons)
    if (!load_subdiv(&(tri->tri.poly.sons), obj, tri, statef))
      return FALSE;

  init_samples(tri);

  return TRUE;
}

BOOLEAN load_pure(OBJECT_OP obj,
		  FILE	    *statef)
/**************************************************************************
  But	: Lit l'etat actuel d'un objet pure dans le fichier ouvert,
	  dans un format binaire dedie. Alloue la place en memoire pour
	  les composantes dynamiques de l'objet
  Entree: obj	    : l'objet
	  statef   : le fichier d'entree
  Sortie: TRUE si l'operation a reussi, FALSE sinon
**************************************************************************/
{
  register  int	i;
  int mark;

  fread(&(obj->object.pure), sizeof(PURE_O), 1, statef);

  obj->object.pure.polys =
    (TRIANGLE *)malloc(sizeof(TRIANGLE)*obj->object.pure.nb_polys);
  assert(obj->object.pure.polys);

  for(i = 0; i < obj->object.pure.nb_polys; i++)
    {
      fread(&mark, sizeof(int), 1, statef);
      if (mark != START_POLY)
	return FALSE;

      if (!load_poly(&(obj->object.pure.polys[i]), obj, statef))
        return FALSE;

      fread(&mark, sizeof(int), 1, statef);
      if (mark != END_POLY)
	return FALSE;
    };

  return TRUE;
}

BOOLEAN load_object(OBJECT_OP	*obj,
		    FILE	*statef)
/**************************************************************************
  But	: Lit l'etat actuel d'un objet dans le fichier ouvert,
	  dans un format binaire dedie. Alloue la place en memoire
  Entree: obj	    : handle sur l'objet
	  statef   : le fichier d'entree
  Sortie: TRUE si l'operation a reussi, FALSE sinon
**************************************************************************/
{
  Object_type   type;
  OBJECT_OP	o, oo, *place;

  *obj = NULL;

  if (!fread(&type, sizeof(Object_type), 1, statef))
    return FALSE;

  if (type == unknown)
    return FALSE;

  o = (OBJECT_OP)malloc(sizeof(OBJECT_O));
  assert(o);

  o->type = type;
  o->next = NULL;
  fread(o->name, NAME_SIZE, 1, statef);

  switch(type) {
    case assembly_type :
      o->object.assembly.sons = NULL;
      place = &(o->object.assembly.sons);
      while(load_object(&oo, statef))
	{
	  *place = oo;
	  place = &(oo->next);
	  *place = NULL;
	}
      break;
    case composite_type :
      o->object.composite.sons = NULL;
      o->object.composite.grid = NULL;
      place = &(o->object.composite.sons);
      while(load_object(&oo, statef))
	{
	  *place = oo;
	  place = &(oo->next);
	  *place = NULL;
	}
      break;
    case pure_type :
      o->object.pure.grid = NULL;
      if (!load_pure(o, statef))
	{
	  free(o);
	  return FALSE;
	};
      break;
    default :
      return FALSE;
  };

  if (!fread(&type, sizeof(Object_type), 1, statef))
    return FALSE;

  if (type != unknown)
    return FALSE;

  *obj = o;
  return TRUE;
}

BOOLEAN load_state(FILE *statef, SCENE_OP scene)
/**************************************************************************
  But	: Lit l'etat du calcul d'une scene depuis un fichier,
	  dans un format binaire dedie
  Entree: statef    : le fichier (ouvert)
	  scene	    : la scene rendue
  Sortie: TRUE si l'operation a reussi, FALSE sinon
**************************************************************************/
{
  VECTOR v_dum1, v_dum2;

  if (fread(&(scene->state), sizeof(STATE_BLK), 1, statef) != 1)
    return FALSE;

  if (!load_object(&(scene->scene), statef))
    {
      free_scene(scene);
      return FALSE;
    }

  if (!load_textures(statef, scene))
    {
      free_scene(scene);	
      return FALSE;
    }

  prep_object(scene->scene, NULL, v_dum1, v_dum2);

  init_grids(scene->scene);

  if (scene->state.not_done)
    {
      scene->state.shooting_p.patch =
	get_patch_by_num(scene, &(scene->state.shooting_p));
      scene->state.new_shoot.patch =
	get_patch_by_num(scene, &(scene->state.new_shoot));
      fix_current_pointers(scene);
    }

  return TRUE;
}

BOOLEAN load_new_state(FILE *file, File_type type, SCENE_OP scene)
/**************************************************************************
  But	: Lit l'etat du calcul d'une nouvelle scene depuis un fichier,
	  dans un format binaire dedie, apres avoir desalloue l'espace
	  memoire emploie par le calcul courant
  Entree: file	    : le fichier ouvert
	  type	    : le type de fichier (description/calcul en cours)
	  scene	    : la scene rendue
  Sortie: TRUE si l'operation a reussi, FALSE sinon
**************************************************************************/
{
  free_scene(scene);
  scene->scene = NULL;

  init_glob_var();

  if (type == des_type)
    {
      yyin = file;
      if (!yyparse())
        init_scene(scene);
      else
	{
	  scene->scene = NULL;
	  return FALSE;
	}
    }
  else
    if (!load_state(file, scene))
      return FALSE;

  init_tev();

  return TRUE;
}
