import java.awt.*;
import java.lang.Math;

public class Transform {
    public Transform children_[] = null;
    public Transform root_ = this;
    
    Transform() {
        mine_.identity();
        scaler_.identity();
        skewer_.identity();
        xRotator_.identity();
        yRotator_.identity();
        zRotator_.identity();
        translator_.identity();
    }        
    public interface Changer {
        void change(Vector3D v);
    }
    public class Scaler implements Changer {
        public void change(Vector3D v) {
            setScale(v);
        }
    }
    public class Rotator implements Changer {
        public void change(Vector3D v) {
            setRotation(v);
        }
    }
    public class Mover implements Changer {
        public void change(Vector3D v) {
            setTranslation(v);
        }
    }
    double scaleX_=1.,scaleY_=1.,scaleZ_=1.,
        xrot_=0.,yrot_=.0,zrot_=0.,
        x_=.0,y_=0.,z_=.0;
    private final double EPS=0.0001;
    // 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;
    protected Matrix3D worldward_ = new Matrix3D(),
        inward_ = new Matrix3D();
    private Matrix3D
        mine_ = new Matrix3D(),
        skewer_ = new Matrix3D(),
        scaler_ = new Matrix3D(),
        xRotator_ = new Matrix3D(),
        yRotator_ = new Matrix3D(),
        zRotator_ = new Matrix3D(),
        translator_ = new Matrix3D();
    boolean do_skew=false,do_scale=false,do_x=false,do_y=false,do_z=false,do_xlate=false;
    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;
        do_skew = 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);
            do_scale = true;
        }
    }
    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);
            do_x = true;
        }
    }
    void setRotationY(double yrot) {
        if(Math.abs(yrot-yrot_)>EPS) {
            changed_ = true;
            yRotator_.yRotation(yrot_ = yrot);
            do_y = true;
        }
    }
    void setRotationZ(double zrot) {
        if(Math.abs(zrot-zrot_)>EPS) {
            changed_ = true;
            zRotator_.zRotation(zrot_ = zrot);
            do_z = true;
        }
    }
    void setRotation(Vector3D v) {
        setRotationX(v.x());
        setRotationY(v.y());
        setRotationZ(v.z());
    }
    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);
            do_xlate = true;
        }
    }       
    void setTranslation(Vector3D v) {
        setTranslation(v.x(),v.y(),v.z());
    }
    public Matrix3D getWorldy() {
        return worldward_;
    }
    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_;
    }
    void transform(Matrix3D view) {
        Matrix3D mine = getMine();
        mine.multiply(view,worldward_);
        worldward_.inverse(temp1_);
        temp1_.transpose(inward_);
        if(children_!=null) 
            for(int i = 0; i<children_.length; ++i)
                if(children_[i]!=null)
                    children_[i].transform(worldward_);
    }
    boolean isDescendent(Transform other) {
        if(children_!=null)
            for(Transform child : children_)
                if(other==child)
                    return true;
                else if(child!=null && child.isDescendent(other))
                    return true;
        return false;
    }
    void reroot(Transform root) {
        root_ = root;
        if(children_!=null)
            for(Transform child : children_)
                if(child!=null)
                    child.reroot(root);
    }
    void dumpTree(int ind) {
        for(int i=0; i<ind; ++i)
            System.out.print("   ");
        System.out.println(this);
        if(children_!=null)
            for(Transform child : children_)
                if(child!=null)
                    child.dumpTree(ind+1);
    }
    void join(Transform parent) {
        if(parent.children_==null)
            parent.children_=new Transform[10];
        for(int i = 0; i<parent.children_.length; ++i)
            if(parent.children_[i]==null) {
                parent.children_[i] = this;
                setTranslation(x_-parent.x_,y_-parent.y_,z_-parent.z_);
                reroot(parent.root_);
                return;
            }
    }        
}