/*
 * $Id: FrameBuffer.java,v 1.3.4.2 2000/06/18 18:54:36 yamauchi Exp $
 *     (M\pi)^2 : Frame Buffer (in host computer)
 *	Copyright 1999 By YAMAUCHI Hitoshi 山内 斉 
 *	@author YAMAUCHI Hitoshi 山内 斉
 *
 * original source frameBuffer.h, frameBuffer.cc
 *
 * 1999年8月17日(火)
 */

package jp.ac.tohoku.is.archi.mpi2.raytrace;

import  java.io.*;

import  jp.ac.tohoku.is.archi.mpi2.net.*;
import  jp.ac.tohoku.is.archi.mpi2.obj.*;
import  jp.ac.tohoku.is.archi.mpi2.sysparam.*;
import  jp.ac.tohoku.is.archi.mpi2.util.*;

/**
 * global frame buffer
 */
public final class FrameBuffer implements Mpi2Constants {
    public final static int RED_buf 	= 0;
    public final static int GREEN_buf 	= 1;
    public final static int BLUE_buf	= 2;
    public final static int RGB_NUM	= 3;

    private  double [][] mBuffer; 	   // double であるか int が良いか? 
    private  int         mXScreenSize;	   // screen size x
    private  int         mYScreenSize;	   // 		 y
    private  boolean     mIsImmidiateOut;  // 情報を受信したらすぐに出力するか
    
    /**
     * save PBM ASCII
     *   Raw file で出したいが，binary の出力がめんどうなので．．．
     */
    private void savePBMAscii(File writefile)
    {
	PrintWriter printer = null;
        try {
	    printer = new PrintWriter(new FileOutputStream(writefile));
        }
        catch(IOException e)
        {
            throw new RuntimeException("Can not open printer stream " + e);
        }

	// output header
	printer.println("P3");
	printer.println("# CREATOR: Mpi2Java " + VERSION + " " + COPYRIGHT);
	printer.println(this.mXScreenSize + " " + this.mYScreenSize);
	printer.println("255");	// depth = 8
	
	for(int y = 0; y < this.mYScreenSize; y++)
	{
	    for(int x = 0; x < this.mXScreenSize; x++)	
	    {
		final int idx = this.getIdxFromXY(x, y);
		for(int buf = 0; buf < 3; buf++)
		{
		    double ivalraw = this.mBuffer[buf][idx];
		    if(ivalraw > MAX_INTENSITY)
		    {
			ivalraw = MAX_INTENSITY; // 輝度が最大値を越える場合
		    }else if(ivalraw < 0.0)
		    {
			ivalraw = 0.0;
		    }
		    // depth = 8,  color = 0 to 255
		    int ivalout = (int)(ivalraw * 255.0);	
		    printer.print(ivalout + " ");
		}
		if(x % 8 == 7)
		{
		    printer.print("\n");
		}
	    }
	}
	printer.close();
    }


    /**
     * 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)} の形
     */
    private void saveGFBRaw(File writefile)
    {
	PrintWriter printer = null;
        try {
	    printer = new PrintWriter(new FileOutputStream(writefile));
        }
        catch(IOException e)
        {
            throw new RuntimeException("Can not open printer stream " + e);
        }

	printer.println("GFB Raw CREATOR: mpi2 " + VERSION + " " + COPYRIGHT);
	printer.println("size " + this.mXScreenSize + " " + 
			this.mYScreenSize);

	for(int y = 0; y < this.mYScreenSize; y++)
	{
	    for(int x = 0; x < this.mXScreenSize; x++)	
	    {
		final int idx = this.getIdxFromXY(x, y);
		printer.println(this.mBuffer[RED_buf][idx]   + " " +  // R
				this.mBuffer[GREEN_buf][idx] + " " +  // G
				this.mBuffer[BLUE_buf][idx]);	      // B 
	    }
	}
	printer.close();
    }

    private int getIdxFromXY(int x, int y)
    {
	return(y * this.mYScreenSize + x);
    }

    public FrameBuffer()
    {
	this.mBuffer = new double[3][];
	this.mBuffer[RED_buf] 	= null;
	this.mBuffer[GREEN_buf]	= null;
	this.mBuffer[BLUE_buf]	= null;
	this.mXScreenSize	= -1;	  
	this.mYScreenSize	= -1;	  
	this.mIsImmidiateOut	= false;
    }

    /**
     * initialize frame buffer
     */
    public void init(int screenX, int screenY)
    {
	if((screenX <= 0) || (screenY <= 0))
	{
	    System.out.println("Error! : illegal screen size (" + screenX + 
			       ", " + screenY +  ")");
	    Mpi2CommFactory.getInstance().exit(1);
	}

	this.mXScreenSize = screenX;
	this.mYScreenSize = screenY;
	for(int buf = 0; buf < RGB_NUM; buf++)
	{
	    this.mBuffer[buf] = 
		new double [this.mXScreenSize * this.mYScreenSize];
	    for(int x = 0; x < this.mXScreenSize; x++)
	    {
		for(int y = 0; y < this.mYScreenSize; y++)
		{
		    this.mBuffer[buf][this.getIdxFromXY(x, y)] = 0.0;
		}
	    }
	}
	this.mIsImmidiateOut = false;
    }
	
    // 受信データをすぐに stdout に出力するかのフラグ操作
    public void setImmidiateOut()		
    {
	this.mIsImmidiateOut = true;	
    }
    public void unsetImmidiateOut()	
    {
	this.mIsImmidiateOut = false;	
    }
    public boolean getImmidiateOut() 	
    {
	return(this.mIsImmidiateOut);
    }
	
    /**
     * save 
     */
    public void saveData(String savefile)
    {
	FileInfo fif = FileInfo.getInstance();

	// check save filename
	if((savefile.equals(FileInfo.INIT_FILENAME)) || 
	   (savefile.equals("")))
	{
	    System.out.println("Error!:illegal save filename at FrameBuffer."+ 
			       "saveData. The name will be changed.");
	}
	File f = FileUtil.newUniqueWriteFP(savefile, ".tmp");
	
	// select save format
	switch(FileInfo.getInstance().getSaveFileFormat())
	{
	case FileInfo.FI_SAVE_PPM:
	    this.savePBMAscii(f);
	    break;
	case FileInfo.FI_SAVE_GFB:
	    this.saveGFBRaw(f);
	    break;
	default:
	    System.out.println("Error! : can not recognize file format. " + 
			       "save as gfb");
	    this.saveGFBRaw(f);
	    break;
	}
    }
	
    /**
     * add intensity
     */
    public void addIntensity(Intensity intensity_val)
    {
	int screenX   = intensity_val.getScreenX();
	int screenY   = intensity_val.getScreenY();    
	Vector3 i_val = intensity_val.peekIntensityRGB();

	if(!((0 <= screenX) && (screenX < this.mXScreenSize)))
	{
	    System.out.println("Error! : FrameBuffer.setIntensity screenX = "
			       + screenX);
	}
	if(!((0 <= screenY) && (screenY < this.mYScreenSize)))
	{
	    System.out.println("Error! : FrameBuffer.setIntensity screenY = "
			       + screenY);
	}
	
	Assert.out((0 <= screenX) && (screenX < this.mXScreenSize), 
		   "Error!: illegal screen size X");
	Assert.out((0 <= screenY) && (screenY < this.mYScreenSize), 
		   "Error!: illegal screen size Y");
	// System.out.println("DEBUG FB: " + intensity_val);
	Assert.out((i_val.get(Vector3.X) >= 0.0) && 
		   (i_val.get(Vector3.Y) >= 0.0) && 
		   (i_val.get(Vector3.Z) >= 0.0), 
		   "Error!: illegal intensity. intensity val must be >=0.0");

	final int idx = this.getIdxFromXY(screenX, screenY);
	this.mBuffer[RED_buf][idx] 	+= i_val.get(Vector3.X);
	this.mBuffer[GREEN_buf][idx] 	+= i_val.get(Vector3.Y);
	this.mBuffer[BLUE_buf][idx]  	+= i_val.get(Vector3.Z);

	if(this.mIsImmidiateOut == true)
	{
	    String s = "GFB " + screenX + " " + screenY + " " +
		this.mBuffer[RED_buf][idx] 	+ " " + 
		this.mBuffer[GREEN_buf][idx]	+ " " + 
		this.mBuffer[BLUE_buf][idx];
	    System.out.println(s);
	}
    }
}




