/*
 * $RCSfile: HiResCoord.java,v $
 *
 * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
 *
 * Use is subject to license terms.
 *
 * $Revision: 1.4 $
 * $Date: 2007/02/09 17:18:02 $
 * $State: Exp $
 */

package javax.media.j3d;

import java.lang.Integer;
import javax.vecmath.*;

/**
 * High resolution coordinate object.
 *
 */

/** 
 * The HiResCoord object specifies the location of scene
 * components within the Virtual Universe.
 * The coordinates of all scene graph objects are relative to
 * the HiResCoord of the Locale in which they are contained.
 * <P>
 * The HiResCoord defines a point using a set of three
 * high-resolution coordinates, each of which consists of three
 * two's-complement fixed-point numbers.
 * Each high-resolution number consists of 256 total bits with a
 * binary point at bit 128, or between the integers at index
* 3 and 4. A high-resolution coordinate of 1.0 is defined to be exactly
 * 1 meter. This coordinate system is sufficient to describe a
 * universe in excess of several billion light years across, yet
 * still define objects smaller than a proton.
 * <P>
 * Java 3D uses integer arrays of length
 * eight to define or extract a single 256-bit coordinate value.
 * Java 3D interprets the integer at index 0 as the 32
 * most-significant bits and the integer at index 7 as the 32
 * least-significant bits.
 */

public class HiResCoord {
    /**
     * The eight-element array containing the high resolution coordinate's
     * x value.
     */
    int		x[];

    /**
     * The eight-element array containing the high resolution coordinate's
     * y value.
     */
    int		y[];

    /**
     * The eight-element array containing the high resolution coordinate's
     * z value.
     */
    int		z[];

private double scales[] = { 
      79228162514264337593543950336.0,                                             // 2^96	 
      18446744073709551616.0,                                                      // 2^64	 
      4294967296.0,                                                                // 2^32	
      1.0,                                                                         // 2^0
      2.3283064365386962890625e-10,                                                // 2^-32
      5.421010862427522170037264004349708557128906250000000000000000e-20,          // 2^-64
      1.26217744835361888865876570445245796747713029617443680763244628906250e-29,  // 2^-96
      2.938735877055718769921841343055614194546663891930218803771879265696043148636817932128906250e-39 };



    /** 
     * Constructs and initializes a new HiResCoord using the values
     * provided in the argument.
     * The HiResCoord represents 768 bits of floating point 3-Space.
     * @param X an eight element array specifying the x position
     * @param Y an eight element array specifying the y position
     * @param Z an eight element array specifying the z position
     */
    public HiResCoord(int[] X, int[] Y, int[] Z) {
    int i;

	this.x = new int[8];
	this.y = new int[8];
	this.z = new int[8];

        for(i=0;i<8;i++) {
           this.x[i] = X[i];
           this.y[i] = Y[i];
           this.z[i] = Z[i];
        }

    }

    /** 
     * Constructs and initializes a new HiResCoord using the values
     * provided in the argument.
     * The HiResCoord represents 768 bits of floating point 3-Space.
     * @param hc the HiResCoord to copy
     */
    public HiResCoord(HiResCoord hc) {
	this.x = new int[8];
	this.y = new int[8];
	this.z = new int[8];

	this.x[0] = hc.x[0];
	this.y[0] = hc.y[0];
	this.z[0] = hc.z[0];

	this.x[1] = hc.x[1];
	this.y[1] = hc.y[1];
	this.z[1] = hc.z[1];

	this.x[2] = hc.x[2];
	this.y[2] = hc.y[2];
	this.z[2] = hc.z[2];

	this.x[3] = hc.x[3];
	this.y[3] = hc.y[3];
	this.z[3] = hc.z[3];

	this.x[4] = hc.x[4];
	this.y[4] = hc.y[4];
	this.z[4] = hc.z[4];

	this.x[5] = hc.x[5];
	this.y[5] = hc.y[5];
	this.z[5] = hc.z[5];

	this.x[6] = hc.x[6];
	this.y[6] = hc.y[6];
	this.z[6] = hc.z[6];

	this.x[7] = hc.x[7];
	this.y[7] = hc.y[7];
	this.z[7] = hc.z[7];
    }

    /** 
     * Constructs and initializes a new HiResCoord located at (0, 0, 0).
     * The HiResCoord represents 768 bits of floating point 3-Space.
     */
    public HiResCoord() {
	this.x = new int[8];
	this.y = new int[8];
	this.z = new int[8];
    }

    /**
     * Sets this HiResCoord to the location specified by the
     * parameters provided.
     * @param X an eight-element array specifying the x position
     * @param Y an eight-element array specifying the y position
     * @param Z an eight-element array specifying the z position
     */
    public void setHiResCoord(int[] X, int[] Y, int[] Z) {
    int i;

        for(i=0;i<8;i++) {
           this.x[i] = X[i];
           this.y[i] = Y[i];
           this.z[i] = Z[i];
        }

    }

    /**
     * Sets this HiResCoord to the location specified by the
     * hires provided.
     * @param hires the hires coordinate to copy
     */
    public void setHiResCoord(HiResCoord hires) {
	this.x[0] = hires.x[0];
	this.y[0] = hires.y[0];
	this.z[0] = hires.z[0];

	this.x[1] = hires.x[1];
	this.y[1] = hires.y[1];
	this.z[1] = hires.z[1];

	this.x[2] = hires.x[2];
	this.y[2] = hires.y[2];
	this.z[2] = hires.z[2];

	this.x[3] = hires.x[3];
	this.y[3] = hires.y[3];
	this.z[3] = hires.z[3];

	this.x[4] = hires.x[4];
	this.y[4] = hires.y[4];
	this.z[4] = hires.z[4];

	this.x[5] = hires.x[5];
	this.y[5] = hires.y[5];
	this.z[5] = hires.z[5];

	this.x[6] = hires.x[6];
	this.y[6] = hires.y[6];
	this.z[6] = hires.z[6];

	this.x[7] = hires.x[7];
	this.y[7] = hires.y[7];
	this.z[7] = hires.z[7];
    }


    /**
     * Sets this HiResCoord's X value to that specified by the argument.
     * @param X an eight-element array specifying the x position
     */
    public void setHiResCoordX(int[] X) {
	this.x[0] = X[0];
	this.x[1] = X[1];
	this.x[2] = X[2];
	this.x[3] = X[3];
	this.x[4] = X[4];
	this.x[5] = X[5];
	this.x[6] = X[6];
	this.x[7] = X[7];
    }

    /**
     * Sets this HiResCoord's Y value to that specified by the argument.
     * @param Y an eight-element array specifying the y position
     */
    public void setHiResCoordY(int[] Y) {
	this.y[0] = Y[0];
	this.y[1] = Y[1];
	this.y[2] = Y[2];
	this.y[3] = Y[3];
	this.y[4] = Y[4];
	this.y[5] = Y[5];
	this.y[6] = Y[6];
	this.y[7] = Y[7];
    }

    /**
     * Sets this HiResCoord's Z value to that specified by the argument.
     * @param Z an eight-element array specifying the z position
     */
    public void setHiResCoordZ(int[] Z) {
	this.z[0] = Z[0];
	this.z[1] = Z[1];
	this.z[2] = Z[2];
	this.z[3] = Z[3];
	this.z[4] = Z[4];
	this.z[5] = Z[5];
	this.z[6] = Z[6];
	this.z[7] = Z[7];
    }
    
    /**
     * Retrieves this HiResCoord's location and saves the coordinates
     * in the specified arrays. The arrays must be large enough
     * to hold all of the ints.
     * @param X an eight element array that will receive the x position
     * @param Y an eight element array that will receive the y position
     * @param Z an eight element array that will receive the z position
     */
    public void getHiResCoord(int[] X, int[] Y, int[] Z) {
	X[0] = this.x[0];
	X[1] = this.x[1];
	X[2] = this.x[2];
	X[3] = this.x[3];
	X[4] = this.x[4];
	X[5] = this.x[5];
	X[6] = this.x[6];
	X[7] = this.x[7];

	Y[0] = this.y[0];
	Y[1] = this.y[1];
	Y[2] = this.y[2];
	Y[3] = this.y[3];
	Y[4] = this.y[4];
	Y[5] = this.y[5];
	Y[6] = this.y[6];
	Y[7] = this.y[7];

	Z[0] = this.z[0];
	Z[1] = this.z[1];
	Z[2] = this.z[2];
	Z[3] = this.z[3];
	Z[4] = this.z[4];
	Z[5] = this.z[5];
	Z[6] = this.z[6];
	Z[7] = this.z[7];
    }
    
    /**
     * Retrieves this HiResCoord's location and places it into the hires
     * argument.
     * @param hc the hires coordinate that will receive this node's location
     */
    public void getHiResCoord(HiResCoord hc) {
	hc.x[0] = this.x[0];
	hc.x[1] = this.x[1];
	hc.x[2] = this.x[2];
	hc.x[3] = this.x[3];
	hc.x[4] = this.x[4];
	hc.x[5] = this.x[5];
	hc.x[6] = this.x[6];
	hc.x[7] = this.x[7];

	hc.y[0] = this.y[0];
	hc.y[1] = this.y[1];
	hc.y[2] = this.y[2];
	hc.y[3] = this.y[3];
	hc.y[4] = this.y[4];
	hc.y[5] = this.y[5];
	hc.y[6] = this.y[6];
	hc.y[7] = this.y[7];

	hc.z[0] = this.z[0];
	hc.z[1] = this.z[1];
	hc.z[2] = this.z[2];
	hc.z[3] = this.z[3];
	hc.z[4] = this.z[4];
	hc.z[5] = this.z[5];
	hc.z[6] = this.z[6];
	hc.z[7] = this.z[7];
    }
    
    /**
     * Retrieves this HiResCoord's X value and stores it in the specified
     * array. The array must be large enough to hold all of the ints. 
     * @param X an eight-element array that will receive the x position
     */
    public void getHiResCoordX(int[] X) {
	X[0] = this.x[0];
	X[1] = this.x[1];
	X[2] = this.x[2];
	X[3] = this.x[3];
	X[4] = this.x[4];
	X[5] = this.x[5];
	X[6] = this.x[6];
	X[7] = this.x[7];
    }

    /**
     * Retrieves this HiResCoord's Y value and stores it in the specified
     * array. The array must be large enough to hold all of the ints. 
     * @param Y an eight-element array that will receive the y position
     */
    public void getHiResCoordY(int[] Y) {
	Y[0] = this.y[0];
	Y[1] = this.y[1];
	Y[2] = this.y[2];
	Y[3] = this.y[3];
	Y[4] = this.y[4];
	Y[5] = this.y[5];
	Y[6] = this.y[6];
	Y[7] = this.y[7];
    }

    /**
     * Retrieves this HiResCoord's Z value and stores it in the specified
     * array. The array must be large enough to hold all of the ints. 
     * @param Z an eight-element array that will receive the z position
     */
    public void getHiResCoordZ(int[] Z) {
	Z[0] = this.z[0];
	Z[1] = this.z[1];
	Z[2] = this.z[2];
	Z[3] = this.z[3];
	Z[4] = this.z[4];
	Z[5] = this.z[5];
	Z[6] = this.z[6];
	Z[7] = this.z[7];
    }

    /**
     * Compares the specified HiResCoord to this HiResCoord.
     * @param h1 the second HiResCoord
     * @return true if equal, false if not equal
     */
    public boolean equals(HiResCoord h1) {
        try {
	   return ((this.x[0] == h1.x[0])
		&& (this.x[1] == h1.x[1])
		&& (this.x[2] == h1.x[2])
		&& (this.x[3] == h1.x[3])
		&& (this.x[4] == h1.x[4])
		&& (this.x[5] == h1.x[5])
		&& (this.x[6] == h1.x[6])
		&& (this.x[7] == h1.x[7])
		&& (this.y[0] == h1.y[0])
		&& (this.y[1] == h1.y[1])
		&& (this.y[2] == h1.y[2])
		&& (this.y[3] == h1.y[3])
		&& (this.y[4] == h1.y[4])
		&& (this.y[5] == h1.y[5])
		&& (this.y[6] == h1.y[6])
		&& (this.y[7] == h1.y[7])
		&& (this.z[0] == h1.z[0])
		&& (this.z[1] == h1.z[1])
		&& (this.z[2] == h1.z[2])
		&& (this.z[3] == h1.z[3])
		&& (this.z[4] == h1.z[4])
		&& (this.z[5] == h1.z[5])
		&& (this.z[6] == h1.z[6])
		&& (this.z[7] == h1.z[7]));
        }
        catch (NullPointerException e2) {return false;}

    }

    /**
     * Returns true if the Object o1 is of type HiResCoord and all of the
     * data members of o1 are equal to the corresponding data members in
     * this HiResCoord.
     * @param o1 the second HiResCoord
     * @return true if equal, false if not equal
     */
    public boolean equals(Object  o1) {
        try {
	   HiResCoord h1 = (HiResCoord)o1;
   	   return ((this.x[0] == h1.x[0])
		&& (this.x[1] == h1.x[1])
		&& (this.x[2] == h1.x[2])
		&& (this.x[3] == h1.x[3])
		&& (this.x[4] == h1.x[4])
		&& (this.x[5] == h1.x[5])
		&& (this.x[6] == h1.x[6])
		&& (this.x[7] == h1.x[7])
		&& (this.y[0] == h1.y[0])
		&& (this.y[1] == h1.y[1])
		&& (this.y[2] == h1.y[2])
		&& (this.y[3] == h1.y[3])
		&& (this.y[4] == h1.y[4])
		&& (this.y[5] == h1.y[5])
		&& (this.y[6] == h1.y[6])
		&& (this.y[7] == h1.y[7])
		&& (this.z[0] == h1.z[0])
		&& (this.z[1] == h1.z[1])
		&& (this.z[2] == h1.z[2])
		&& (this.z[3] == h1.z[3])
		&& (this.z[4] == h1.z[4])
		&& (this.z[5] == h1.z[5])
		&& (this.z[6] == h1.z[6])
		&& (this.z[7] == h1.z[7]));
        }
        catch (NullPointerException e2) {return false;}
        catch (ClassCastException   e1) {return false;}


    }
    /**
     * Adds two HiResCoords placing the results into this HiResCoord.
     * @param h1 the first HiResCoord
     * @param h2 the second HiResCoord
     */
    public void add(HiResCoord h1, HiResCoord h2) {
    // needs to handle carry bits
    // move to long, add, add in carry bit

        hiResAdd( this, h1, h2 );

    }

    /**
     * Subtracts two HiResCoords placing the results into this HiResCoord.
     * @param h1 the first HiResCoord
     * @param h2 the second HiResCoord
     */
    public void sub(HiResCoord h1, HiResCoord h2) {
       HiResCoord tmpHc = new HiResCoord();

    // negate via two's complement then add
    //
        hiResNegate( tmpHc, h2);
        hiResAdd( this, h1, tmpHc);
        
    }

    /**
     * Negates the specified HiResCoords and places the
     * results into this HiResCoord.
     * @param h1 the source HiResCoord
     */
    public void negate(HiResCoord h1) {

        hiResNegate( this, h1);

    }

    /**
     * Negates this HiResCoord
     */
    public void negate() {

      hiResNegate( this, this );

    }

    /**
     * Scales the specified HiResCoords by the specified value and
     * places the results into this HiResCoord.
     * @param scale the amount to scale the specified HiResCoord
     * @param h1 the source HiResCoord
     */
    public void scale(int scale, HiResCoord h1) {
	 hiResScale( h1.x, this.x, scale);
	 hiResScale( h1.y, this.y, scale);
	 hiResScale( h1.z, this.z, scale);
    }

    /**
     * Scales this HiResCoord by the specified value.
     * @param scale the amount to scale the specified HiResCoord
     */
    public void scale(int scale) {
	 hiResScale( this.x, this.x, scale);
	 hiResScale( this.y, this.y, scale);
	 hiResScale( this.z, this.z, scale);
	 return;
    }

    /**
     * Subtracts the specified HiResCoord from this HiResCoord
     * placing the difference vector into the specified
     * double-precision vector.
     * @param h1 the HiResCoord to be subtracted from this
     * @param v the vector that will receive the result
     */
    public void difference(HiResCoord h1, Vector3d v) {
       // negate coord via two compliment, add, convert result to double
       // by scaling each bit set appropriately

        hiResDiff( this, h1, v);
	return;
    }

    /**
     * The floating point distance between the specified
     * HiResCoord and this HiResCoord.
     * @param h1 the second HiResCoord
     */
    public double distance(HiResCoord h1) {
        Vector3d diff = new Vector3d();

        hiResDiff( this, h1, diff);

        return( Math.sqrt( diff.x*diff.x + diff.y*diff.y + diff.z*diff.z));
    }

    private void hiResNegate( HiResCoord ho, HiResCoord hi) {

        negateCoord( ho.x, hi.x);
        negateCoord( ho.y, hi.y);
       negateCoord( ho.z, hi.z);

	return;
    }

   private void negateCoord( int cout[], int cin[] ) {
     int i;

     for(i=0;i<8;i++) {
        cout[i] = ~cin[i];  // take compliment of each
     }

     for(i=7;i>=0;i--) {             // add one
       if( cout[i] == 0xffffffff) {
          cout[i] = 0;
       } else {
          cout[i] += 1;
          break;
       }
     }
     return;
   }

   private void hiResAdd(HiResCoord ho, HiResCoord h1, HiResCoord h2 ){  
    int i;
    long tmp1, tmp2,carry;
    long signMask = Integer.MAX_VALUE;
    long signBit = 1;
    signBit =  signBit << 31;
    long carryMask = 0x7fffffff;
    carryMask = carryMask <<1;
    carryMask += 1;


    carry = 0;
    for(i=7;i>0;i--) {
        tmp1 = 0;
        tmp1 = signMask & h1.x[i];   // mask off sign bit so will not get put in msb
        if( h1.x[i] < 0 ) tmp1 |= signBit; // add sign bit back

        tmp2 = 0;
        tmp2 = signMask & h2.x[i];   // mask off sign bit so will not get put in msb
        if( h2.x[i] < 0 ) tmp2 |= signBit; // add sign bit back

        tmp2 = tmp2+tmp1 + carry;
        carry = tmp2 >> 32;  // get carry bits for next operation
        ho.x[i] = (int)(tmp2 & carryMask); // mask off high bits
    }
    ho.x[0] = h1.x[0] + h2.x[0] + (int)carry;


    carry = 0;
    for(i=7;i>0;i--) {
        tmp1 = 0;
        tmp1 = signMask & h1.y[i];   // mask off sign bit so will not get put in msb
        if( h1.y[i] < 0 ) tmp1 |= signBit; // add sign bit back

        tmp2 = 0;
        tmp2 = signMask & h2.y[i];   // mask off sign bit so will not get put in msb
        if( h2.y[i] < 0 ) tmp2 |= signBit; // add sign bit back

        tmp2 = tmp2+tmp1 + carry;
        carry = tmp2 >> 32;  // get carry bits for next operation
        ho.y[i] = (int)(tmp2 & carryMask); // mask off high bits
    }
    ho.y[0] = h1.y[0] + h2.y[0] + (int)carry;

    carry = 0;
    for(i=7;i>0;i--) {
        tmp1 = 0;
        tmp1 = signMask & h1.z[i];   // mask off sign bit so will not get put in msb
        if( h1.z[i] < 0 ) tmp1 |= signBit; // add sign bit back

        tmp2 = 0;
        tmp2 = signMask & h2.z[i];   // mask off sign bit so will not get put in msb
        if( h2.z[i] < 0 ) tmp2 |= signBit; // add sign bit back

        tmp2 = tmp2+tmp1 + carry;
        carry = tmp2 >> 32;  // get carry bits for next operation
        ho.z[i] = (int)(tmp2 & carryMask); // mask off high bits
    }
    ho.z[0] = h1.z[0] + h2.z[0] + (int)carry;
    return;
  }

  private void hiResScale( int tin[], int tout[], double scale) { 
      int i;
      long tmp,carry;
      int signMask = Integer.MAX_VALUE;
      long carryMask = 0x7fffffff;
      carryMask = carryMask <<1;
      carryMask += 1;
      long signBit = 1;
      signBit =  signBit << 31;
 
      carry = 0;
      for(i=7;i>0;i--) {
        tmp = 0;
        tmp = (long)(signMask & tin[i]);   // mask off sign bit
        if( tin[i] < 0 ) tmp |= signBit; // add sign bit back
        tmp = (long)(tmp*scale + carry);
        carry = tmp >> 32;  // get carry bits for next operation
        tout[i] = (int)(tmp & carryMask); // mask off high bits
      }
      tout[0] = (int)(tin[0]*scale + carry);
      return;
  }
  private void hiResDiff( HiResCoord h1, HiResCoord h2, Vector3d diff) { 
       int i;
       HiResCoord diffHi = new HiResCoord();
       long value;
       int coordSpace[] = new int[8];
       int[] tempCoord;
       int signMask = Integer.MAX_VALUE;
       long signBit = 1;
       signBit =  signBit << 31;

    // negate via two's complement then add
    //
        hiResNegate( diffHi, h2);
        hiResAdd( diffHi, h1, diffHi);


        if( diffHi.x[0] < 0 ) {
	   tempCoord = coordSpace;
	   negateCoord( tempCoord, diffHi.x );
        } else {
	   tempCoord = diffHi.x;
        }
        diff.x = 0;
        for(i=7;i>0;i--) {
	   value = (long)(tempCoord[i] & signMask);
	   if( tempCoord[i] < 0) value |= signBit;
	   diff.x += (double)(scales[i]*value);
	}
	diff.x += scales[0]*tempCoord[0];
        if( diffHi.x[0] < 0 )diff.x = -diff.x;

        if( diffHi.y[0] < 0 ) {
	   tempCoord = coordSpace;
	   negateCoord( tempCoord, diffHi.y );
        } else {
	   tempCoord = diffHi.y;
        }
        diff.y = 0;
        for(i=7;i>0;i--) {
	   value = (long)(tempCoord[i] & signMask);
	   if( tempCoord[i] < 0) value |= signBit;
	   diff.y += scales[i]*value;
	}
	diff.y += scales[0]*tempCoord[0];
        if( diffHi.y[0] < 0 )diff.y = -diff.y;

        if( diffHi.z[0] < 0 ) {
	   tempCoord = coordSpace;
	   negateCoord( tempCoord, diffHi.z );
        } else {
	   tempCoord = diffHi.z;
        }
        diff.z = 0;
        for(i=7;i>0;i--) {
	   value = (long)(tempCoord[i] & signMask);
	   if( tempCoord[i] < 0) value |= signBit;
	   diff.z += scales[i]*value;
	}
	diff.z += scales[0]*tempCoord[0];
        if( diffHi.z[0] < 0 )diff.z = -diff.z;
   return;
  }
}
