/*
 * $Id: Material.java,v 1.11 2000/06/15 13:55:51 yamauchi Exp $
 *     (M\pi)^2 : Material 
 *	Copyright 1999 By YAMAUCHI Hitoshi 山内 斉 
 *	@author YAMAUCHI Hitoshi 山内 斉
 *	@author MAEDA Atusi
 *
 * original source material.h, material.cc
 * 
 *-- 1999年7月28日(水) 本日より前田さんがこの研究に加わった．
 *-- すばらしい．
 */

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

import java.io.Serializable;
import java.lang.String;

import jp.ac.tohoku.is.archi.mpi2.jdk12src.Comparable;
import jp.ac.tohoku.is.archi.mpi2.util.Vector3;
import jp.ac.tohoku.is.archi.mpi2.util.Encodable;
import jp.ac.tohoku.is.archi.mpi2.util.ObjectDecoder;

/**
 * <code>Material<code> class represents object material information 
 * such as diffuse reflection coefficient, specular reflection
 * coefficient, transparency, and so on.
 *
 * @author 	YAMAUCHI Hitoshi (山内斉)
 */
public final class Material 
implements Comparable, Encodable
{
    // effective name length : 有効な名前長 送受信するので固定長が便利．
    private static final int EFFECTIVE_NAME_LEN = 64;
    
    // member
    private String  mName;		// material name (Length 64 まで認識)
    private Vector3 mDiffRef;		// diffuse reflectance
    private Vector3 mDiffTrans;		// diffuse transmittance
    private Vector3 mDiffEmit;		// diffuse emittance
    private double  mSpecRefCosN; 	// Phone の cos^n low (未使用)
    private Vector3 mSpecRef;		// specular reflectance
    private double  mSpecTransCosN;	// specular roughness (未使用) 0 で鏡面
    private Vector3 mSpecTrans;		// specular transmittance (透過率)
    private double  mIdxRefraction;	// index of refraction (屈折率)
					// ies (配向データ: 未実装)
    private double  mFrustumHalfTopRad; // 2次光の frustum の頂角の半分(radian)
    
    private int     mID;	// 共有のためのID:このIDを使って送信，共有する

    // illegal name
    public final static String NULLMatName = "NullMaterial";     
    
    /**
     * Allocates a new Material
     */
    public Material()
    {
	this.mName 		= new String(NULLMatName);
	this.mDiffRef		= new Vector3();
	this.mDiffTrans		= new Vector3();
	this.mDiffEmit		= new Vector3();
	this.mSpecRefCosN	= -1.0;
	this.mSpecRef		= new Vector3();
	this.mSpecTransCosN	= -1.0;
	this.mSpecTrans		= new Vector3();
	this.mIdxRefraction	= -1.0;
	this.mFrustumHalfTopRad = -1.0;
	this.mID		= -1;
    }

    /**
     * Copy constructor 
     */
    public Material(Material rhs)
    {
	this.mName 		= rhs.mName;
	this.mDiffRef		= new Vector3(rhs.mDiffRef);
	this.mDiffTrans		= new Vector3(rhs.mDiffTrans);
	this.mDiffEmit		= new Vector3(rhs.mDiffEmit);
	this.mSpecRefCosN	= rhs.mSpecRefCosN;
	this.mSpecRef		= new Vector3(rhs.mSpecRef);
	this.mSpecTransCosN	= rhs.mSpecTransCosN;
	this.mSpecTrans		= new Vector3(rhs.mSpecTrans);
	this.mIdxRefraction	= rhs.mIdxRefraction;
	this.mFrustumHalfTopRad = rhs.mFrustumHalfTopRad;
	this.mID		= rhs.mID;
    }

    /**
     * set material name
     */
    public void setName(String name)
    {
	this.mName = name;
    }
    /** 
     * peek material name
     */
    public String peekName()
    { 
	return(this.mName);		
    }

    /**
     * set diffuse reflection coefficient
     */
    public void setDiffRef(Vector3 aDiffRef)
    {
	this.mDiffRef = aDiffRef;
    }
    /**
     * peek diffuse reflection coefficient
     */
    public Vector3 peekDiffRef()
    { 
	return(this.mDiffRef);	
    }

    /**
     * set diffuse transparency 
     */
    public void setDiffTrans(Vector3 aDiffTrans)
    {
	this.mDiffTrans = aDiffTrans;
    }
    /**
     * peek diffuse transparency
     */
    public Vector3 getDiffTrans()
    { 
	return(this.mDiffTrans);
    }

    /**
     * set diffuse emmitance
     */
    public void setDiffEmit(Vector3 aDiffEmit)
    {
	this.mDiffEmit = aDiffEmit;
    }
    /**
     * peek diffuse emmitance
     */
    public Vector3 peekDiffEmit()
    {
	return(this.mDiffEmit);	
    }

    /**
     * set specular reflection cos^n 
     */
    public void setSpecRefCosN(double aSpecRefCosN)
    {
	this.mSpecRefCosN = aSpecRefCosN;
    }
    /**
     * get specular reflection cos^n 
     */
    public double  getSpecRefCosN()
    { 
	return(this.mSpecRefCosN);	
    }

    /**
     * set specular reflection coefficient
     */
    public void setSpecRef(Vector3 aSpecRef)
    {
	this.mSpecRef = aSpecRef;
    }
    /**
     * peek specular reflection coefficient
     */
    public Vector3 peekSpecRef() 	
    { 
	return(this.mSpecRef);	
    }

    /**
     * set specular transparency cos^n
     */
    public void setSpecTransCosN(double aSpecTransCosN)
    {
	this.mSpecTransCosN = aSpecTransCosN;
    }
    /**
     * get specular transparency cos^n
     */
    public double  getSpecTransCosN() 
    { 
	return(this.mSpecTransCosN);	
    }

    /**
     * set specular transparency 
     */
    public void setSpecTrans(Vector3 aSpecTrans)
    {
	this.mSpecTrans = aSpecTrans;
    }
    /**
     * peek specular transparency 
     */
    public Vector3 peekSpecTrans() 	
    { 
	return(this.mSpecTrans);	
    }

    /**
     * set IdxRefraction
     */
    public void setIdxRefraction(double aIdxRefraction)
    {
	this.mIdxRefraction = aIdxRefraction;
    }
    /**
     * get IdxRefraction
     */
    public double  getIdxRefraction() 
    { 
	return(this.mIdxRefraction);	
    }

    /**
     * set Id 
     */
    public void setID(int id)
    {
	this.mID = id;
    }
    /**
     * get Id 
     */
    public int  getID() 
    { 
	return(this.mID);	
    }

    /**
     * set frustum top radian of half 
     */
    public void setFrustumHalfTopRad(double aFrustumHalfTopRad)
    {
	this.mFrustumHalfTopRad = aFrustumHalfTopRad;
    }

    /**
     * get frustum top radian of half 
     */
    public double getFrustumHalfTopRad()
    {
	return(this.mFrustumHalfTopRad);
    }

    /**
     * is nullmaterial
     */
    public boolean isNullMaterial()
    {
	return(this.mName.equals(NULLMatName));
    }

    /**
     * to string 
     */
    public String toString()
    {
	String ret = 
	    "# [material name] "     + this.mName        + "\n" +
	    "rd " + this.mName + " " + this.mDiffRef	 + "\n" +
	    "td " + this.mName + " " + this.mDiffTrans   + "\n" +
	    "ed " + this.mName + " " + this.mDiffEmit	 + "\n" +
	    "rs " + this.mName + " " + this.mSpecRefCosN + " "  + 
				       this.mSpecRef 	 + "\n" +
	    "ts " + this.mName + " " + this.mSpecTransCosN + " "+ 
				       this.mSpecTrans   + "\n" +
	    "ir " + this.mName + " " + this.mIdxRefraction + "\n" +
	    "fhtrad " + this.mName + " " + this.mFrustumHalfTopRad + "\n" +
	    "matID " + this.mName  + " " + this.mID;
	return(ret);
    }	

    /**
     * to string with headder
     */
    public String toStringWithHead(final String head)
    {
	String ret = 
	    head + "# [material name] "     + this.mName         + "\n" +
	    head + "rd " + this.mName + " " + this.mDiffRef	 + "\n" +
	    head + "td " + this.mName + " " + this.mDiffTrans    + "\n" +
	    head + "ed " + this.mName + " " + this.mDiffEmit	 + "\n" +
	    head + "rs " + this.mName + " " + this.mSpecRefCosN  + " "  + 
	    this.mSpecRef 	 + "\n" +
	    head + "ts " + this.mName + " " + this.mSpecTransCosN+ " "+ 
	    this.mSpecTrans   + "\n" +
	    head + "ir " + this.mName + " " + this.mIdxRefraction+ "\n" +
	    head + "fhtrad " + this.mName+" "+ this.mFrustumHalfTopRad + "\n" +
	    head + "matID "  + this.mName  + " " + this.mID;
	return(ret);
    }	

    /**
     * implements Comparable
     */
    public int compareTo(Object obj)
    {
	if(obj instanceof Material)
	{
	    return(this.mName.compareTo(((Material)obj).mName));
	}else
	{
	    throw new 
		ClassCastException("cannot compare with no Material obj");
	}
    }

  // For Encodable interface (and ObjectDecoder object).
  final static int vectorSize = Vector3.ZERO.sizeInDouble();
  final static int doubleSize =
  EFFECTIVE_NAME_LEN		// for mName
    + 4				// for mSpecRefCosN, mSpecTransCosN,
				//     mIdxRefraction, mFrustumHalfTopRad
    + 5 * vectorSize		// for five Vector3's.
    + 1;			// for material ID
  public int sizeInDouble() {
    return doubleSize;
  }
  public void encodeInDouble(double[] array, int offset) {
      /**
    byte[] bytes = new byte[16];
    byte[] nameBytes = mName.getBytes();
    System.arraycopy(nameBytes, 0, bytes, 0, nameBytes.length);
    long l = ((long)bytes[0] << 56) + ((long)bytes[1] << 48) +
      ((long)bytes[2] << 40) + ((long)bytes[3] << 32) +
      ((long)bytes[4] << 24) + ((long)bytes[5] << 16) +
      ((long)bytes[6] << 8) + (long)bytes[7];
    array[offset] = Double.longBitsToDouble(l);
    l = ((long)bytes[8] << 56) + ((long)bytes[9] << 48) +
      ((long)bytes[10] << 40) + ((long)bytes[11] << 32) +
      ((long)bytes[12] << 24) + ((long)bytes[13] << 16) +
      ((long)bytes[14] << 8) + (long)bytes[15];
    array[offset + 1] = Double.longBitsToDouble(l);
    offset += 2;
    */
    final char space = ' ';
    for(int i = 0; i < EFFECTIVE_NAME_LEN; i++)
    {
	if(i < this.mName.length())
	{
	    array[offset + i] = (double)(this.mName.charAt(i));
	}else
	{
	    array[offset + i] = (double)space;
	}
    }
    offset += EFFECTIVE_NAME_LEN;


    mDiffRef.encodeInDouble(array, offset);
    offset += vectorSize;

    mDiffTrans.encodeInDouble(array, offset);
    offset += vectorSize;

    mDiffEmit.encodeInDouble(array, offset);
    offset += vectorSize;

    array[offset++] = mSpecRefCosN;
    mSpecRef.encodeInDouble(array, offset);
    offset += vectorSize;

    array[offset++] = mSpecTransCosN;
    mSpecTrans.encodeInDouble(array, offset);
    offset += vectorSize;

    array[offset++] = mIdxRefraction;
    array[offset++] = mFrustumHalfTopRad;

    array[offset++] = (double)(this.mID);
  }
  static class MaterialObjectDecoder implements ObjectDecoder {
    public int sizeInDouble() {
	return doubleSize;
    }
    public Object decodeFromDouble(double[] array, int offset) {
      Material m = new Material();
	  /*	  
      char[] chars = new char[16];
      long l = Double.doubleToLongBits(array[offset]);
      chars[0] = (char)((l >> 56) & 0xff);
      chars[1] = (char)((l >> 48) & 0xff);
      chars[2] = (char)((l >> 40) & 0xff);
      chars[3] = (char)((l >> 32) & 0xff);
      chars[4] = (char)((l >> 24) & 0xff);
      chars[5] = (char)((l >> 16) & 0xff);
      chars[6] = (char)((l >>  8) & 0xff);
      chars[7] = (char)(l & 0xff);
      l = Double.doubleToLongBits(array[offset + 1]);
      chars[8] = (char)((l >> 56) & 0xff);
      chars[9] = (char)((l >> 48) & 0xff);
      chars[10] = (char)((l >> 40) & 0xff);
      chars[11] = (char)((l >> 32) & 0xff);
      chars[12] = (char)((l >> 24) & 0xff);
      chars[13] = (char)((l >> 16) & 0xff);
      chars[14] = (char)((l >>  8) & 0xff);
      chars[15] = (char)(l & 0xff);
      m.mName = new String(chars).trim();
      offset += 2;
      */
      char[] chars = new char[EFFECTIVE_NAME_LEN];
      for(int i = 0; i < EFFECTIVE_NAME_LEN; i++)
      {
	  chars[i] = (char)(array[offset + i]);
      }
      m.mName = new String(chars).trim();
      offset += EFFECTIVE_NAME_LEN;

      ObjectDecoder vod = Vector3.getObjectDecoder();
      m.mDiffRef = (Vector3)vod.decodeFromDouble(array, offset);
      offset += vectorSize;

      m.mDiffTrans = (Vector3)vod.decodeFromDouble(array, offset);
      offset += vectorSize;

      m.mDiffEmit = (Vector3)vod.decodeFromDouble(array, offset);
      offset += vectorSize;

      m.mSpecRefCosN = array[offset++];
      m.mSpecRef = (Vector3)vod.decodeFromDouble(array, offset);
      offset += vectorSize;

      m.mSpecTransCosN = array[offset++];
      m.mSpecTrans = (Vector3)vod.decodeFromDouble(array, offset);
      offset += vectorSize;

      m.mIdxRefraction = array[offset++];
      m.mFrustumHalfTopRad = array[offset++];
      
      m.mID = (int)(array[offset++]);
      
      return m;
    }
  }
  private final static ObjectDecoder odInstance
    = new MaterialObjectDecoder();
  public static ObjectDecoder getObjectDecoder() {
    return odInstance;
  }
}
