/****************************************************************************
 *                  postproc.cpp
 *
 *  Author: Wlodzimierz ABX Skiba (abx@abx.art.pl)
 *
 * 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: postproc.cpp,v $
 * $Revision: 1.18 $
 * $Author: chris $
 *
 *****************************************************************************/

#include "frame.h"
#include "vector.h"
#include "povray.h"
#include "function.h"
#include "fpmetric.h"
#include "renderio.h"
#include "pov_util.h"
#include "statspov.h"
#include "vbuffer.h"
#include "colour.h"
#include "fnpovfpu.h"
#include "povmsend.h"

#ifdef POST_PROCESS_PATCH

#include "postproc.h"

BEGIN_POV_NAMESPACE

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

#define PP_DATA_EXTENSION ".ppd"
/*****************************************************************************
* Local preprocessor defines
******************************************************************************/
const unsigned int FIRST_INTERNAL_PP_FUNCTION = 79
                                 #ifdef F_TRIANGLE_PATCH
                                   + 1
                                 #endif
                                 ;
const unsigned int LAST_INTERNAL_PP_FUNCTION  = FIRST_INTERNAL_PP_FUNCTION + PP_LAST_INFO + 3;
const char * pp_postfix[PP_LAST_INFO] =
{
  "red"   ,
  "green" ,
  "blue"  ,
  "alpha" ,
  "ix"    ,
  "iy"    ,
  "iz"    ,
  "nx"    ,
  "ny"    ,
  "nz"    ,
  "pnx"   ,
  "pny"   ,
  "pnz"   ,
  "depth" ,
  "u"     ,
  "v"
};

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

POST_PROCESS * First_Post_Process;
int Post_Process_Index;
int Post_Process_Pass;
int Post_Process_X;
int Post_Process_Y;

/*****************************************************************************
* Static variables
******************************************************************************/
static IOBase * PP_Files[PP_LAST_INFO];
static char * pp_names[PP_LAST_INFO];



/*****************************************************************************
*
* FUNCTION
*
*   Create_Post_Process
*
* DESCRIPTION
*
*   Memory allocation for structure and initialization of all fields.
*
******************************************************************************/

POST_PROCESS *Create_Post_Process()
{
  POST_PROCESS *New=(POST_PROCESS *)POV_MALLOC(sizeof (POST_PROCESS), "post_process block");

  New->next = NULL;
  New->prev = NULL;
  New->Order = 0;
  New->Function[PP_COLOUR_RED] = NULL;
  New->Function[PP_COLOUR_GREEN] = NULL;
  New->Function[PP_COLOUR_BLUE] = NULL;
  New->Function[PP_COLOUR_ALPHA] = NULL;
  New->Save_File_Name = NULL;
  New->time = 0.0;

  return New;
};

/*****************************************************************************
*
* FUNCTION
*
*   Destroy_Post_Process
*
* DESCRIPTION
*
*   Unlock memory for structure and its components.
*
******************************************************************************/

void Destroy_Post_Process(POST_PROCESS **Post_Process)
{
  POST_PROCESS *Next_Post_Process;
  POST_PROCESS *Current_Post_Process;

  Current_Post_Process = *Post_Process;
  while(Current_Post_Process)
  {
    Destroy_Function(Current_Post_Process->Function[PP_COLOUR_RED]);
    Destroy_Function(Current_Post_Process->Function[PP_COLOUR_GREEN]);
    Destroy_Function(Current_Post_Process->Function[PP_COLOUR_BLUE]);
    Destroy_Function(Current_Post_Process->Function[PP_COLOUR_ALPHA]);

    Next_Post_Process = Current_Post_Process->next;

    if(Current_Post_Process->Save_File_Name!=NULL)
    {
      POV_FREE(Current_Post_Process->Save_File_Name);
      Current_Post_Process->Save_File_Name = NULL;
    }
    POV_FREE(Current_Post_Process);
    Current_Post_Process = NULL;
    Current_Post_Process = Next_Post_Process;
  }
  *Post_Process = NULL;
};

/*****************************************************************************
*
* FUNCTION
*
*   Post_Process_Initialize_Info
*
* DESCRIPTION
*
*   Writes 0.0 to every entry
*
******************************************************************************/

void Post_Process_Initialize_Info(DBL *PP_Info)
{
  if(PP_Info)
    for (int i=0; i<PP_LAST_INFO; i++)
      PP_Info[i] = ( i == PP_DEPTH ? Max_Distance : 0.0);
}

/*****************************************************************************
*
* FUNCTION
*
*   Post_Process_Intersection_Info
*
* DESCRIPTION
*
*   Stores intersection components as post_processing data.
*
******************************************************************************/

void Post_Process_Intersection_Info(DBL *PP_Info, INTERSECTION *Best_Intersection)
{
  PP_Info[PP_IPOINT_X]=Best_Intersection->IPoint[X];
  PP_Info[PP_IPOINT_Y]=Best_Intersection->IPoint[Y];
  PP_Info[PP_IPOINT_Z]=Best_Intersection->IPoint[Z];
  PP_Info[PP_INORMAL_X]=Best_Intersection->INormal[X];
  PP_Info[PP_INORMAL_Y]=Best_Intersection->INormal[Y];
  PP_Info[PP_INORMAL_Z]=Best_Intersection->INormal[Z];
  PP_Info[PP_PNORMAL_X]=Best_Intersection->PNormal[X];
  PP_Info[PP_PNORMAL_Y]=Best_Intersection->PNormal[Y];
  PP_Info[PP_PNORMAL_Z]=Best_Intersection->PNormal[Z];
  PP_Info[PP_DEPTH]=Best_Intersection->Depth;
  PP_Info[PP_U]=Best_Intersection->Iuv[U];
  PP_Info[PP_V]=Best_Intersection->Iuv[V];
}

/*****************************************************************************
*
* FUNCTION
*
*   Post_Process_Colour_Info
*
* DESCRIPTION
*
*   Stores colour components as post_processing data.
*
******************************************************************************/

void Post_Process_Colour_Info(DBL *PP_Info, COLOUR Colour)
{
  PP_Info[PP_COLOUR_RED]=Colour[pRED];
  PP_Info[PP_COLOUR_GREEN]=Colour[pGREEN];
  PP_Info[PP_COLOUR_BLUE]=Colour[pBLUE];
  PP_Info[PP_COLOUR_ALPHA]=Colour[pTRANSM];
}

/*****************************************************************************
*
* FUNCTION
*
*   Initialize_Post_Processing
*
* DESCRIPTION
*
*   NULL values for files and filenames. Has to be executed before each parsing.
*
******************************************************************************/

void Initialize_Post_Processing()
{
  for (int i=0; i<PP_LAST_INFO; i++)
  {
    pp_names[ i ] = NULL;
    PP_Files[ i ] = NULL;
  }
  First_Post_Process = NULL;
  Post_Process_Index = 0;
  Post_Process_Pass = 0;
  Post_Process_X = 0;
  Post_Process_Y = 0;
}

/*****************************************************************************
*
* FUNCTION
*
*   Initialize_Post_Processing_Cache
*
* DESCRIPTION
*
*   Opens cache for one component of post_process data.
*   Has to be executed when function{internal(post_process_function)} appear in script.
*
******************************************************************************/

void Initialize_Post_Processing_Cache(int component)
{
  char File_Name[256];

  if((pp_names[component]==NULL)&&(PP_Files[component]==NULL))
  {
    strcpy(File_Name, opts.Output_Numbered_Name);
    strcat(File_Name, ".");
    strcat(File_Name, pp_postfix[ component ]);
    strcat(File_Name, PP_DATA_EXTENSION);

    pp_names[component] = (char *) POV_MALLOC( strlen( File_Name ) + 1, "filename" );
    strcpy(pp_names[component],File_Name);

    if ((PP_Files[component] = New_OStream(File_Name, POV_File_Unknown, false)) == NULL)
    {
      Error("Cannot save post processing data to %s", File_Name );
    }
  }
}

/*****************************************************************************
*
* FUNCTION
*
*   Reinitialize_Post_Processing
*
* DESCRIPTION
*
*   Closes opened caches and reopens it for reading.
*   Has to be executed after rendering and before postprocessing
*
******************************************************************************/

void Reinitialize_Post_Processing()
{
  int i;
  unsigned long correct_size=(opts.Last_Line-opts.First_Line)*(opts.Last_Column-opts.First_Column)*sizeof(DBL);

  for (i=0; i<PP_LAST_INFO; i++)
    if( PP_Files[i] != NULL )
      {
        // change output to input
        delete PP_Files[i];
        if ((PP_Files[i] = New_IStream(pp_names[i], POV_File_Unknown)) == NULL)
        {
          Error("Cannot open post processing data from %s", pp_names[i] );
        }
        // test for correct number of outputed files
        if ( PP_Files[i]->fsize() != correct_size)
          Error("Wrong size of post processing data file");
      }
}

/*****************************************************************************
*
* FUNCTION
*
*   Deinitialize_Post_Processing
*
* DESCRIPTION
*
*   Closes cache files and deletes files.
*   Has to be executed after post processing
*
******************************************************************************/

void Deinitialize_Post_Processing()
{
  int i;

  for (i=0; i<PP_LAST_INFO; i++)
    if( PP_Files[i] != NULL )
      {
        delete PP_Files[i];
        PP_Files[i]=NULL;
        if( pp_names[i] )
        {
       	  DELETE_FILE( pp_names[i] );
          POV_FREE( pp_names[i] );
        }
        pp_names[i] = NULL;
      }
  if(First_Post_Process)
  {
    Destroy_Post_Process(&First_Post_Process);
    First_Post_Process = NULL;
    Post_Process_Index = 0;
  }
}

/*****************************************************************************
*
* FUNCTION
*
*   Save_Post_Process_Info
*
* DESCRIPTION
*
*   Stores components in caches.
*   Has to be executed once per pixel.
*
******************************************************************************/

void Save_Post_Process_Info(DBL *PP_Info)
{
  if(PP_Info)
    for (int i=0; i<PP_LAST_INFO; i++)
      if( PP_Files[ i ] != NULL )
        PP_Files[ i ]->write( & ( PP_Info[ i ] ) , sizeof( PP_Info[ i ] ) );
}

/*****************************************************************************
*
* FUNCTION
*
*   Read_Output
*
* DESCRIPTION
*
*   Reads output of rendering as one of colour or intersection components
*
******************************************************************************/

DBL Read_Output(DBL x, DBL y, int Component)
{
  if(Post_Process_Index==0)
    Error("Post process related functions can't be used before post processing");
  if(PP_Files[Component]==NULL)
    Error("Not cached component used for post processing");
  if((x<0.0)||(x>=1.0)||(y<0.0)||(y>=1.0))
    return 0.0;

  DBL Value=0.0;
  int ix = (int) floor( x * (DBL) Frame.Screen_Width );
  int iy = (int) floor( y * (DBL) Frame.Screen_Height );
  unsigned long pos = opts.Last_Column - opts.First_Column ;
  pos *= iy - opts.First_Line;
  pos += ix - opts.First_Column;
  pos *= sizeof( Value );
  PP_Files[Component]->seekg(pos,SEEK_SET);
  PP_Files[Component]->read( (char *) &Value , sizeof( Value ) );
  return Value;
}

/*****************************************************************************
*
* FUNCTION
*
*   Read_Post_Process
*
* DESCRIPTION
*
*   Reads output of post_processes as one of colour components
*
******************************************************************************/

DBL Read_Post_Process(DBL x, DBL y, int PP_Index, int Component)
{
  POST_PROCESS **Post_Process_Cursor = &First_Post_Process;
  int prev_order = Post_Process_Index;
  UV_VECT uv_screen;
  DBL Value;

  if(PP_Index<0)
    PP_Index += Post_Process_Index;
  if(PP_Index==0)
    return Read_Output(x,y,Component);
  while((*Post_Process_Cursor)&&((*Post_Process_Cursor)->Order!=PP_Index))
    Post_Process_Cursor = &((*Post_Process_Cursor)->next);
  if(((*Post_Process_Cursor)==NULL)||
     (PP_Index<0)||
     (PP_Index>=Post_Process_Index))
    Error("Wrong refference to post process");
  Make_UV_Vector(uv_screen,x,y);
  Post_Process_Index = (*Post_Process_Cursor)->Order;
  Value = Evaluate_Function_UV( *((*Post_Process_Cursor)->Function[Component]), uv_screen),
  Post_Process_Index = prev_order;
  return Value;
}

/*****************************************************************************
*
* FUNCTION
*
*   Post_Process_Loop
*
* DESCRIPTION
*
*   Calculates all pixel for specified post process
*   Not null with not empty Save_File_Name have to be passed
*   NULL checking for above should be done outside
*
******************************************************************************/

void Post_Process_Loop(int &Post_Process_Pass,POST_PROCESS *Rendered_Post_Process)
{
  int x;
  int skip_lines;
  int skip_odd_lines;
  UV_VECT uv_screen;

  if(Rendered_Post_Process->Save_File_Name)
    open_output_file(Rendered_Post_Process->Save_File_Name);

  /* Odd/even line tracing depends on the frame number. */

  skip_odd_lines = !(((opts.FrameSeq.FrameNumber % 2)==1) ^ opts.FrameSeq.Odd_Field_Flag);

  /* Field rendering starts on an odd or even line. */

  skip_lines = (opts.FrameSeq.Field_Render_Flag) && !(opts.Options & ANTIALIAS);

  /* Loop over all rows. */

  for (Current_Line_Number = opts.First_Line; Current_Line_Number < opts.Last_Line; Current_Line_Number++)
  {
    uv_screen[V] = ((DBL)Current_Line_Number + .5 )/Frame.Screen_Height;

    /* Skip odd or even lines depending on the line number. */

    if ((skip_lines) && ((Current_Line_Number % 2) == skip_odd_lines))
    {
      /* Write previous line again. */

      if (Current_Line_Number > opts.First_Line)
      {
        output_single_image_line_with_alpha_correction(Previous_Line, Current_Line_Number);
      }
      else
      {
        POV_WRITE_LINE (Previous_Line, Current_Line_Number)
      }

      continue;
    }

    Post_Process_Y = Current_Line_Number;
    Post_Process_X = opts.First_Column;
    Send_ProgressUpdate(PROGRESS_POST_PROCESS);
    Do_Cooperate(0);

    /* Loop over all columns. */

    for (x = opts.First_Column; x < opts.Last_Column; x++)
    {
      Post_Process_X = x;

      /* Check for user abort. */

      Check_User_Abort(false);

      /* Get current pixel post_processed */

      uv_screen[U] = ((DBL)x + .5 )/Frame.Screen_Width;

      /* Get current pixel post_processed */

      Post_Process_Index = Rendered_Post_Process->Order;
      Make_ColourA(
        Current_Line[x],
        Evaluate_Function_UV( *(Rendered_Post_Process->Function[PP_COLOUR_RED]), uv_screen),
        Evaluate_Function_UV( *(Rendered_Post_Process->Function[PP_COLOUR_GREEN]), uv_screen),
        Evaluate_Function_UV( *(Rendered_Post_Process->Function[PP_COLOUR_BLUE]), uv_screen),
        0,
        Evaluate_Function_UV( *(Rendered_Post_Process->Function[PP_COLOUR_ALPHA]), uv_screen)
      );
      // Clip_Colour( Current_Line[x] , Current_Line[x] );
      Post_Process_Index = 0;

      /* Display pixel. */
      plot_pixel(x, Current_Line_Number, Current_Line[x]);
      POV_ASSIGN_PIXEL (x, Current_Line_Number, Current_Line [x])
    }

    /* Write current row to disk. */

    output_prev_image_line_and_advance(Current_Line_Number);
  }

  /* Write last row to disk. */

  if (opts.Last_Line != opts.First_Line)
  {
    output_single_image_line_with_alpha_correction(Previous_Line,opts.Last_Line - 1);
  }

  /* close output file */
  if(Output_File != NULL)
  {
    delete Output_File;
    Output_File = NULL;
  }
}

END_POV_NAMESPACE

#endif
