/****************************************************************************
 *               povmsend.cpp
 *
 * This module contains POVMS received message handling routines.
 *
 * from Persistence of Vision(tm) Ray Tracer version 3.6.
 * Copyright 1991-2003 Persistence of Vision Team
 * Copyright 2003-2004 Persistence of Vision Raytracer Pty. Ltd.
 *---------------------------------------------------------------------------
 * NOTICE: This source code file is provided so that users may experiment
 * with enhancements to POV-Ray and to port the software to platforms other
 * than those supported by the POV-Ray developers. There are strict rules
 * regarding how you are permitted to use this file. These rules are contained
 * in the distribution and derivative versions licenses which should have been
 * provided with this file.
 *
 * These licences may be found online, linked from the end-user license
 * agreement that is located at http://www.povray.org/povlegal.html
 *---------------------------------------------------------------------------
 * This program is based on the popular DKB raytracer version 2.12.
 * DKBTrace was originally written by David K. Buck.
 * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
 *---------------------------------------------------------------------------
 *
 *===========================================================================
 * This file is part of MegaPOV, a modified and unofficial version of POV-Ray
 * For more information on MegaPOV visit our website:
 * http://megapov.inetart.net/
 *===========================================================================
 *
 * $RCSfile: povmsend.cpp,v $
 * $Revision: 1.31 $
 * $Author: chris $
 *
 *****************************************************************************/

#include <stdarg.h>
#include <float.h>
#include <algorithm>

#include "frame.h"
#include "vector.h"
#include "parse.h"
#include "povray.h"
#include "tokenize.h"
#include "userio.h"
#include "userdisp.h"
#include "povms.h"
#include "povmsend.h"
#include "octree.h"
#include "radiosit.h"
#include "optout.h"
#include "pov_err.h"
#include "pov_util.h"

#ifndef DONT_SHOW_IMAGE_LIB_VERSIONS
// these are needed for copyright notices and version numbers
#include "zlib.h"
#include "png.h"
#include "jversion.h"

// Including tiffio.h causes the Windows compile to break. As all we need is the
// version function, we just declare it here.
//#define __STDC__
//#include "tiffio.h"

extern "C" const char* TIFFGetVersion(void);
#endif

#ifdef CLOTHRAY_PATCH
  #include "clothray.h"
#endif
#ifdef MECHANICS_SIMULATION_PATCH 
  #include "mechsim.h"
#endif
#ifdef POST_PROCESS_PATCH
  #include "fnpovfpu.h"
  #include "postproc.h"
#endif

/*****************************************************************************
* Global variables
******************************************************************************/

extern POVMSContext POVMS_Render_Context; // GLOBAL VARIABLE

BEGIN_POV_NAMESPACE

extern POVMSObject *gStartedStreamMessage; // GLOBAL VARIABLE

extern const char *Extract_Version(const char *str);
extern int GetPhotonStat(POVMSType a);


#ifdef RADSTATS
extern long ot_inscount; // GLOBAL VARIABLE
extern long ot_nodecount; // GLOBAL VARIABLE
extern long ot_blockcount; // GLOBAL VARIABLE
extern long ot_minsize; // GLOBAL VARIABLE
extern long ot_maxsize; // GLOBAL VARIABLE

extern long overflows; // GLOBAL VARIABLE
extern long minloops; // GLOBAL VARIABLE
extern long maxloops; // GLOBAL VARIABLE

extern long ot_seenodecount; // GLOBAL VARIABLE
extern long ot_seeblockcount; // GLOBAL VARIABLE
extern long ot_doblockcount; // GLOBAL VARIABLE
extern long ot_dotokcount; // GLOBAL VARIABLE
extern long ot_lowerrorcount; // GLOBAL VARIABLE
#endif

/*****************************************************************************
* Local preprocessor defines
******************************************************************************/


/*****************************************************************************
* Local typedefs
******************************************************************************/


/*****************************************************************************
* Local variables
******************************************************************************/

DBL Previous_t = 0; // GLOBAL VARIABLE
int Previous_tp = 0; // GLOBAL VARIABLE
int Previous_th = 0; // GLOBAL VARIABLE
int Previous_tr = 0; // GLOBAL VARIABLE


/*****************************************************************************
* Local functions
******************************************************************************/

int BuildCommand(POVMSObjectPtr msg, POVMSType key, SHELLDATA *data);
int BuildRenderTime(POVMSObjectPtr msg, POVMSType key, int parse, int photon, int render, 
#ifdef CLOTHRAY_PATCH
                    int cloth,
#endif
#ifdef MECHANICS_SIMULATION_PATCH 
                    int mechsim,
#endif
#ifdef POST_PROCESS_PATCH
                    int postproc,
#endif
                    int total);
int BuildParseStatistics(POVMSObjectPtr msg);
int BuildRenderStatistics(POVMSObjectPtr msg, COUNTER *pstats);
int BuildProgress(POVMSObjectPtr msg, int progress);
int AddStatistic(POVMSObjectPtr msg, POVMSType key, COUNTER *counter);
int AddOIStatistic(POVMSAttributeListPtr list, int index, COUNTER *pstats);


/*****************************************************************************
*
* FUNCTION
*
*   BuildCommand
*
* INPUT
*   
* OUTPUT
*   
* RETURNS
*
* AUTHOR
*
*   Thorsten Froehlich
*   
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

int BuildCommand(POVMSObjectPtr msg, POVMSType key, SHELLDATA *data)
{
   POVMSObject obj;
   int err;

   err = POVMSObject_New(&obj, kPOVObjectClass_Command);
   if(err == 0)
      err = POVMSUtil_SetString(&obj, kPOVAttrib_CommandString, data->Command);
   if(err == 0)
   {
      #ifdef AVOID_MIGHT_BE_USED_UNINITIALIZED_WARNINGS_PATCH
        int i=0;
      #else
        int i;
      #endif

      switch(data->Ret)
      {
         case IGNORE_RET:
            i = 'I';
            break;
         case QUIT_RET:
            i = 'Q';
            break;
         case USER_RET:
            i = 'U';
            break;
         case FATAL_RET:
            i = 'F';
            break;
         case SKIP_ONCE_RET:
            i = 'S';
            break;
         case ALL_SKIP_RET:
            i = 'A';
            break;
      }

      if(data->Inverse == true)
         i = -i;

      err = POVMSUtil_SetInt(&obj, kPOVAttrib_ReturnAction, i);
   }
   if(err == 0)
      err = POVMSObject_Set(msg, &obj, key);

   return err;
}


/*****************************************************************************
*
* FUNCTION
*
*   BuildRenderTime
*
* INPUT
*   
* OUTPUT
*   
* RETURNS
*
* AUTHOR
*
*   Thorsten Froehlich
*   
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

int BuildRenderTime(POVMSObjectPtr msg, POVMSType key, int parse, int photon, int render, 
#ifdef CLOTHRAY_PATCH
                    int cloth,
#endif
#ifdef MECHANICS_SIMULATION_PATCH 
                    int mechsim,
#endif
#ifdef POST_PROCESS_PATCH
                    int postproc,
#endif
                    int total)
{
   POVMSObject obj;
   int err;

   err = POVMSObject_New(&obj, kPOVObjectClass_RTime);
   if(err == 0)
      err = POVMSUtil_SetInt(&obj, kPOVAttrib_ParseTime, parse);
   if(err == 0)
      err = POVMSUtil_SetInt(&obj, kPOVAttrib_PhotonTime, photon);
#ifdef CLOTHRAY_PATCH
   if(err == 0)
      err = POVMSUtil_SetInt(&obj, kPOVAttrib_ClothTime, cloth);
#endif
#ifdef MECHANICS_SIMULATION_PATCH 
   if(err == 0)
      err = POVMSUtil_SetInt(&obj, kPOVAttrib_MechTime, mechsim);
#endif
#ifdef POST_PROCESS_PATCH
   if(err == 0)
      err = POVMSUtil_SetInt(&obj, kPOVAttrib_PostTime, postproc);
#endif
   if(err == 0)
      err = POVMSUtil_SetInt(&obj, kPOVAttrib_TraceTime, render);
   if(err == 0)
      err = POVMSUtil_SetInt(&obj, kPOVAttrib_TotalTime, total);
   if(err == 0)
      err = POVMSObject_Set(msg, &obj, key);

   return err;
}


/*****************************************************************************
*
* FUNCTION
*
*   BuildRenderOptions
*
* INPUT
*   
* OUTPUT
*   
* RETURNS
*
* AUTHOR
*
*   Thorsten Froehlich
*   
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

int BuildRenderOptions(POVMSObjectPtr msg)
{
   POVMSAttribute attr;
   int err = kNoErr;

   if(msg == NULL)
      return kParamErr;

   if(err == kNoErr)
      err = POVMSAttr_New(&attr);
   if(err == kNoErr)
   {
      err = POVMSAttr_Set(&attr, kPOVMSType_WildCard, (void *)(&opts.Preview_RefCon), sizeof(unsigned long));
      if(err == kNoErr)
         err = POVMSObject_Set(msg, &attr, kPOVAttrib_PreviewRefCon);
   }
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_Height, Frame.Screen_Height);
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_Width, Frame.Screen_Width);
   if(err == kNoErr)
   {
      if (opts.First_Column == -1)
         err = POVMSUtil_SetFloat(msg, kPOVAttrib_StartColumn, opts.First_Column_Percent);
      else
         err = POVMSUtil_SetFloat(msg, kPOVAttrib_StartColumn, opts.First_Column);
   }
   if(err == kNoErr)
   {
      if (opts.Last_Column == -1)
         err = POVMSUtil_SetFloat(msg, kPOVAttrib_EndColumn, opts.Last_Column_Percent);
      else
         err = POVMSUtil_SetFloat(msg, kPOVAttrib_EndColumn, opts.Last_Column);
   }
   if(err == kNoErr)
   {
      if (opts.First_Line == -1)
         err = POVMSUtil_SetFloat(msg, kPOVAttrib_StartRow, opts.First_Line_Percent);
      else
         err = POVMSUtil_SetFloat(msg, kPOVAttrib_StartRow, opts.First_Line);
   }
   if(err == kNoErr)
   {
      if (opts.Last_Line == -1)
         err = POVMSUtil_SetFloat(msg, kPOVAttrib_EndRow, opts.Last_Line_Percent);
      else
         err = POVMSUtil_SetFloat(msg, kPOVAttrib_EndRow, opts.Last_Line);
   }
   if(err == kNoErr)
      err = POVMSUtil_SetBool(msg, kPOVAttrib_TestAbort, (opts.Options & EXITENABLE) != 0);
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_TestAbortCount, opts.Abort_Test_Counter);
   if(err == kNoErr)
      err = POVMSUtil_SetBool(msg, kPOVAttrib_ContinueTrace, (opts.Options & CONTINUE_TRACE) != 0);
   if(err == kNoErr)
      err = POVMSUtil_SetString(msg, kPOVAttrib_CreateIni, opts.Ini_Output_File_Name);
   if(err == kNoErr)
      err = POVMSUtil_SetFloat(msg, kPOVAttrib_Clock, opts.FrameSeq.Clock_Value);
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_InitialFrame, max(opts.FrameSeq.InitialFrame, 1));
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_FinalFrame, max(opts.FrameSeq.FinalFrame, 1));
   #ifdef FRAME_STEP_PATCH
     if(err == kNoErr)
        err = POVMSUtil_SetInt(msg, kPOVAttrib_FrameStep, opts.FrameSeq.FrameStep);
   #endif
   if(err == kNoErr)
      err = POVMSUtil_SetFloat(msg, kPOVAttrib_InitialClock, opts.FrameSeq.InitialClock);
   if(err == kNoErr)
   {
      if(opts.FrameSeq.FinalFrame <= 1)
         err = POVMSUtil_SetFloat(msg, kPOVAttrib_FinalClock, 1.0);
      else
         err = POVMSUtil_SetFloat(msg, kPOVAttrib_FinalClock, opts.FrameSeq.FinalClock);
   }
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_SubsetStartFrame, max(opts.FrameSeq.SubsetStartFrame, 1));
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_SubsetEndFrame, max(opts.FrameSeq.SubsetEndFrame, 1));
   if(err == kNoErr)
      err = POVMSUtil_SetBool(msg, kPOVAttrib_CyclicAnimation, (opts.Options & CYCLIC_ANIMATION) != 0);
   if(err == kNoErr)
      err = POVMSUtil_SetBool(msg, kPOVAttrib_FieldRender, opts.FrameSeq.Field_Render_Flag);
   if(err == kNoErr)
      err = POVMSUtil_SetBool(msg, kPOVAttrib_OddField, opts.FrameSeq.Odd_Field_Flag);
   if(err == kNoErr)
      err = POVMSUtil_SetBool(msg, kPOVAttrib_PauseWhenDone, (opts.Options & PROMPTEXIT) != 0);
   if(err == kNoErr)
      err = POVMSUtil_SetBool(msg, kPOVAttrib_Verbose, (opts.Options & VERBOSE) != 0);
   if(err == kNoErr)
      err = POVMSUtil_SetBool(msg, kPOVAttrib_DrawVistas, (opts.Options & USE_VISTA_DRAW) != 0);
   if(err == kNoErr)
      err = POVMSUtil_SetBool(msg, kPOVAttrib_Display, (opts.Options & DISPLAY) != 0);
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_VideoMode, opts.DisplayFormat);
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_Palette, opts.PaletteOption);
   if(err == kNoErr)
      err = POVMSUtil_SetFloat(msg, kPOVAttrib_DisplayGamma, opts.DisplayGamma);
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_PreviewStartSize, opts.PreviewGridSize_Start);
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_PreviewEndSize, opts.PreviewGridSize_End);
   if(err == kNoErr)
      err = POVMSUtil_SetBool(msg, kPOVAttrib_OutputToFile, (opts.Options & DISKWRITE) != 0);
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_OutputFileType, opts.OutputFormat);
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_Compression, opts.OutputQuality);
   if(err == kNoErr)
      err = POVMSUtil_SetBool(msg, kPOVAttrib_OutputAlpha, (opts.Options & OUTPUT_ALPHA) != 0);
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_BitsPerColor, opts.OutputQuality);
   if(err == kNoErr)
      err = POVMSUtil_SetString(msg, kPOVAttrib_OutputFile, opts.Output_File_Name);
   if(err == kNoErr)
      err = POVMSUtil_SetString(msg, kPOVAttrib_OutputPath, opts.Output_Path);
#if PRECISION_TIMER_AVAILABLE
   if(err == kNoErr)
      err = POVMSUtil_SetBool(msg, kPOVAttrib_CreateHistogram, opts.histogram_on != 0);
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_HistogramFileType, opts.histogram_type);
   if(err == kNoErr)
      err = POVMSUtil_SetString(msg, kPOVAttrib_HistogramFile, opts.Histogram_File_Name);
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_HistogramGridSizeX, opts.histogram_x);
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_HistogramGridSizeY, opts.histogram_y);
#endif /* PRECISION_TIMER_AVAILABLE */
   if(err == kNoErr)
      err = BuildCommand(msg, kPOVAttrib_PreSceneCommand, &opts.Shellouts[PRE_SCENE_SHL]);
   if(err == kNoErr)
      err = BuildCommand(msg, kPOVAttrib_PreFrameCommand, &opts.Shellouts[PRE_FRAME_SHL]);
   if(err == kNoErr)
      err = BuildCommand(msg, kPOVAttrib_PostSceneCommand, &opts.Shellouts[POST_SCENE_SHL]);
   if(err == kNoErr)
      err = BuildCommand(msg, kPOVAttrib_PostFrameCommand, &opts.Shellouts[POST_FRAME_SHL]);
   if(err == kNoErr)
      err = BuildCommand(msg, kPOVAttrib_UserAbortCommand, &opts.Shellouts[USER_ABORT_SHL]);
   if(err == kNoErr)
      err = BuildCommand(msg, kPOVAttrib_FatalErrorCommand, &opts.Shellouts[FATAL_SHL]);
   if(err == kNoErr)
      err = POVMSUtil_SetString(msg, kPOVAttrib_InputFile, opts.Input_File_Name);
   if(err == kNoErr)
   {
      POVMSAttributeList list;

      err = POVMSAttrList_New(&list);
      if(err == kNoErr)
      {
         int ii;

         for(ii = 0; ii < opts.Library_Path_Index; ii++)
         {
            err = POVMSAttr_New(&attr);
            if(err == kNoErr)
            {
               err = POVMSAttr_Set(&attr, kPOVMSType_CString, opts.Library_Paths[ii], strlen(opts.Library_Paths[ii]) + 1);
               if(err == kNoErr)
                  err = POVMSAttrList_Append(&list, &attr);
               else
                  err = POVMSAttr_Delete(&attr);
            }
         }
         if(err == kNoErr)
            err = POVMSObject_Set(msg, &list, kPOVAttrib_LibraryPath);
      }
   }
   if(err == kNoErr)
   {
      POVMSFloat f = opts.Language_Version / 100.0;
      err = POVMSUtil_SetFloat(msg, kPOVAttrib_Version, f);
   }
/* FIXME
   if(err == kNoErr)
      err = POVMSUtil_SetBool(msg, kPOVAttrib_DebugConsole, Stream_Info[DEBUG_STREAM]->console != NULL);
   if(err == kNoErr)
      err = POVMSUtil_SetBool(msg, kPOVAttrib_FatalConsole, Stream_Info[FATAL_STREAM]->console != NULL);
   if(err == kNoErr)
      err = POVMSUtil_SetBool(msg, kPOVAttrib_RenderConsole, Stream_Info[RENDER_STREAM]->console != NULL);
   if(err == kNoErr)
      err = POVMSUtil_SetBool(msg, kPOVAttrib_StatisticsConsole, Stream_Info[STATISTIC_STREAM]->console != NULL);
   if(err == kNoErr)
      err = POVMSUtil_SetBool(msg, kPOVAttrib_WarningConsole, Stream_Info[WARNING_STREAM]->console != NULL);
   if(err == kNoErr)
      err = POVMSUtil_SetBool(msg, kPOVAttrib_AllConsole, Stream_Info[ALL_STREAM]->console != NULL);
   if((err == kNoErr) && (Stream_Info[DEBUG_STREAM]->name != NULL))
      err = POVMSUtil_SetString(msg, kPOVAttrib_DebugFile, Stream_Info[DEBUG_STREAM]->name);
   if((err == kNoErr) && (Stream_Info[FATAL_STREAM]->name != NULL))
      err = POVMSUtil_SetString(msg, kPOVAttrib_FatalFile, Stream_Info[FATAL_STREAM]->name);
   if((err == kNoErr) && (Stream_Info[RENDER_STREAM]->name != NULL))
      err = POVMSUtil_SetString(msg, kPOVAttrib_RenderFile, Stream_Info[RENDER_STREAM]->name);
   if((err == kNoErr) && (Stream_Info[STATISTIC_STREAM]->name != NULL))
      err = POVMSUtil_SetString(msg, kPOVAttrib_StatisticsFile, Stream_Info[STATISTIC_STREAM]->name);
   if((err == kNoErr) && (Stream_Info[WARNING_STREAM]->name != NULL))
      err = POVMSUtil_SetString(msg, kPOVAttrib_WarningFile, Stream_Info[WARNING_STREAM]->name);
   if((err == kNoErr) && (Stream_Info[ALL_STREAM]->name != NULL))
      err = POVMSUtil_SetString(msg, kPOVAttrib_AllFile, Stream_Info[ALL_STREAM]->name);
*/   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_Quality, opts.Quality);
   if(err == kNoErr)
      err = POVMSUtil_SetBool(msg, kPOVAttrib_Bounding, opts.Use_Slabs);
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_BoundingThreshold, opts.BBox_Threshold);
   if(err == kNoErr)
      err = POVMSUtil_SetBool(msg, kPOVAttrib_LightBuffer, (opts.Options & USE_LIGHT_BUFFER) != 0);
   if(err == kNoErr)
      err = POVMSUtil_SetBool(msg, kPOVAttrib_VistaBuffer, (opts.Options & USE_VISTA_BUFFER) != 0);
   if(err == kNoErr)
      err = POVMSUtil_SetBool(msg, kPOVAttrib_RemoveBounds, (opts.Options & REMOVE_BOUNDS) != 0);
   if(err == kNoErr)
      err = POVMSUtil_SetBool(msg, kPOVAttrib_SplitUnions, (opts.Options & SPLIT_UNION) != 0);
   if(err == kNoErr)
      err = POVMSUtil_SetBool(msg, kPOVAttrib_Antialias, (opts.Options & ANTIALIAS) != 0);
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_SamplingMethod, opts.Tracing_Method);
   if(err == kNoErr)
      err = POVMSUtil_SetFloat(msg, kPOVAttrib_AntialiasThreshold, opts.Antialias_Threshold);
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_AntialiasDepth, opts.AntialiasDepth);
   if(err == kNoErr)
      err = POVMSUtil_SetBool(msg, kPOVAttrib_Jitter, (opts.Options & JITTER) != 0);
   if(err == kNoErr)
      err = POVMSUtil_SetFloat(msg, kPOVAttrib_JitterAmount, opts.JitterScale);
   if(err == kNoErr)
      err = POVMSUtil_SetString(msg, kPOVAttrib_IncludeHeader, opts.Header_File_Name);

   return err;
}


/*****************************************************************************
*
* FUNCTION
*
*   BuildRenderStatus
*
* INPUT
*   
* OUTPUT
*   
* RETURNS
*
* AUTHOR
*
*   Thorsten Froehlich
*   
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

int BuildParseStatistics(POVMSObjectPtr msg)
{
   int err = kNoErr;

   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_FiniteObjects, numberOfFiniteObjects);
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_InfiniteObjects, numberOfInfiniteObjects);
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_LightSources, numberOfLightSources);

   return err;
}


/*****************************************************************************
*
* FUNCTION
*
*   BuildRenderStatistics
*
* INPUT
*   
* OUTPUT
*   
* RETURNS
*
* AUTHOR
*
*   Thorsten Froehlich
*   
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

int BuildRenderStatistics(POVMSObjectPtr msg, COUNTER *pstats)
{
   POVMSAttributeList list;
   int err = kNoErr;

   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_Height, Frame.Screen_Height);
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_Width, Frame.Screen_Width);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_Pixels, &pstats[Number_Of_Pixels]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_PixelSamples, &pstats[Number_Of_Samples]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_Rays, &pstats[Number_Of_Rays]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_RaysSaved, &pstats[ADC_Saves]);
   if(err == kNoErr)
      err = POVMSAttrList_New(&list);
   if(err == kNoErr)
   {
      int i;

      for(i = 0; intersection_stats[i].infotext != NULL; i++)
      {
         err = AddOIStatistic(&list, i, pstats);
         if(err != kNoErr)
            break;
      }
   }
   if(err == kNoErr)
      err = POVMSObject_Set(msg, &list, kPOVAttrib_ObjectIStats);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_ShadowTest, &pstats[Shadow_Ray_Tests]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_ShadowTestSuc, &pstats[Shadow_Rays_Succeeded]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_IsoFindRoot, &pstats[Ray_IsoSurface_Find_Root]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_FunctionVMCalls, &pstats[Ray_Function_VM_Calls]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_FunctionVMInstrEst, &pstats[Ray_Function_VM_Instruction_Est]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_PolynomTest, &pstats[Polynomials_Tested]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_RootsEliminated, &pstats[Roots_Eliminated]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_CallsToNoise, &pstats[Calls_To_Noise]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_CallsToDNoise, &pstats[Calls_To_DNoise]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_MediaSamples, &pstats[Media_Samples]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_MediaIntervals, &pstats[Media_Intervals]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_ReflectedRays, &pstats[Reflected_Rays_Traced]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_InnerReflectedRays, &pstats[Internal_Reflected_Rays_Traced]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_RefractedRays, &pstats[Refracted_Rays_Traced]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_TransmittedRays, &pstats[Transmitted_Rays_Traced]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_IStackOverflow, &pstats[Istack_overflows]);
#if defined(MEM_STATS)
   Long_To_Counter(mem_stats_smallest_alloc(), pstats[MemStat_Smallest_Alloc]);
   Long_To_Counter(mem_stats_largest_alloc(), pstats[MemStat_Largest_Alloc]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_MinAlloc, &pstats[MemStat_Smallest_Alloc]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_MaxAlloc, &pstats[MemStat_Largest_Alloc]);
#if (MEM_STATS>=2)
   Long_To_Counter(mem_stats_total_allocs(), pstats[MemStat_Total_Allocs]);
   Long_To_Counter(mem_stats_total_frees(), pstats[MemStat_Total_Frees]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_CallsToAlloc, &pstats[MemStat_Total_Allocs]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_CallsToFree, &pstats[MemStat_Total_Frees]);
#endif
   Long_To_Counter(mem_stats_largest_mem_usage(), pstats[MemStat_Largest_Mem_Usage]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_PeakMemoryUsage, &pstats[MemStat_Largest_Mem_Usage]);
#endif

   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_TraceLevel, Highest_Trace_Level);
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_MaxTraceLevel, Max_Trace_Level);
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_RadGatherCount, ra_gather_count);
   if(err == kNoErr)
      err = POVMSUtil_SetInt(msg, kPOVAttrib_RadReuseCount, ra_reuse_count);

   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_PhotonsShot, &pstats[Number_Of_Photons_Shot]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_PhotonsStored, &pstats[Number_Of_Photons_Stored]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_GlobalPhotonsStored, &pstats[Number_Of_Global_Photons_Stored]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_MediaPhotonsStored, &pstats[Number_Of_Media_Photons_Stored]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_PhotonsPriQInsert, &pstats[Priority_Queue_Add]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_PhotonsPriQRemove, &pstats[Priority_Queue_Remove]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_GatherPerformedCnt, &pstats[Gather_Performed_Count]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_GatherExpandedCnt, &pstats[Gather_Expanded_Count]);

#ifdef MECHANICS_SIMULATION_PATCH            /* Christoph Hormann September 2002 */
   if(err == kNoErr)
		  err = POVMSUtil_SetFloat(msg, kPOVAttrib_MechSimTSMin, MechsimOptions.TS_Min);
   if(err == kNoErr)
		  err = POVMSUtil_SetFloat(msg, kPOVAttrib_MechSimTSAvg, 
															 (MechsimOptions.End_Time-MechsimOptions.Start_Time)/MechsimOptions.Steps);
   if(err == kNoErr)
		  err = POVMSUtil_SetFloat(msg, kPOVAttrib_MechSimTSMax, MechsimOptions.TS_Max);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_MechSimSteps, &pstats[MechSim_Steps]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_MechSimCollMass, &pstats[MechSim_Mass_Collisions]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_MechSimCollFace, &pstats[MechSim_Face_Collisions]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_MechSimCollCon, &pstats[MechSim_Connection_Collisions]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_MechSimEnvCollisions, &pstats[MechSim_Environment_Collisions]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_MechSimEnvPenetrations, &pstats[MechSim_Environment_Penetrations]);

#endif
#ifdef RADIOSITY_STATISTICS_PATCH
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_RadGatherFinal, &pstats[Radiosity_GatherFinal]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_RadGatherFirst, &pstats[Radiosity_GatherFirst]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_RadGatherSecond, &pstats[Radiosity_GatherSecond]);
   if(err == kNoErr)
      err = AddStatistic(msg, kPOVAttrib_RadGatherThird, &pstats[Radiosity_GatherThird]);
#endif
#ifdef RADSTATS
   if(err == kNoErr)
      err = POVMSUtil_SetLong(msg, kPOVAttrib_ot_inscount, ot_inscount);
   if(err == kNoErr)
      err = POVMSUtil_SetLong(msg, kPOVAttrib_ot_nodecount, ot_nodecount);
   if(err == kNoErr)
      err = POVMSUtil_SetLong(msg, kPOVAttrib_ot_blockcount, ot_blockcount);
   if(err == kNoErr)
      err = POVMSUtil_SetLong(msg, kPOVAttrib_ot_seenodecount, ot_seenodecount);
   if(err == kNoErr)
      err = POVMSUtil_SetLong(msg, kPOVAttrib_ot_seeblockcount, ot_seeblockcount);
   if(err == kNoErr)
      err = POVMSUtil_SetLong(msg, kPOVAttrib_ot_minsize, ot_minsize);
   if(err == kNoErr)
      err = POVMSUtil_SetLong(msg, kPOVAttrib_ot_maxsize, ot_maxsize);
   if(err == kNoErr)
      err = POVMSUtil_SetLong(msg, kPOVAttrib_ot_overflows, overflows);
   if(err == kNoErr)
      err = POVMSUtil_SetLong(msg, kPOVAttrib_ot_minloops, minloops);
   if(err == kNoErr)
      err = POVMSUtil_SetLong(msg, kPOVAttrib_ot_maxloops, maxloops);
   if(err == kNoErr)
      err = POVMSUtil_SetLong(msg, kPOVAttrib_ot_inscount, ot_inscount);
   if(err == kNoErr)
      err = POVMSUtil_SetLong(msg, kPOVAttrib_ot_doblockcount, ot_doblockcount);
   if(err == kNoErr)
      err = POVMSUtil_SetLong(msg, kPOVAttrib_ot_dotokcount, ot_dotokcount);
   if(err == kNoErr)
      err = POVMSUtil_SetLong(msg, kPOVAttrib_ot_lowerrorcount, ot_lowerrorcount);
#endif

   return 0;
}


/*****************************************************************************
*
* FUNCTION
*
*   BuildProgress
*
* INPUT
*   
* OUTPUT
*   
* RETURNS
*
* AUTHOR
*
*   Thorsten Froehlich
*   
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

int BuildProgress(POVMSObjectPtr msg, int progress)
{
	int ret = kNoErr;
	DBL time_dif;

	STOP_TIME
	time_dif = TIME_ELAPSED

	ret = POVMSUtil_SetInt(msg, kPOVAttrib_TotalTime, int(time_dif));

	switch(progress)
	{
		case kPOVList_Prog_CreatingBoundingSlabs:
			break;
		case kPOVList_Prog_CreatingVistaBuffer:
			break;
		case kPOVList_Prog_CreatingLightBuffers:
			break;
		case kPOVList_Prog_BuildingPhotonMaps:
			(void)POVMSUtil_SetInt(msg, kPOVAttrib_TotalPhotonCount, GetPhotonStat(kPOVAttrib_TotalPhotonCount));
			(void)POVMSUtil_SetInt(msg, kPOVAttrib_ObjectPhotonCount, GetPhotonStat(kPOVAttrib_ObjectPhotonCount));
			(void)POVMSUtil_SetInt(msg, kPOVAttrib_MediaPhotonCount, GetPhotonStat(kPOVAttrib_MediaPhotonCount));
			(void)POVMSUtil_SetInt(msg, kPOVAttrib_PhotonXSamples, GetPhotonStat(kPOVAttrib_PhotonXSamples));
			(void)POVMSUtil_SetInt(msg, kPOVAttrib_PhotonYSamples, GetPhotonStat(kPOVAttrib_PhotonYSamples));
			break;
		case kPOVList_Prog_LoadingPhotonMaps:
			break;
		case kPOVList_Prog_SavingPhotonMaps:
			break;
		case kPOVList_Prog_SortingPhotons:
			(void)POVMSUtil_SetInt(msg, kPOVAttrib_CurrentPhotonCount, GetPhotonStat(kPOVAttrib_CurrentPhotonCount));
			(void)POVMSUtil_SetInt(msg, kPOVAttrib_TotalPhotonCount, GetPhotonStat(kPOVAttrib_TotalPhotonCount));
			break;
		case kPOVList_Prog_ReclaimingMemory:
			break;
		case kPOVList_Prog_WritingINIFile:
			break;
		case kPOVList_Prog_WritingHistogramFile:
			break;
		case kPOVList_Prog_PerformingShelloutCommand:
			break;
		case kPOVList_Prog_ResumingInterruptedTrace:
			break;
		case kPOVList_Prog_ProcessingFrame:
			(void)POVMSUtil_SetInt(msg, kPOVAttrib_CurrentFrame, opts.FrameSeq.FrameNumber - opts.FrameSeq.InitialFrame + 1);
			(void)POVMSUtil_SetInt(msg, kPOVAttrib_FrameCount, opts.FrameSeq.FinalFrame - opts.FrameSeq.InitialFrame + 1);
			(void)POVMSUtil_SetInt(msg, kPOVAttrib_AbsoluteCurFrame, opts.FrameSeq.FrameNumber);
			(void)POVMSUtil_SetFloat(msg, kPOVAttrib_FirstClock, opts.FrameSeq.InitialClock);
			(void)POVMSUtil_SetFloat(msg, kPOVAttrib_CurrentClock, opts.FrameSeq.Clock_Value);
			(void)POVMSUtil_SetFloat(msg, kPOVAttrib_LastClock, opts.FrameSeq.FinalClock);
			break;
		case kPOVList_Prog_Parsing:
			(void)POVMSUtil_SetLong(msg, kPOVAttrib_CurrentToken, Current_Token_Count);
			break;
		case kPOVList_Prog_Displaying:
			break;
		case kPOVList_Prog_Rendering:
			(void)POVMSUtil_SetInt(msg, kPOVAttrib_CurrentLine, Current_Line_Number - opts.First_Line + 1);
			(void)POVMSUtil_SetInt(msg, kPOVAttrib_AbsoluteCurrentLine, Current_Line_Number);
			(void)POVMSUtil_SetInt(msg, kPOVAttrib_LineCount, opts.Last_Line - opts.First_Line);
#ifdef RADIOSITY_STATISTICS_PATCH
			if(opts.Radiosity_Enabled)
				(void)POVMSUtil_SetInt(msg, kPOVAttrib_RadGatherCount, ra_gather_count - RadiosityCount);
#endif
			if(MosaicPreviewSize > 1)
				(void)POVMSUtil_SetInt(msg, kPOVAttrib_MosaicPreviewSize, MosaicPreviewSize);
			else
			{
				if(opts.Options & ANTIALIAS)
					(void)POVMSUtil_SetInt(msg, kPOVAttrib_SuperSampleCount, SuperSampleCount);
#ifndef RADIOSITY_STATISTICS_PATCH
				if(opts.Radiosity_Enabled)
					(void)POVMSUtil_SetInt(msg, kPOVAttrib_RadGatherCount, ra_gather_count - RadiosityCount);
#endif
			}
			break;
		case kPOVList_Prog_DoneTracing:
			break;
		case kPOVList_Prog_AbortingRender:
			break;
		case kPOVList_Prog_UserAbort:
			break;

#ifdef CLOTHRAY_PATCH
		 	case kPOVList_Prog_WritingClothrayMesh:
				(void)POVMSUtil_SetInt(msg, kPOVAttrib_ClothRayMeshWrite, GetClothrayStat(kPOVAttrib_ClothRayMeshWrite));
				break;
			case kPOVList_Prog_RunningClothSimulation:
				(void)POVMSUtil_SetInt(msg, kPOVAttrib_ClothRaySimulation, GetClothrayStat(kPOVAttrib_ClothRaySimulation));
			break;
#endif
#ifdef MECHANICS_SIMULATION_PATCH 
			case kPOVList_Prog_MechanicsSimulation:
				(void)POVMSUtil_SetInt(msg, kPOVAttrib_MechSimProgress,  (int)(100.0*max(0.0, MechsimOptions.Time-MechsimOptions.Start_Time)/(MechsimOptions.End_Time-MechsimOptions.Start_Time)));
				(void)POVMSUtil_SetFloat(msg, kPOVAttrib_MechSimTime, MechsimOptions.Time);
				if (MechsimOptions.Method>=3)
					(void)POVMSUtil_SetFloat(msg, kPOVAttrib_MechSimTS, MechsimOptions.Time_Step);
				else
					(void)POVMSUtil_SetFloat(msg, kPOVAttrib_MechSimTS, -1.0);
			break;
#endif
#ifdef POST_PROCESS_PATCH 
			case kPOVList_Prog_PostProcess:
				(void)POVMSUtil_SetInt(msg, kPOVAttrib_PostProcessPhase,      (int)Post_Process_Pass);
				(void)POVMSUtil_SetInt(msg, kPOVAttrib_PostProcessProgressX,  (int)Post_Process_X);
				(void)POVMSUtil_SetInt(msg, kPOVAttrib_PostProcessProgressY,  (int)Post_Process_Y);
			break;
#endif

	}

	return ret;
}


/*****************************************************************************
*
* FUNCTION
*
*   AddStatistic
*
* INPUT
*   
* OUTPUT
*   
* RETURNS
*
* AUTHOR
*
*   Thorsten Froehlich
*   
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

int AddStatistic(POVMSObjectPtr msg, POVMSType key, COUNTER *counter)
{
   return POVMSUtil_SetLong(msg, key, *counter);
}


/*****************************************************************************
*
* FUNCTION
*
*   AddOIStatistic
*
* INPUT
*   
* OUTPUT
*   
* RETURNS
*
* AUTHOR
*
*   Thorsten Froehlich
*   
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

int AddOIStatistic(POVMSAttributeListPtr list, int index, COUNTER *pstats)
{
   POVMSObject obj;
   int err;

   err = POVMSObject_New(&obj, kPOVObjectClass_OIStat);
   if(err == kNoErr)
      err = POVMSUtil_SetString(&obj, kPOVAttrib_ObjectName, intersection_stats[index].infotext);
   if(err == kNoErr)
      err = POVMSUtil_SetInt(&obj, kPOVAttrib_ObjectID, intersection_stats[index].povms_id);
   if(err == kNoErr)
      err = POVMSUtil_SetLong(&obj, kPOVAttrib_ISectsTests, pstats[intersection_stats[index].stat_test_id]);
   if(err == kNoErr)
      err = POVMSUtil_SetLong(&obj, kPOVAttrib_ISectsSucceeded, pstats[intersection_stats[index].stat_suc_id]);
   if(err == kNoErr)
      err = POVMSAttrList_Append(list, &obj);

   return err;
}


/*****************************************************************************
*
* FUNCTION
*
*   Send_InitInfo
*
* INPUT
*   
* OUTPUT
*   
* RETURNS
*
* AUTHOR
*
*   Thorsten Froehlich
*   
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

void Send_InitInfo()
{
   POVMSAttributeList attrlist;
   POVMSAttribute attr;
   POVMSObject msg;
   int err = kNoErr;

   if(err == kNoErr)
      err = POVMSObject_New(&msg, kPOVMSType_WildCard);

   if(err == kNoErr)
      err = POVMSUtil_SetString(&msg, kPOVAttrib_PlatformName, POVRAY_PLATFORM_NAME);
   if(err == kNoErr)
      err = POVMSUtil_SetFormatString(&msg, kPOVAttrib_CoreVersion,
                                      "Persistence of Vision(tm) Ray Tracer Version %s%s", POV_RAY_VERSION, COMPILER_VER);
   if(err == kNoErr)
      err = POVMSUtil_SetString(&msg, kPOVAttrib_EnglishText,
                                DISTRIBUTION_MESSAGE_1 "\n" DISTRIBUTION_MESSAGE_2 "\n" DISTRIBUTION_MESSAGE_3
                                "\nPOV-Ray is based on DKBTrace 2.12 by David K. Buck & Aaron A. Collins\n" POV_RAY_COPYRIGHT);
   if(err == kNoErr)
      err = POVMSUtil_SetBool(&msg, kPOVAttrib_Official, POV_RAY_IS_OFFICIAL);

   if(err == kNoErr)
      err = POVMSAttrList_New(&attrlist);
   if(err == kNoErr)
   {
      for(int i = 0; Primary_Developers[i] != NULL; i++)
      {
         err = POVMSAttr_New(&attr);
         if(err == kNoErr)
         {
            err = POVMSAttr_Set(&attr, kPOVMSType_CString, Primary_Developers[i], strlen(Primary_Developers[i]) + 1);
            if(err == kNoErr)
               err = POVMSAttrList_Append(&attrlist, &attr);
            else
               err = POVMSAttr_Delete(&attr);
         }
      }
   }
   if(err == kNoErr)
      err = POVMSObject_Set(&msg, &attrlist, kPOVAttrib_PrimaryDevs);

   if(err == kNoErr)
      err = POVMSAttrList_New(&attrlist);
   if(err == kNoErr)
   {
      for(int i = 0; Contributing_Authors[i] != NULL; i++)
      {
         err = POVMSAttr_New(&attr);
         if(err == kNoErr)
         {
            err = POVMSAttr_Set(&attr, kPOVMSType_CString, Contributing_Authors[i], strlen(Contributing_Authors[i]) + 1);
            if(err == kNoErr)
               err = POVMSAttrList_Append(&attrlist, &attr);
            else
               err = POVMSAttr_Delete(&attr);
         }
      }
   }
   if(err == kNoErr)
      err = POVMSObject_Set(&msg, &attrlist, kPOVAttrib_ContributingDevs);


#ifdef MEGAPOV_MASTER_SWITCH
   if(err == kNoErr)
      err = POVMSAttrList_New(&attrlist);
   if(err == kNoErr)
   {
      for(int i = 0; Contributing_MegaPOV[i] != NULL; i++)
      {
         err = POVMSAttr_New(&attr);
         if(err == kNoErr)
         {
            err = POVMSAttr_Set(&attr, kPOVMSType_CString, Contributing_MegaPOV[i], strlen(Contributing_MegaPOV[i]) + 1);
            if(err == kNoErr)
               err = POVMSAttrList_Append(&attrlist, &attr);
            else
               err = POVMSAttr_Delete(&attr);
         }
      }
   }
   if(err == kNoErr)
      err = POVMSObject_Set(&msg, &attrlist, kPOVAttrib_MegaPOVDevs);
#endif



   if(err == kNoErr)
      err = POVMSAttrList_New(&attrlist);
#ifndef DONT_SHOW_IMAGE_LIB_VERSIONS
   // ZLib library version and copyright notice
   if(err == kNoErr)
   {
      err = POVMSAttr_New(&attr);
      if(err == kNoErr)
      {
         const char *tempstr = pov_tsprintf("ZLib %s, Copyright 1995-1998 Jean-loup Gailly and Mark Adler", Extract_Version(zlibVersion()));
         err = POVMSAttr_Set(&attr, kPOVMSType_CString, (void *)tempstr, strlen(tempstr) + 1);
         if(err == kNoErr)
            err = POVMSAttrList_Append(&attrlist, &attr);
         else
            err = POVMSAttr_Delete(&attr);
      }
   }
   // LibPNG library version and copyright notice
   if(err == kNoErr)
   {
      err = POVMSAttr_New(&attr);
      if(err == kNoErr)
      {
         const char *tempstr = pov_tsprintf("LibPNG %s, Copyright 1998-2002 Glenn Randers-Pehrson", Extract_Version(png_get_libpng_ver(NULL)));
         err = POVMSAttr_Set(&attr, kPOVMSType_CString, (void *)tempstr, strlen(tempstr) + 1);
         if(err == kNoErr)
            err = POVMSAttrList_Append(&attrlist, &attr);
         else
            err = POVMSAttr_Delete(&attr);
      }
   }
   // LibJPEG library version and copyright notice
   if(err == kNoErr)
   {
      err = POVMSAttr_New(&attr);
      if(err == kNoErr)
      {
         const char *tempstr = pov_tsprintf("LibJPEG %s, Copyright 1998 Thomas G. Lane", Extract_Version(JVERSION));
         err = POVMSAttr_Set(&attr, kPOVMSType_CString, (void *)tempstr, strlen(tempstr) + 1);
         if(err == kNoErr)
            err = POVMSAttrList_Append(&attrlist, &attr);
         else
            err = POVMSAttr_Delete(&attr);
      }
   }
   // LibTIFF library version and copyright notice
   if(err == kNoErr)
   {
      err = POVMSAttr_New(&attr);
      if(err == kNoErr)
      {
         const char *tempstr = pov_tsprintf("LibTIFF %s, Copyright 1988-1997 Sam Leffler, 1991-1997 SGI", Extract_Version(TIFFGetVersion()));
         err = POVMSAttr_Set(&attr, kPOVMSType_CString, (void *)tempstr, strlen(tempstr) + 1);
         if(err == kNoErr)
            err = POVMSAttrList_Append(&attrlist, &attr);
         else
            err = POVMSAttr_Delete(&attr);
      }
   }
#endif
   if(err == kNoErr)
      err = POVMSObject_Set(&msg, &attrlist, kPOVAttrib_ImageLibVersions);

   if(err == kNoErr)
      err = POVMSMsg_SetupMessage(&msg, kPOVMsgClass_Miscellaneous, kPOVMsgIdent_InitInfo);
   if(err == kNoErr)
      err = POVMSMsg_SetDestinationAddress(&msg, FRONTEND_ADDRESS);

   if(err == kNoErr)
      err = POVMS_Send(POVMS_Render_Context, &msg, NULL, kPOVMSSendMode_NoReply);

   if(err != 0)
      (void)POVMS_ASSERT_OUTPUT("Sending InitInfo failed!", "povmsend.cpp", 0);
}

/*****************************************************************************
*
* FUNCTION
*
*   Send_Progress
*
* INPUT
*   
* OUTPUT
*   
* RETURNS
*
* AUTHOR
*
*   Thorsten Froehlich
*   
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

int Send_Progress(const char *statusString, int progressState)
{
	POVMSObject msg;
	int err = kNoErr;

	Previous_t = 0;

	if(err == kNoErr)
		err = POVMSObject_New(&msg, kPOVObjectClass_Progress);

	if(err == kNoErr)
		err = POVMSUtil_SetString(&msg, kPOVAttrib_EnglishText, statusString);
	if(err == kNoErr)
		err = POVMSUtil_SetBool(&msg, kPOVAttrib_ProgressStatus, false);

	if(err == kNoErr)
		err = BuildProgress(&msg, progressState);

	if(err == kNoErr)
		err = POVMSMsg_SetupMessage(&msg, kPOVMsgClass_RenderOutput, kPOVMsgIdent_Progress);
	if(err == kNoErr)
		err = POVMSMsg_SetDestinationAddress(&msg, FRONTEND_ADDRESS);

	if(err == kNoErr)
		err = POVMS_Send(POVMS_Render_Context, &msg, NULL, kPOVMSSendMode_NoReply);

	return err;
}

/*****************************************************************************
*
* FUNCTION
*
*   Send_ProgressUpdate
*
* INPUT
*   
* OUTPUT
*   
* RETURNS
*
* AUTHOR
*
*   Thorsten Froehlich
*   
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

int Send_ProgressUpdate(int progressState, int t)
{
	POVMSObject msg;
	int err = kNoErr;
	DBL time_dif;

	STOP_TIME
	time_dif = TIME_ELAPSED

	if((fabs(time_dif - Previous_t) >= t) || (t == 0))
	{
		Previous_t = time_dif;

		if(err == kNoErr)
			err = POVMSObject_New(&msg, kPOVObjectClass_Progress);

		if(err == kNoErr)
			err = POVMSUtil_SetBool(&msg, kPOVAttrib_ProgressStatus, true);

		if(err == kNoErr)
			err = BuildProgress(&msg, progressState);

		if(err == kNoErr)
			err = POVMSMsg_SetupMessage(&msg, kPOVMsgClass_RenderOutput, kPOVMsgIdent_Progress);
		if(err == kNoErr)
			err = POVMSMsg_SetDestinationAddress(&msg, FRONTEND_ADDRESS);

		if(err == kNoErr)
			err = POVMS_Send(POVMS_Render_Context, &msg, NULL, kPOVMSSendMode_NoReply);
	}

	return err;
}


/*****************************************************************************
*
* FUNCTION
*
*   Send_FrameStatistics
*
* INPUT
*   
* OUTPUT
*   
* RETURNS
*
* AUTHOR
*
*   Thorsten Froehlich
*   
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

int Send_FrameStatistics()
{
	POVMSObject msg;
	int err = kNoErr;

	if(err == kNoErr)
		err = POVMSObject_New(&msg, kPOVObjectClass_FStats);

    if(err == kNoErr)
        err = BuildRenderTime(&msg, kPOVAttrib_FrameTime, tparse_frame, tphoton_frame, trender_frame, 
#ifdef CLOTHRAY_PATCH
                              tcloth_frame,
#endif
#ifdef MECHANICS_SIMULATION_PATCH 
                              tmechsim_frame,
#endif
#ifdef POST_PROCESS_PATCH
                              tpostproc_frame,
#endif
                              tparse_frame+tphoton_frame+trender_frame
#ifdef CLOTHRAY_PATCH
                              +tcloth_frame
#endif
#ifdef MECHANICS_SIMULATION_PATCH 
                              +tmechsim_frame
#endif
#ifdef POST_PROCESS_PATCH
                              +tpostproc_frame
#endif
                              );
    if(err == kNoErr)
        err = BuildRenderTime(&msg, kPOVAttrib_AnimationTime, tparse_total, tphoton_total, trender_total,
#ifdef CLOTHRAY_PATCH
                              tcloth_total,
#endif
#ifdef MECHANICS_SIMULATION_PATCH 
                              tmechsim_total,
#endif
#ifdef POST_PROCESS_PATCH
                              tpostproc_total,
#endif
                              tparse_total+tphoton_total+trender_total
#ifdef CLOTHRAY_PATCH
                              +tcloth_total
#endif
#ifdef MECHANICS_SIMULATION_PATCH 
                              +tmechsim_total
#endif
#ifdef POST_PROCESS_PATCH
                              +tpostproc_total
#endif
                              );

	if(err == kNoErr)
		err = POVMSMsg_SetupMessage(&msg, kPOVMsgClass_RenderOutput, kPOVMsgIdent_FrameStatistics);
	if(err == kNoErr)
		err = POVMSMsg_SetDestinationAddress(&msg, FRONTEND_ADDRESS);

	if(err == kNoErr)
		err = POVMS_Send(POVMS_Render_Context, &msg, NULL, kPOVMSSendMode_NoReply);

	return err;
}


/*****************************************************************************
*
* FUNCTION
*
*   Send_ParseStatistics
*
* INPUT
*   
* OUTPUT
*   
* RETURNS
*
* AUTHOR
*
*   Thorsten Froehlich
*   
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

int Send_ParseStatistics()
{
	POVMSObject msg;
	int err = kNoErr;

	if(err == kNoErr)
		err = POVMSObject_New(&msg, kPOVObjectClass_PStats);

	if(err == kNoErr)
		err = BuildParseStatistics(&msg);

	if(err == kNoErr)
		err = POVMSMsg_SetupMessage(&msg, kPOVMsgClass_RenderOutput, kPOVMsgIdent_ParseStatistics);
	if(err == kNoErr)
		err = POVMSMsg_SetDestinationAddress(&msg, FRONTEND_ADDRESS);

	if(err == kNoErr)
		err = POVMS_Send(POVMS_Render_Context, &msg, NULL, kPOVMSSendMode_NoReply);

	return err;
}


/*****************************************************************************
*
* FUNCTION
*
*   Send_RenderStatistics
*
* INPUT
*   
* OUTPUT
*   
* RETURNS
*
* AUTHOR
*
*   Thorsten Froehlich
*   
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

int Send_RenderStatistics(bool total)
{
   POVMSObject msg;
   int err = kNoErr;

   if(err == kNoErr)
      err = POVMSObject_New(&msg, kPOVObjectClass_RStats);
   if(err == kNoErr)
   {
      if(total == true)
         err = BuildRenderStatistics(&msg, totalstats);
      else
         err = BuildRenderStatistics(&msg, stats);
   }
   if(err == kNoErr)
      err = POVMSMsg_SetupMessage(&msg, kPOVMsgClass_RenderOutput, kPOVMsgIdent_RenderStatistics);
   if(err == kNoErr)
      err = POVMSMsg_SetDestinationAddress(&msg, FRONTEND_ADDRESS);
   if(err == kNoErr)
      err = POVMS_Send(POVMS_Render_Context, &msg, NULL, kPOVMSSendMode_NoReply);

   return err;
}


/*****************************************************************************
*
* FUNCTION
*
*   Send_RenderOptions
*
* INPUT
*   
* OUTPUT
*   
* RETURNS
*
* AUTHOR
*
*   Thorsten Froehlich
*   
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

int Send_RenderOptions()
{
   POVMSObject msg;
   int err = kNoErr;

   if(err == kNoErr)
      err = POVMSObject_New(&msg, kPOVObjectClass_ROptions);
   if(err == kNoErr)
      err = BuildRenderOptions(&msg);
   if(err == kNoErr)
      err = POVMSMsg_SetupMessage(&msg, kPOVMsgClass_RenderOutput, kPOVMsgIdent_RenderOptions);
   if(err == kNoErr)
      err = POVMSMsg_SetDestinationAddress(&msg, FRONTEND_ADDRESS);
   if(err == kNoErr)
      err = POVMS_Send(POVMS_Render_Context, &msg, NULL, kPOVMSSendMode_NoReply);

   return err;
}


/*****************************************************************************
*
* FUNCTION
*
*   Send_RenderDone
*
* INPUT
*   
* OUTPUT
*   
* RETURNS
*
* AUTHOR
*
*   Thorsten Froehlich
*   
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

int Send_RenderStarted(bool continuetrace)
{
   POVMSObject msg;
   int err = kNoErr;

   err = POVMSObject_New(&msg, kPOVMSType_WildCard);
   if(err == kNoErr)
      err = POVMSObject_Copy(gStartedStreamMessage, &msg);
   if(err == kNoErr)
      err = POVMSUtil_SetBool(&msg, kPOVAttrib_ContinueTrace, continuetrace);
   if(err == kNoErr)
      err = POVMSMsg_SetupMessage(&msg, kPOVMsgClass_RenderOutput, kPOVMsgIdent_RenderStarted);
   if(err == kNoErr)
      err = POVMSMsg_SetDestinationAddress(&msg, FRONTEND_ADDRESS);
   if(err == kNoErr)
      err = POVMS_Send(POVMS_Render_Context, &msg, NULL, kPOVMSSendMode_NoReply);

   return err;
}


/*****************************************************************************
*
* FUNCTION
*
*   Send_RenderDone
*
* INPUT
*   
* OUTPUT
*   
* RETURNS
*
* AUTHOR
*
*   Thorsten Froehlich
*   
* DESCRIPTION
*
*   -
*
* CHANGES
*
*   -
*
******************************************************************************/

int Send_RenderDone(bool withtime)
{
   POVMSObject msg;
   int err = kNoErr;
   int tp, th, tr;
#ifdef CLOTHRAY_PATCH
   int tc;
#endif
#ifdef MECHANICS_SIMULATION_PATCH 
   int tm;
#endif
#ifdef POST_PROCESS_PATCH
   int tpp;
#endif

   if (trender == 0.0)
   {
     STOP_TIME

     trender = TIME_ELAPSED
   }

   tp = 
        #ifdef AVOID_TYPE_CONVERSION_WARNINGS_PATCH
        (int)
        #endif
        tparse_total;
   if(tp <= 0)
     tp = 
          #ifdef AVOID_TYPE_CONVERSION_WARNINGS_PATCH
          (int)
          #endif
          tparse;
   th = 
        #ifdef AVOID_TYPE_CONVERSION_WARNINGS_PATCH
        (int)
        #endif
        tphoton_total;
   if(th <= 0)
     th = 
          #ifdef AVOID_TYPE_CONVERSION_WARNINGS_PATCH
          (int)
          #endif
          tphoton;
#ifdef CLOTHRAY_PATCH
   tc = 
        #ifdef AVOID_TYPE_CONVERSION_WARNINGS_PATCH
        (int)
        #endif
        tcloth_total;
   if(tc <= 0)
     tc = 
          #ifdef AVOID_TYPE_CONVERSION_WARNINGS_PATCH
          (int)
          #endif
          tcloth;
#endif
#ifdef MECHANICS_SIMULATION_PATCH 
   tm = 
        #ifdef AVOID_TYPE_CONVERSION_WARNINGS_PATCH
        (int)
        #endif
        tmechsim_total;
   if(tm <= 0)
     tm = 
          #ifdef AVOID_TYPE_CONVERSION_WARNINGS_PATCH
          (int)
          #endif
          tmechsim;
#endif
#ifdef POST_PROCESS_PATCH
   tpp = 
        #ifdef AVOID_TYPE_CONVERSION_WARNINGS_PATCH
        (int)
        #endif
        tpostproc_total;
   if(tpp <= 0)
     tpp = 
          #ifdef AVOID_TYPE_CONVERSION_WARNINGS_PATCH
          (int)
          #endif
          tpostproc;
#endif
   tr = 
        #ifdef AVOID_TYPE_CONVERSION_WARNINGS_PATCH
        (int)
        #endif
        trender_total;
   if(tr <= 0)
     tr = 
          #ifdef AVOID_TYPE_CONVERSION_WARNINGS_PATCH
          (int)
          #endif
          trender;

   err = POVMSObject_New(&msg, kPOVMSType_WildCard);
   if(err == kNoErr)
      BuildRenderTime(&msg, kPOVAttrib_AnimationTime, tp, th, tr, 
#ifdef CLOTHRAY_PATCH
                      tc,
#endif
#ifdef MECHANICS_SIMULATION_PATCH 
                      tm,
#endif
#ifdef POST_PROCESS_PATCH
                      tpp,
#endif
                      tp + th + tr
#ifdef CLOTHRAY_PATCH
                      +tc
#endif
#ifdef MECHANICS_SIMULATION_PATCH 
                      +tm
#endif
#ifdef POST_PROCESS_PATCH
                      +tpp
#endif
                      );
   if(err == kNoErr)
      err = POVMSMsg_SetupMessage(&msg, kPOVMsgClass_RenderOutput, kPOVMsgIdent_RenderDone);
   if(err == kNoErr)
      err = POVMSMsg_SetDestinationAddress(&msg, FRONTEND_ADDRESS);
   if(err == kNoErr)
      err = POVMS_Send(POVMS_Render_Context, &msg, NULL, kPOVMSSendMode_NoReply);

   return err;
}

END_POV_NAMESPACE
