//------------------------------------------------------------
// $Id: frameBuffer.cc,v 1.1.1.1 1999/11/25 06:26:09 yamauchi Exp $
//------------------------------------------------------------
//	(M\pi)^2 frame buffer (in host computer)
//	1996/8/7
//	(C) 1996-1998 Copyright YAMAUCHI Hitoshi 山内 斉
//------------------------------------------------------------
#include <assert.h>
#include <iostream.h>
#include <stdio.h>
#include <stdlib.h>

#include "constants.h"
#include "frameBuffer.h"
#include "mpi2exit.h"
#include "mpi2_funcs.h"
#include "Option.h"
#include "version.h"
#include "FileInfo.h"
#include "Intensity.h"

//------------------------------------------------------------
// protected functions
//------------------------------------------------------------
// save image data by pbm raw
//------------------------------
void frameBufferClass::
savePBMRaw(FILE *pfp)
{
    assert(pfp != NULL);    
    fprintf(pfp, "P6\n");
    fprintf(pfp, "# CREATOR: mpi2 %s (C) Hitoshi Yamauchi\n", MPI2_VERSION);
    fprintf(pfp, "%d %d\n", this->mXScreenSize, this->mYScreenSize);
    fprintf(pfp, "255\n");	// depth = 8
    
    int    x, y, buf;
    unsigned char   ivalout;
    double ivalraw;
    for(y = 0; y < this->mYScreenSize; y++)
    {
	for(x = 0; x < this->mXScreenSize; x++)	
	{
	    for(buf = 0; buf < 3; buf++)
	    {
		assert(this->mppBuffer[buf][x][y] >= 0.0);
		if(this->mppBuffer[buf][x][y] > MAX_INTENSITY)
		{
		    ivalraw = MAX_INTENSITY; // 輝度が最大値を越える
		}else
		{
		    ivalraw = this->mppBuffer[buf][x][y];
		}
		// depth = 8,  color = 0 to 255
		ivalout = (unsigned char)(ivalraw * 255.0);	
		fprintf(pfp, "%c", ivalout);		   
	    }
	}
    }
}

//------------------------------
// save Global Frame Buffer Raw
//	file format (By Hitoshi Yamauchi)
// 	line 1 : GFB Raw CREATOR: mpi2 (version) (C) Hitoshi Yamauchi
//	line 2 : size X Y
// 	rest   : R G B がまず X が変化する方向に入っている C の[Y][X] 
// 		 で for(Y) {for(X)} の形
//------------------------------
void frameBufferClass::
saveGFBRaw(FILE *pfp)
{
    assert(pfp != NULL);    
    fprintf(pfp, "GFB Raw CREATOR: mpi2 %s (C) Hitoshi Yamauchi\n", 
	    MPI2_VERSION);
    fprintf(pfp, "size %d %d\n", this->mXScreenSize, this->mYScreenSize);

    int x, y;
    for(y = 0; y < this->mYScreenSize; y++)
    {
	for(x = 0; x < this->mXScreenSize; x++)	
	{
	    fprintf(pfp, "%g %g %g\n", 
		    this->mppBuffer[0][x][y],  // R
		    this->mppBuffer[1][x][y],  // G
		    this->mppBuffer[2][x][y]); // B 
	}
    }
}

//------------------------------------------------------------
// public functions
//------------------------------------------------------------
// constructor
//------------------------------
frameBufferClass::
frameBufferClass(void)
{
    this->mppBuffer[RED_buf] 	= NULL;
    this->mppBuffer[GREEN_buf]	= NULL;
    this->mppBuffer[BLUE_buf]	= NULL;
    this->mXScreenSize		= -1;	  
    this->mYScreenSize		= -1;	  
    this->mIsImmidiateOut	= NO;
}

//------------------------------
// initialize frame buffer
//------------------------------
void frameBufferClass::
init(int screenX, int screenY)
{
    if((screenX <= 0) || (screenY <= 0))
    {
	cerr << "Error! : illegal screen size (" << screenX << 
	    ", " << screenY <<  ")" << endl;
	commMpi2_exit(1);
    }
    int buf;
    for(buf = 0; buf < RGB_NUM; buf++)
    {
	if(this->mppBuffer[buf] != NULL)
	{
	    int x;
	    for(x = 0; x < this->mXScreenSize; x++)
	    {
		delete [] this->mppBuffer[buf][x];
	    }
	    delete [] this->mppBuffer[buf];	    
	    this->mppBuffer[buf] = NULL;	    
	}
    }
    this->mXScreenSize = screenX;
    this->mYScreenSize = screenY;
    for(buf = 0; buf < RGB_NUM; buf++)
    {
	this->mppBuffer[buf] = new double *[this->mXScreenSize];	    
	int x;
	for(x = 0; x < this->mXScreenSize; x++)
	{
	    this->mppBuffer[buf][x] = new double [this->mYScreenSize];
	    int y;
	    for(y = 0; y < this->mYScreenSize; y++)
	    {
		this->mppBuffer[buf][x][y] = 0.0;		
	    }
	}
    }
    this->mIsImmidiateOut = NO;
    if(Option::getInstance()->getVerboseMode() == YES)
    {
	cout << genHd(VERBOSEHEAD, HostID) << "framebuffer initialized."<<endl;
    }
}

//------------------------------
// save 
//------------------------------
void frameBufferClass::
saveData(const char *pSaveName)
{
    // check save filename
    if((pSaveName == NULL) || (pSaveName[0] == '\0'))
    {
	cerr << "Error! : illegal save filename " << 
	    "at frameBufferClass::savePBMRaw." << endl;
	commMpi2_exit(1);
    }

    FILE *pfp = getUniqueWriteFP(pSaveName, NULL, 0);

    // select save format
    switch(FileInfo::getInstance()->getSaveFileFormat())
    {
    case FileInfo::FI_SAVE_PPM:
	this->savePBMRaw(pfp);
	break;
    case FileInfo::FI_SAVE_GFB:
	this->saveGFBRaw(pfp);
	break;
    default:
	cout << "Error! : can not recognize file format. save as ppm" << endl;
	this->savePBMRaw(pfp);
	break;
    }
    fclose(pfp);
}

//------------------------------
// set intensity
//------------------------------
void frameBufferClass::
setIntensity(const IntensityClass &intensity_val)
{
    int screenX = intensity_val.getScreenX();
    int screenY = intensity_val.getScreenY();    
    Vector3 i_val = intensity_val.getIntensityRGB();

    if(!((0 <= screenX) && (screenX < this->mXScreenSize)))
    {
	cout << "Error! : frameBufferClass::setIntensity screenX = "
	    << screenX << endl;  
    }
    if(!((0 <= screenY) && (screenY < this->mYScreenSize)))
    {
	cout << "Error! : frameBufferClass::setIntensity screenY = "
	    << screenY << endl; 
    }

    assert((0 <= screenX) && (screenX < this->mXScreenSize));
    assert((0 <= screenY) && (screenY < this->mYScreenSize));
    // cout << "debug FB: " << intensity << endl;
    assert((i_val.get(Vector3::X) >= 0.0) && 
	   (i_val.get(Vector3::Y) >= 0.0) && 
	   (i_val.get(Vector3::Z) >= 0.0));

    this->mppBuffer[RED_buf][screenX][screenY]   += i_val.get(Vector3::X);
    this->mppBuffer[GREEN_buf][screenX][screenY] += i_val.get(Vector3::Y);
    this->mppBuffer[BLUE_buf][screenX][screenY]  += i_val.get(Vector3::Z);

    if(this->mIsImmidiateOut == YES)
    {
	cout << "GFB " << screenX << " " << screenY << " "; 
	cout << this->mppBuffer[RED_buf][screenX][screenY]	<< " ";
	cout << this->mppBuffer[GREEN_buf][screenX][screenY] 	<< " ";
	cout << this->mppBuffer[BLUE_buf][screenX][screenY] 	<< endl;
    }
}
