/* hura.c: Het Ultieme Rendering Algoritme (The Ultimate Rendering Algorithm) */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>

#include "hura.h"
#include "huraP.h"

#include "pools.h"
#include "scene.h"	/* contains global scene variables. */
#include "error.h"
#include "options.h"	/* for command line option processing relevant to the HURA */

/* it's a good idea to store all global variables for a rendering method in
 * one struct, declared e.g. in huraP.h, so you have only one global
 * variable. Choose a unique name for this variable in order to avoid
 * name clashes with other rendering methods. I often call this struct
 * the 'state' of the rendering method. */
HURA_STATE hura;

/* Initializes the rendering methods 'state' structure. Don't forget to
 * update radiance.c to call this routine! */
static void HuraDefaults(void)
{
  hura.cpu_secs = 0.0;
  hura.bla = 3.141592;
}

/* command line options for the HURA are described in the following table. See
 * options.h for details. Other examples are in main.c, ui_main,c radiance.c, camera.c,
 * etc... The only thing you need to do in order to add an option is add a line to this
 * table, except for writing the action routine if one is needed (that is:
 * if processing the option involves more than asigning a value to a variable) */
static CMDLINEOPTDESC huraOptions[] = {
  {"-hura-bla",	0,	Tfloat,		&hura.bla,	DEFAULT_ACTION,
   "-hura-bla <float>\t: crucial HURA parameter"},
  {NULL	, 	0,	TYPELESS, 	NULL, 	DEFAULT_ACTION,
   NULL }
};

/* called from radiance.c ParseRadianceOptions(), which is called from main.c 
 * ParseGlobalOptions() for parsing command line arguments. Recognized command line
 * "words" are removed from the command line by ParseOptions(). */
static void ParseHuraOptions(int *argc, char **argv)
{
  ParseOptions(huraOptions, argc, argv);
}

/* called from PrintRadianceOptions() in radiance.c, which is called among others
 * when the -help option is given on the command line. */
static void PrintHuraOptions(FILE *fp)
{
  fprintf(fp, "\nHURA options: The HURA has not been found, but we provided already one option:\n");
  PrintOptions(fp, huraOptions);
}

/* for counting how much CPU time was used for the computations */
static void UpdateCpuSecs(void)
{
  clock_t t;

  t = clock();
  hura.cpu_secs += (float)(t - hura.lastclock)/(float)CLOCKS_PER_SEC;
  hura.lastclock = t;
}

/* for waking up now and then while the computations are going on.
 * You can check hura.wake_up at safe points during the computations to
 * react to user possible input, e.g. by calling ProcessWaitingEvents() (ui.h). */
static void wake_up(int sig)
{
  hura.wake_up = TRUE;

  signal(SIGALRM, wake_up);
  alarm(/*after*/ 1 /*second*/);

  UpdateCpuSecs();
}

/* "pools" are a way to save storage overhead if you need many small
 * but equally sized cells. That's often the case here. 
 * HURA_PATCH_DATA is declared in huraP.h. */
#ifndef NOPOOLS
static POOL *dataPool = (POOL *)NULL;
#define NEWDATA()	(HURA_PATCH_DATA *)NewPoolCell(sizeof(HURA_PATCH_DATA), 0, "HURA data", &dataPool)
#define DISPOSEDATA(data) Dispose((char *)data, &dataPool)
#else
#define NEWDATA()	(HURA_PATCH_DATA *)Alloc(sizeof(HURA_PATCH_DATA))
#define DISPOSEDATA(data) Free((char *)data, sizeof(HURA_PATCH_DATA))
#endif

/* Create, print and dispose of data associated with the patch, passed as
 * argument to this function. Called when selecting the rendering method
 * in the Rendering main menu or when loading a new scene. */
static void *CreatePatchData(PATCH *patch)
{
  return patch->radiance_data = (void *)NEWDATA();
}

static void PrintPatchData(FILE *out, PATCH *patch)
{
  fprintf(out, "flux = "); ColorPrint(out, FLUX(patch)); fprintf(out, "\n");
}

static void DestroyPatchData(PATCH *patch)
{
  DISPOSEDATA(patch->radiance_data);
  patch->radiance_data = (void *)NULL;
}

/* static function initializing a patch (it's a good idea to declare static 
 * all your functions that are only needed here). */
static void InitPatch(PATCH *P)
{
  COLORCLEAR(FLUX(P));
}

/* Initializes the computations for the current scene (if any). */
static void InitHura(void)
{
  Warning(NULL, "The Ultimate Rendering Algorithm still needs to be implemented");

  /* ForAllPatches is an iterator macro defined in patchlist.h */
  /* 'Patches' is a global linear list of patches in the scene, defined
   * in scene.h */
  ForAllPatches(P, Patches) {
    InitPatch(P);
  } EndForAll;
}

/* Performs one step of the radiance computations. The goal most often is
 * to fill in a RGB color for display of each patch and/or vertex. See
 * the PATCH and VERTEX structs in ../patch.h and ../vertex.h. These
 * colors are used for hardware rendering if the default hardware rendering
 * method is not superceeded in this file. */
static int DoHuraStep(void)
{
  void (*prev_alrm_handler)(int signr); 
  unsigned prev_alarm_left;

  /* install a timer that will wake us up ofter one second for checking for 
   * user events */
  prev_alrm_handler = signal(SIGALRM, wake_up);
  prev_alarm_left = alarm(/*after*/ 1 /*second*/);
  hura.wake_up = FALSE;
  hura.lastclock = clock();

  /* do some real work now */
  Warning("DoHuraStep", "The Ultimate Rendering Algorithm has not been found yet, sorry ...");

  /* reinstall the previous alarm handler */
  signal(SIGALRM, prev_alrm_handler);
  alarm(prev_alarm_left);

  return TRUE;	/* done. Return FALSE if you want the computations to 
		 * continue. */
}

/* undoes the effect of Init() and all side-effects of Step() */
static void TerminateHura(void)
{
  /* nothing yet to do */
}

/* Returns the radiance emitted from the patch at the point with parameters
 * (u,v) into the direction 'dir'. */
static COLOR GetHuraRadiance(PATCH *patch, double u, double v, VECTOR dir)
{
  COLOR rad;

  COLORCLEAR(rad);
  return rad;
}

static char *GetHuraStats(void)
{
  static char stats[1000];
  char *p;
  int n;

  p = stats;
  sprintf(p, "H.U.R.A. Statistics:\n\n%n", &n); p += n;
  sprintf(p, "No statistics yet ...\n%n", &n); p += n;

  return stats;
}

static void HuraRecomputeDisplayColors(void)
{
  Error("HuraRecomputeDisplayColors", "Not yet implemented");
}

static void HuraUpdateMaterial(MATERIAL *oldmaterial, MATERIAL *newmaterial)
{
  Error("HuraUpdateMaterial", "Not yet implemented");
}

RADIANCEMETHOD Hura = {
  "HURA", 4,
  "The Ultimate Rendering Algorithm",
  "huraButton",
  HuraDefaults,
  ParseHuraOptions,
  PrintHuraOptions,
  InitHura,
  DoHuraStep,
  TerminateHura,
  GetHuraRadiance,
  CreatePatchData,
  PrintPatchData,
  DestroyPatchData,
  CreateHuraControlPanel,
  UpdateHuraControlPanel,
  ShowHuraControlPanel,
  HideHuraControlPanel,
  GetHuraStats,
  (void (*)(void))NULL,		/* use default hardware renderer */
  HuraRecomputeDisplayColors,
  HuraUpdateMaterial,
  (void (*)(FILE *))NULL	/* use default VRML model saver */
};

