/* ui_file.c: user interface file menu */

#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

#include "ui.h"
#include "uit.h"
#include "defaults.h"
#include "canvas.h"
#include "render.h"
#include "statistics.h"
#include "pools.h"
#include "error.h"
#include "readmgf.h"
#include "fix.h"
#include "appdata.h"
#include "camera.h"
#include "writevrml.h"
#include "scene.h"
#include "radiance.h"
#include "fileopts.h"

int force_onesided_surfaces, monochrome, nqcdivs, disable_textures;

/* Forward declarations */

static void PushRecentFile(char *filename);
static void UpdateCurrentFileCamera(void);


/* + + + + + + + + + + + file options dialog + + + + + + + + + + + + +*/
static Widget CreateNrQuartCircDivDialog(Widget parent, char *name)
{
  return CreatePromptDialog(parent, name, FET_INTEGER, (XtPointer)&nqcdivs, NULL, 0);
}

static Widget CreateFileOptionsMenu(Widget parent)
{
  Widget menu = CreateSubMenu(parent, "fileOptionsButton", "fileOptionsMenu");

  CreateToggleButton(menu, "forceOneSidednessButton",
		     force_onesided_surfaces,
		     NULL, (XtPointer)&force_onesided_surfaces);

  CreateToggleButton(menu, "disableTexturesButton",
		     disable_textures,
		     NULL, (XtPointer)&disable_textures);

  CreateToggleButton(menu, "monochromeButton",
		     monochrome,
		     NULL, (XtPointer)&monochrome);

  CreateCascadeDialog(menu, "nqcdivsButton", 
		      CreateNrQuartCircDivDialog, 
		      "nqcdivsDialog",
		      NULL, NULL);

  return menu;
}


/* + + + + + + + + + + + + + + + load button + + + + + + + + + + + +*/
static int LoadFile(char *filename, FILE *fp, int ispipe, Widget loadBox)
{
  int succes;

  if (CanvasGetMode() == CANVASMODE_WORKING) {
    Warning(NULL, "Interrupt computations first before loading a new scene");
    return 0;	/* don't unmanage the file selection box */
  }

  if (!fp) {
    Error(NULL, "Please enter a file name to be loaded");
    return 0;
  }

  UpdateCurrentFileCamera();

  CanvasPushMode(CANVASMODE_WORKING);
  succes = ReadFile(filename);
  CanvasPullMode();

  if (succes)
    PushRecentFile(filename);

  RenderNewDisplayList();
  CanvasPostRedraw();

  return succes;	/* unmanage the file selection box if succes */
}

static int LoadBgFile(char *filename, FILE *fp, int ispipe, Widget loadBox)
{
  int succes;

  if (!fp) {
    Error(NULL, "Please enter a file name to be loaded");
    return 0;
  }

  UpdateCurrentFileCamera();

  CanvasPushMode(CANVASMODE_WORKING);
  succes = InitBackground(fp);
  CanvasPullMode();

  RenderNewDisplayList();
  CanvasPostRedraw();


  return succes;	/* unmanage the file selection box if succes */
}


/* Creates a dialog box for selecting a file to be loaded. */
static Widget CreateLoadBox(Widget parent, char *name)
{
  return CreateFileSelectionDialog(parent, name, LoadFile, "r");
}

/* Creates a dialog box for selecting a file to be loaded. */
static Widget CreateLoadBgBox(Widget parent, char *name)
{
  return CreateFileSelectionDialog(parent, name, LoadBgFile, "r");
}

/* + + + + + + + + + + + + + + + Fix button + + + + + + + + + + + +*/
static void FixVertexOrderInViewCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
  FixVertexOrderInView();
}

static void CreateFixMenu(Widget fileMenu)
{
  Widget fixMenu;

  fixMenu = CreateSubMenu(fileMenu, "fixButton", "fixMenu");
  CreatePushButton(fixMenu, "fixVertexOrderInViewButton", FixVertexOrderInViewCallback, (XtPointer)NULL);
}

/* + + + + + + + + + + + + + + + Save button + + + + + + + + + + + +*/
static int SaveImage(char *fname, FILE *fp, int ispipe, Widget saveBox)
{
  if (!fp)
    return 1;	/* no file to save anything to, so don't save */

  /* make sure the dialog box has dissappeared before saving */
  XtUnmanageChild(saveBox);
  CheckForEvents();
  CanvasRedraw();

  CanvasPushMode(CANVASMODE_WORKING);
  SaveScreen(fname, fp, ispipe);
  CanvasPullMode();

  return 0;	/* probably succes, but we have already unmanaged the file 
		 * selection box. */
}

static Widget CreateSaveImageBox(Widget parent, char *name)
{
  return CreateFileSelectionDialog(parent, name, SaveImage, "w");
}

static int SaveModel(char *fname, FILE *fp, int ispipe, Widget saveBox)
{
  clock_t t;

  if (!fp)
    return 1;	/* no file to save anything to, so don't save */

  if (!World) {
    Warning(NULL, "There is no model loaded");
    return 1;
  }

  CanvasPushMode(CANVASMODE_WORKING);
  fprintf(stderr, "Saving VRML model to file '%s' ... ", fname); fflush(stderr);
  t = clock();

  if (Radiance && Radiance->WriteVRML)
    Radiance->WriteVRML(fp);
  else
    WriteVRML(fp);

  fprintf(stderr, "%g secs.\n", (float)(clock() - t) / (float)CLOCKS_PER_SEC);
  CanvasPullMode();

  return 1;	/* assume succes, so the dialog box is unmanaged. */
}

static Widget CreateSaveModelBox(Widget parent, char *name)
{
  return CreateFileSelectionDialog(parent, name, SaveModel, "w");
}

/* + + + + + + + + + + + + + + + Stats button + + + + + + + + + + + +*/
/* format string to be used for printing statistics about the current scene.
 * The format string is the text string assigned to the fileStatsMessage 
 * label at startup and is extracted in CreateFileStatsBox() when creating the 
 * interface. */
static char *fileStatsFormatString = (char *)NULL;

/* XmLabelClass widgets used to display the statstics about the current scene */
static Widget fileStatsMessage = (Widget)NULL;

/* updates the file statstics */
void UpdateFileStats(void)
{
  char buf[MAX_LABEL_STRING_LENGTH];

  if (!fileStatsFormatString || !fileStatsMessage)
    return;

  sprintf(buf, fileStatsFormatString,
	  nrgeoms, nrcompounds, nrsurfaces, nrvertices, nrpatches,
	  total_area, 
	  ColorLuminance(total_emitted_power), 
	  ColorLuminance(max_selfemitted_power),
	  M_PI*ColorLuminance(max_selfemitted_radiance),
	  M_PI*ColorLuminance(estimated_average_radiance),
	  ColorGray(average_reflectivity),
	  (int)GetMemoryUsage()/1024,
	  (int)GetMemoryOverhead()/1024);

  SetLabelString(fileStatsMessage, buf);
}

/* called when the Update button on a stats dialog box is clicked: pops down the
 * statistics dialog box. */
static void UpdateFileStatsCallback(Widget statsBox, XtPointer client_data, XtPointer call_data)
{
  UpdateFileStats();
}

/* called when the Dismiss button on a stats dialog box is clicked: pops down the
 * statistics dialog box. */
static void DismissStatsCallback(Widget statsBox, XtPointer client_data, XtPointer call_data)
{
  XtUnmanageChild(statsBox);
}

/* Creates a dialog box for showing statistics about the current scene */
static Widget CreateFileStatsBox(Widget parent, char *name)
{
  Widget statsBox;

  /* create a message dialog to show some statistics about the computation */
  statsBox = CreateDialog(parent, name);

  /* create a label widget inside the statsBox */
  fileStatsMessage = CreateLabel(statsBox, "fileStatsMessage");

  /* at startup, the text defined for the fileStatsMessage label widget is nothing 
   * else than the printf format string to be used for printing the statistics. */
  fileStatsFormatString = GetLabelString(fileStatsMessage);

  /* fill in the statstics for the current scene */
  UpdateFileStats();

  XtAddCallback(statsBox, XmNokCallback, UpdateFileStatsCallback, (XtPointer)NULL);
  XtAddCallback(statsBox, XmNcancelCallback, DismissStatsCallback, (XtPointer)NULL);

  return statsBox;
}

/* called when the user presses the 'Statistics' button in the file menu */
static void ShowFileStats(Widget statsButton, XtPointer client_data, XtPointer call_data)
{
  Widget statsBox = (Widget)client_data;

  UpdateFileStats();
  XtManageChild(statsBox);
}

/* + + + + + + + + + + + + + + + Exit button + + + + + + + + + + + +*/
/* when the user clicks the 'Exit' button in the File menu ... */
static void Exit(Widget w, XtPointer client_data, XtPointer call_data)
{
  UpdateCurrentFileCamera();
  SaveUserOptions();
  exit(0);
}


/* + + + + + + + + + + + + Recent File management + + + + + + + + + + +*/

char *GetFilePart(char *name)
{
  char *filepart;

  if(name == NULL)
    return NULL;

  filepart = rindex(name, '/');

  if(filepart == NULL)
  {
    return(name);
  }

  return(filepart + 1); /* After the '/' */
}


/* SetRecentFileLabels : sets file menu labels correctly */

void SetRecentFileLabels(void)
{
  char *name;
  int i;

  for(i=0; i < NUM_RECENT_FILES; i++)
  {
    if(appData.recentFile[i].spec == NULL)
    {
      name = NULL;
    }
    else
    {
      name = GetFilePart(appData.recentFile[i].spec);
    }

    if(name == NULL || name[0] == '-')
    {
      XtUnmanageChild(appData.recentFile[i].widget);
    }
    else
    {
      XtVaSetValues(appData.recentFile[i].widget,
		    XtVaTypedArg, XmNlabelString, XmRString,
		    name, strlen(name)+1, NULL);
      XtManageChild(appData.recentFile[i].widget);
    }
  }
}

/* TODO : Get rid of this static */
static int IsFilePushed = 0;

static void PushRecentFile(char *filename)
{
  int i;

  IsFilePushed = 1;

  /* Check if filename is already present in the list.
   * Last entry is not important, since that will be discarded
   * anyway if the filename is not found
   */

  i = 0;
  while((i < NUM_RECENT_FILES - 1) && ((appData.recentFile[i].spec == NULL) ||
				      (strcmp(filename, 
					      appData.recentFile[i].spec))))
  {
    i++;
  }

  /* i = recent file field that can be overwritten */

  /* TODO Free filename that is discarded ?? */

  /* Copy other recent files */

  while(i > 0)
  {
    /* Don't copy the widget !!! */
    appData.recentFile[i].spec = appData.recentFile[i-1].spec;
    appData.recentFile[i].cam = appData.recentFile[i-1].cam;
    appData.recentFile[i].acam = appData.recentFile[i-1].acam;
    i--;
  }

  appData.recentFile[0].spec = strdup(filename);

  SetRecentFileLabels();
}

static void LoadRecentFile(Widget w, XtPointer client_data, 
			   XtPointer call_data)
{
  int fileNr = (int)client_data;
  char *filename = appData.recentFile[fileNr].spec;
  CAMERA    oldCam, oldACam;
  CAMERA  * newCam   = &(appData.recentFile[fileNr].cam);
  CAMERA  * newACam  = &(appData.recentFile[fileNr].acam);
  int camPushed = FALSE;

  fprintf(stderr, "LoadRecentFile '%s'\n", filename);

  UpdateCurrentFileCamera();

  CanvasPushMode(CANVASMODE_WORKING);
  
  /* Store the current camera and alternate camera */
  oldCam = Camera;
  oldACam = AlternateCamera;

  if (ReadFile(filename)) {
    /* Set the camera */
 
    if(!CameraCompare(&oldCam, &Camera))
    {
      /* The camera was changed during the file read.
      ** We save this camera, so that it can be restored
      ** if needed. (The test is not foolproof, since
      ** the new cam can be equal to the old cam.)
      */

      CameraPush(&Camera);
      camPushed = TRUE;
    }

    if(!(CameraSet(&Camera, &(newCam->eyep), &(newCam->lookp), 
		   &(newCam->updir), newCam->fov, Camera.hres,
		   Camera.vres, &(Camera.background))))
    {
      if(camPushed)
      {
	CameraPop(&Camera);
      }
      else
      {
	Camera = oldCam;
      }
    }

    /* Set the alternate camera */
    if(!(CameraSet(&AlternateCamera, &(newACam->eyep), &(newACam->lookp), 
		   &(newACam->updir), newACam->fov, Camera.hres,
		   Camera.vres, &(AlternateCamera.background))))
    {
      Warning("LoadRecentFile","cannot set alternate camera"); 
      AlternateCamera = oldACam;
    }

    RenderNewDisplayList();  
    /* Store the current camera */
    oldCam = Camera;

    RenderScene();
    PushRecentFile(filename);
  }
  CanvasPullMode();
}

/* TODO : filename on command line -> recent files */

static void UpdateCurrentFileCamera(void)
{
  /* recentFile[0] is the current file, except
     when no file is loaded */

  if(IsFilePushed)
  {
    appData.recentFile[0].cam = Camera;
    appData.recentFile[0].acam = AlternateCamera;
  }
}



/* + + + + + + + + + + + + + + + File menu + + + + + + + + + + + + +*/
/* creates the File menu in the main menu bar */
void CreateFileMenu(Widget menuBar)
{
  int i;
  Widget fileMenu = CreateSubMenu(menuBar, "fileButton", "fileMenu");

  CreateFileOptionsMenu(fileMenu);
  CreateCascadeDialog(fileMenu, "loadButton", CreateLoadBox, "loadBox", NULL, NULL);

  CreateSeparator(fileMenu, "fileSeparator");
  CreateCascadeDialog(fileMenu, "saveImageButton", CreateSaveImageBox, "saveImageBox", NULL, NULL);
  CreateCascadeDialog(fileMenu, "saveModelButton", CreateSaveModelBox, "saveModelBox", NULL, NULL);

  CreateSeparator(fileMenu, "fileSeparator");
  CreateCascadeDialog(fileMenu, "loadBgButton", CreateLoadBgBox, "loadBgBox", NULL, NULL);

  CreateSeparator(fileMenu, "fileSeparator");
  CreateCascadeDialog(fileMenu, "statsButton", CreateFileStatsBox, "statsBox", ShowFileStats, NULL);

  /* --------------------- Recent files ----------------------- */
  CreateSeparator(fileMenu, "fileSeparator");
  CreateSeparator(fileMenu, "fileSeparator");

  /* NUM_RECENT_FILES Last used files */

  for(i = 0; i < NUM_RECENT_FILES; i++)
  {
    char tmpStr[20];

    sprintf(tmpStr, "lastFile%iButton", i);

    appData.recentFile[i].widget = CreatePushButton(fileMenu, tmpStr, 
						  LoadRecentFile, 
						  (XtPointer)i);
  }

  SetRecentFileLabels();

  /* ------------------- Exit button ------------------- */
  CreateSeparator(fileMenu, "fileSeparator");
  CreateSeparator(fileMenu, "fileSeparator");
  CreatePushButton(fileMenu, "exitButton", Exit, (XtPointer)NULL);
}

