import java.awt.*;
import java.lang.Math;

public class Shape {
	public PointyPoint vertices_[] = null;
	public int faces_[][] = null;
	public int screenx_[],screeny_[] = null;
	public Color color_ = Color.black;
	
	public Shape children_[] = null;
	
	public Shape(int numvert, int [] facesizes) { 
		init(numvert,facesizes);
	}
	public Shape(int numvert, int nfaces, int facesize) {
		init(numvert,nfaces,facesize);
	}
	public Shape() {} // for subclasses that have to calculate for init()
	void init(int numvert, int [] facesizes) {
		vertices_ = new PointyPoint[numvert];
		translated_ = new PointyPoint[numvert];
		for(int i = 0; i<numvert; ++i)
			translated_[i] = new PointyPoint();
		int points=0;
		faces_ = new int[facesizes.length][];
		for(int i = 0; i<facesizes.length; ++i) {
			points += facesizes[i];
			faces_[i] = new int[facesizes[i]];
		}
		screenx_ = new int[numvert];
		screeny_ = new int[numvert];
		
		mine_.identity();
		scaler_.identity();
		skewer_.identity();
		xRotator_.identity();
		yRotator_.identity();
		zRotator_.identity();
		translator_.identity();
	}
	void init(int numvert, int nfaces, int facesize) {
		int[] facesizes = new int[nfaces];
		for(int i=0; i<nfaces; ++i)
			facesizes[i] = facesize;
		init(numvert,facesizes);
	}
	double scaleX_=1.,scaleY_=1.,scaleZ_=1.,
		xrot_=0.,yrot_=.0,zrot_=0.,
		x_=.0,y_=0.,z_=.0;
	private final double EPS=0.001;
	// waste some memory for less cpu?
	// the idea here is to cache all 5 needed sub-matrices
	// and only remultiply them when necessary..?
	private boolean changed_ = false;
	private Matrix3D worldity_ = new Matrix3D(),
		mine_ = new Matrix3D(),
		skewer_ = new Matrix3D(),
		scaler_ = new Matrix3D(),
		xRotator_ = new Matrix3D(),
		yRotator_ = new Matrix3D(),
		zRotator_ = new Matrix3D(),
		translator_ = new Matrix3D();
	private PointyPoint translated_[] = null;
	static Matrix3D temp1_ = new Matrix3D(), //?
		temp2_ = new Matrix3D();
		
	void setSkewXY(double xy, double yx) { 
		// I just want a 1 lessee if this worx
		skewer_.set(0,1,xy);
		skewer_.set(1,0,yx);
		changed_ = true;
	}
	void setScale(double x, double y, double z) {
		if(Math.abs(x-scaleX_)>EPS || Math.abs(y-scaleY_)>EPS || Math.abs(z-scaleZ_)>EPS) {
			changed_ = true;
			scaler_.scaler(scaleX_ = x,scaleY_ = y,scaleZ_ = z);
		}
	}
	void setScale(Vector3D v) {
		setScale(v.x(),v.y(),v.z());
	}
	void setRotationX(double xrot) {
		if(Math.abs(xrot-xrot_)>EPS) {
			changed_ = true;
			xRotator_.xRotation(xrot_ = xrot);
		}
	}
	void setRotationY(double yrot) {
		if(Math.abs(yrot-yrot_)>EPS) {
			changed_ = true;
			yRotator_.yRotation(yrot_ = yrot);
		}
	}
	void setRotationZ(double zrot) {
		if(Math.abs(zrot-zrot_)>EPS) {
			changed_ = true;
			zRotator_.zRotation(zrot_ = zrot);
		}
	}
	void setTranslation(double x,double y, double z) {
		if(Math.abs(x-x_)>EPS || Math.abs(y-y_)>EPS || Math.abs(z-z_)>EPS) {
			changed_ = true;
			translator_.translation(x_ = x,y_ = y,z_ = z);
		}
	}		
	void setTranslation(Vector3D v) {
		setTranslation(v.x(),v.y(),v.z());
	}
	public Matrix3D getMine() {
		if(changed_) {
			/*
			System.out.println("scaler "+scaler_.toString());
			System.out.println("xrot "+xRotator_.toString());
			System.out.println("yrot "+yRotator_.toString());
			System.out.println("zrot "+zRotator_.toString());
			System.out.println("trans "+translator_.toString());
			*/
			// maybe I'm too obsessed with efficiency.  
			// this is ugly and error-prone
			skewer_.multiply(scaler_,temp2_);
			temp2_.multiply(xRotator_,temp1_);
			temp1_.multiply(yRotator_,temp2_);
			temp2_.multiply(zRotator_,temp1_);
			temp1_.multiply(translator_,mine_);
			//System.out.println("matrix "+mine_);
			changed_ = false;
		}
		return mine_;
	}
	public Matrix3D getWorldy() {
		return worldity_;
	}
	void render(Matrix3D view, double t, double dt) {
		Matrix3D mine = getMine();
		mine.multiply(view,worldity_);
		//System.out.println("render to "+worldity_);
		
		for(int i = 0; i<vertices_.length; ++i) {
			worldity_.transform(vertices_[i].pt,translated_[i].pt);
			Vector3D pt = translated_[i].pt;
			screenx_[i] = (int)pt.x();
			screeny_[i] = (int)pt.y();
			//System.out.println("screen gets "+screenx_[i]+","+screeny_[i]);
		}
		if(children_!=null) 
			for(int i = 0; i<children_.length; ++i)
				if(children_[i]!=null)
					children_[i].render(worldity_,t,dt);
	}
	void draw(Graphics g) {
		g.setColor(color_);
		for(int i = 0; i<faces_.length; ++i) 
			for(int j = 0; j<faces_[i].length; ++j) {
				int k = (j+1)%faces_[i].length,
					x1 = screenx_[faces_[i][j]],
					y1 = screeny_[faces_[i][j]],
					x2 = screenx_[faces_[i][k]],
					y2 = screeny_[faces_[i][k]];
				g.drawLine(x1,y1,x2,y2);
				//System.out.println("line " + x1 + "," + y1 + "," + x2 + ","+ y2);
			}
		if(children_!=null) 
			for(int i = 0; i<children_.length; ++i)
				if(children_[i]!=null)
					children_[i].draw(g);
	}
}
