import com.nttdocomo.ui.*;
import com.nttdocomo.io.*;
import com.nttdocomo.util.*;

import java.util.Random;

import com.beartronics.FP;
import com.beartronics.Matrix3D;

/**
 *
 * Test Fixed Point math functions
 *
 * (C) 2001 Beartronics 
 * Author: Henry Minsky (hqm@alum.mit.edu)
 *
 * Licensed under terms "Artistic License"
 * http://www.opensource.org/licenses/artistic-license.html
 *
 */

public class MathTest extends IApplication  implements Runnable {
    MathCanvas c;
    public void start() {
	c = new MathCanvas(this);
	Display.setCurrent(c);
        Thread runner = new Thread(this);
        runner.start();
    }


    public void run() {
	c.count =0;
	c.state = c.TEST1;
        for (;;) {
            try {
                Thread.sleep(10);
            } catch (Exception e) {}

            c.repaint();
	    c.count++;
        }
    }
}

class MathCanvas extends Canvas {
    private IApplication app;

    public int count = 0;
    public int state = TEST1;

    public static final int TEST1 = 1;

    Model3D md;
    boolean painted = true;
    int xfac;
    int prevx, prevy;
    int xtheta, ytheta;
    int POINT_SEVEN = 45875; // 0.7
    int scalefudge = POINT_SEVEN;
    Matrix3D amat = new Matrix3D(), tmat = new Matrix3D();
    String mdname = null;
    String message = null;

    int screenWidth;
    int screenHeight;

    Random rand;

    MathCanvas(IApplication app) {
	screenWidth = getWidth();
	screenHeight = getHeight();

	this.app = app;
        setSoftLabel(Frame.SOFT_KEY_1, "Exit");

	Model3D m = new Model3D ();
	md = m;
	m.findBB();
	m.compress();
	int xw = m.xmax - m.xmin;
	int yw = m.ymax - m.ymin;
	int zw = m.zmax - m.zmin;
	if (yw > xw) {
	    xw = yw;
	}
	if (zw > xw) {
	    xw = zw;
	}
	int f1 = FP.Div(FP.intToFP(screenWidth), xw);
	int f2 = FP.Div(FP.intToFP(screenHeight), xw);
	xfac = FP.Mul(POINT_SEVEN, FP.Mul((f1 < f2 ? f1 : f2), scalefudge));

    }

    public int radius = 0;
    public static int SPIN = 1;

    public void processEvent(int eventType, int param) {
	if (Display.KEY_PRESSED_EVENT == eventType) {
	    switch (param) {
	    case Display.KEY_SELECT:
	    case Display.KEY_5:
		alpha = 0;
		beta = 0;
		break;

	    case Display.KEY_4:
	    case Display.KEY_LEFT:
		alpha = -SPIN;
		break;

	    case Display.KEY_6:
	    case Display.KEY_RIGHT:
		alpha = SPIN;
		break;

	    case Display.KEY_2:
	    case Display.KEY_UP:
		beta = -SPIN;
		break;

	    case Display.KEY_8:
	    case Display.KEY_DOWN:
		beta = SPIN;
		break;

	    case Display.KEY_SOFT1:
                app.terminate();
		break;
	    }
	}
    }

    boolean clearScreen = true;

    void clearScreen (Graphics g) {
	g.setColor(g.getColorOfRGB(0xff, 0xff, 0xff ));
	g.fillRect(0, 0, screenWidth, screenHeight);
    }

    int alpha = 0;
    int beta=0;
    public  void spin() {
        tmat.unit();
        tmat.xrot(beta%360);
        tmat.yrot(alpha%360);
        amat.mult(tmat);
    }

    public void paint(Graphics g) {
	int cx = screenWidth / 2;
	int cy = screenHeight / 2;

	g.lock();
	clearScreen(g);
	spin();

	md.mat.unit();
	md.mat.translate(-(md.xmin + md.xmax) / 2,
			 -(md.ymin + md.ymax) / 2,
			 -(md.zmin + md.zmax) / 2);
	md.mat.mult(amat);
	md.mat.scale(xfac, -xfac, 16 * (FP.Div(xfac, FP.intToFP(screenWidth))));
	md.mat.translate(FP.intToFP(screenWidth / 2), FP.intToFP(screenHeight / 2), 8<<16);
	md.transformed = false;

	md.paintWireFrame(g);
	g.unlock(true);
   }

}

/** The representation of a 3D model */
class Model3D {
    int vert[];
    int tvert[];
    int nvert, maxvert;
    int con[];
    int ncon, maxcon;
    boolean transformed;
    Matrix3D mat;

    int xmin, xmax, ymin, ymax, zmin, zmax;

    Model3D () {
	mat = new Matrix3D ();
	mat.xrot(20);
	mat.yrot(30);

	int ONE = 1<<16;
	addVert(0, 0, 0);
	addVert(ONE, 0, 0);
	addVert(ONE, ONE, 0);
	addVert(0, ONE, 0);
	addVert(0, 0, ONE);
	addVert(ONE, 0, ONE);
	addVert(ONE, ONE, ONE);
	addVert(0, ONE, ONE);

	add(0,1);
	add(1,2);
	add(2,3);
	add(3,0);
	
	add(4,5);
	add(5,6);
	add(6,7);
	add(7,4);

	add(0,4);
	add(1,5);
	add(2,6);
	add(3,7);
    }




    /** Add a vertex to this model */
    int addVert(int x, int y, int z) {
	int i = nvert;
	if (i >= maxvert)
	    if (vert == null) {
		maxvert = 100;
		vert = new int[maxvert * 3];
	    } else {
		maxvert *= 2;
		int nv[] = new int[maxvert * 3];
		System.arraycopy(vert, 0, nv, 0, vert.length);
		vert = nv;
	    }
	i *= 3;
	vert[i] = x;
	vert[i + 1] = y;
	vert[i + 2] = z;
	return nvert++;
    }
    /** Add a line from vertex p1 to vertex p2 */
    void add(int p1, int p2) {
	int i = ncon;
	if (p1 >= nvert || p2 >= nvert)
	    return;
	if (i >= maxcon)
	    if (con == null) {
		maxcon = 100;
		con = new int[maxcon];
	    } else {
		maxcon *= 2;
		int nv[] = new int[maxcon];
		System.arraycopy(con, 0, nv, 0, con.length);
		con = nv;
	    }
	if (p1 > p2) {
	    int t = p1;
	    p1 = p2;
	    p2 = t;
	}
	con[i] = (p1 << 16) | p2;
	ncon = i + 1;
    }
    /** Transform all the points in this model */
    void transform() {
	if (transformed || nvert <= 0)
	    return;
	if (tvert == null || tvert.length < nvert * 3)
	    tvert = new int[nvert*3];
	mat.transform(vert, tvert, nvert);
	transformed = true;
    }

   /* Quick Sort implementation
    */
   private void quickSort(int a[], int left, int right)
   {
      int leftIndex = left;
      int rightIndex = right;
      int partionElement;
      if ( right > left)
      {

         /* Arbitrarily establishing partition element as the midpoint of
          * the array.
          */
         partionElement = a[ ( left + right ) / 2 ];

         // loop through the array until indices cross
         while( leftIndex <= rightIndex )
         {
            /* find the first element that is greater than or equal to
             * the partionElement starting from the leftIndex.
             */
            while( ( leftIndex < right ) && ( a[leftIndex] < partionElement ) )
               ++leftIndex;

            /* find an element that is smaller than or equal to
             * the partionElement starting from the rightIndex.
             */
            while( ( rightIndex > left ) &&
                   ( a[rightIndex] > partionElement ) )
               --rightIndex;

            // if the indexes have not crossed, swap
            if( leftIndex <= rightIndex )
            {
               swap(a, leftIndex, rightIndex);
               ++leftIndex;
               --rightIndex;
            }
         }

         /* If the right index has not reached the left side of array
          * must now sort the left partition.
          */
         if( left < rightIndex )
            quickSort( a, left, rightIndex );

         /* If the left index has not reached the right side of array
          * must now sort the right partition.
          */
         if( leftIndex < right )
            quickSort( a, leftIndex, right );

      }
   }

   private void swap(int a[], int i, int j)
   {
      int T;
      T = a[i];
      a[i] = a[j];
      a[j] = T;
   }


    /** eliminate duplicate lines */
    void compress() {
	int limit = ncon;
	int c[] = con;
	quickSort(con, 0, ncon - 1);
	int d = 0;
	int pp1 = -1;
	for (int i = 0; i < limit; i++) {
	    int p1 = c[i];
	    if (pp1 != p1) {
		c[d] = p1;
		d++;
	    }
	    pp1 = p1;
	}
	ncon = d;
    }

    int gr[] = new int[16];
    int BLACK = Graphics.getColorOfRGB(0,0,0);

    /** Paint this model to a graphics context.  It uses the matrix associated
	with this model to map from model space to screen space.
	The next version of the browser should have double buffering,
	which will make this *much* nicer */
    void paintWireFrame(Graphics g) {
	if (vert == null || nvert <= 0) {
	    return;
	}
	transform();

	int lg = 0;
	int lim = ncon;
	int c[] = con;
	int v[] = tvert;
	if (lim <= 0 || nvert <= 0)
	    return;
	for (int i = 0; i < lim; i++) {
	    int T = c[i];
	    int p1 = ((T >> 16) & 0xFFFF) * 3;
	    int p2 = (T & 0xFFFF) * 3;
	    int grey = v[p1 + 2] + v[p2 + 2];
	    if (grey < 0)
		grey = 0;
	    if (grey > 15)
		grey = 15;
	    if (grey != lg) {
		lg = grey;
		// g.setColor(gr[grey]);
		g.setColor(BLACK);
	    }
	    int x1 = (v[p1])>>16;
	    int y1 = (v[p1 + 1])>>16;
	    int x2 = (v[p2])>>16;
	    int y2 = (v[p2 + 1])>>16;
	     
	    g.drawLine( x1 , y1 , x2 , y2 );
	}
    }

    /** Find the bounding box of this model */
    void findBB() {
	if (nvert <= 0)
	    return;
	int v[] = vert;
	int xmin = v[0], xmax = xmin;
	int ymin = v[1], ymax = ymin;
	int zmin = v[2], zmax = zmin;
	for (int i = nvert * 3; (i -= 3) > 0;) {
	    int x = v[i];
	    if (x < xmin)
		xmin = x;
	    if (x > xmax)
		xmax = x;
	    int y = v[i + 1];
	    if (y < ymin)
		ymin = y;
	    if (y > ymax)
		ymax = y;
	    int z = v[i + 2];
	    if (z < zmin)
		zmin = z;
	    if (z > zmax)
		zmax = z;
	}
	this.xmax = xmax;
	this.xmin = xmin;
	this.ymax = ymax;
	this.ymin = ymin;
	this.zmax = zmax;
	this.zmin = zmin;
    }
}