module util.parts;


public import std.math;
public import std.string;
public import util.vector;
public import util.matrix;
public import util.shape;
public import util.log;
public import util.animation;
private import util.actor;

private import opengl;


public class Parts:Actor{
	public:
		static enum {
			LINKX ,LINKY ,LINKZ ,POSEX ,POSEY ,POSEZ ,BOTHX ,BOTHY ,BOTHZ
		};
		static enum {POSE = 1 ,LINK = 2 ,BOTH = 3};
		static enum{
			WIRE = 1,POLYGON = 2
		};
		static enum{
			NORMAL ,ENGAGED, FOLLOW , LOCATION ,DISRELATION
		};
		
		static enum{
			NONE ,RIGHT ,LEFT
		};
		static Shape collisionShape;
		Vector3 pos;
		Vector3 rpos;
		Vector3 vpos;
		Shape shape;
		Parts parent;
		Parts[] childs;
		Parts[char[]] childHash;
		Animation[char[]] animes;
		Matrix poseBase;
		Matrix linkBase;
		bool linkBased;
		bool poseBased;
		Matrix rpose;
		Matrix rlink;
		Matrix poseXYZ;
		Matrix linkXYZ;
//		bool dirtypose;
//		bool dirtylink;
		double scaleX, scaleY, scaleZ;
		double rposeX ,rposeY ,rposeZ;
		double rlinkX ,rlinkY ,rlinkZ;
		double poseX ,poseY ,poseZ;
		double linkX ,linkY ,linkZ;
		
		double R ,G ,B ,alpha;
		double wR ,wG ,wB ,walpha;
		double dist;
//		double size;
		double collisionRange;
		
		bool drawn;
		int cnt;
		uint laserState;
		int childKind;
	private:
//		bool _exists;
		ubyte _drawing;
		double _size;
		
		
	
	
	public this(Vector3 pos = new Vector3()  ,Matrix poseBase = new Matrix()){
		super();
		start();
//		_exists = true;
		laserState = NONE;
		collisionRange = 0.0;
		
		
		this.pos = cast(Vector3)pos.clone();
		rpos = cast(Vector3)this.pos.clone();
		vpos = new Vector3();
		drawing = WIRE | POLYGON;
//		partsManager.add(this);
		setLinkBase();
		setPoseBase(poseBase);
		
		
		
	}
	public void start(){
		parent = null;
		//childs.length = 0;
		drawn = false;
		
		cnt = 0;
		scaleX = scaleY = scaleZ = 1.0;
		poseX = poseY = poseZ = 0;
		linkX = linkY = linkZ = 0;
		rposeX = rposeY = rposeZ = 0;
		rlinkX = rlinkY = rlinkZ = 0;
		poseXYZ = new Matrix();
		linkXYZ = new Matrix();
//		dirtypose = true;
//		dirtylink = true;
		rpose = new Matrix();
		R = G = B = 1.0;
		alpha = 1.0;
		
		wR = R;
		wG = G;
		wB = B;
		walpha = alpha;
		
		childKind = NORMAL;
		dist = 0.0;
		this.pos = new Vector3();
//		setLinkBase(new Matrix());
//		setPoseBase(new Matrix());
		
	}
	
	
	
	/*
	public void set(Vector3 pos = new Vector3() ,Matrix poseBase = new Matrix()){
		init(null);
		_exists = true;
		this.pos = cast(Vector3)pos.clone();
		setLinkBase(new Matrix());
		setPoseBase(poseBase);
	}
	*/
	public bool addAnimation(Animation anime ,char[] name){
		
		//if(name in animes)return false;
		animes[name] = anime;
		animes[name].set(this);
		
		return true;
	}
	public void deleteAnimation(char[] name){
		animes[name] = null;
	}
	public bool inAnimation(){
		bool inanime=false;
		foreach(Animation a;animes){
			if(a.active)inanime=true;
		}
		return inanime;
	}
	public bool inAnimation(char[] name){
		if(name in animes){
			return animes[name].active;
		}else return false;
	}
	public void setLinkBase(Matrix linkBase=null){
		
		if(linkBase is null){
			linkBase = new Matrix();
			linkBased = false;
		}else linkBased = true;
		this.linkBase = cast(Matrix)linkBase.clone();
		setRlink();
//		this.rlink = matRotateXYZ(linkX ,linkY ,linkZ) * cast(Matrix)linkBase.clone();
	}	
	public void setPoseBase(Matrix poseBase=null){
		if(poseBase is null){
			poseBase = new Matrix();
			poseBased = false;
		}else poseBased = true;
		this.poseBase = cast(Matrix)poseBase.clone();
		setRpose();
		
//		this.rpose =  matRotateXYZ(poseX ,poseY ,poseZ) * cast(Matrix)poseBase.clone();
	}
	public void setColor(double R ,double G ,double B ,double alpha ,bool transmit = false){
		this.R = R; this.G = G;this.B = B;this.alpha = alpha;
		if(transmit){
			foreach(Parts child;cast(Parts[])childHash.values){
				child.setColor(R ,G ,B ,alpha);
			}
		}
	}
	public void addChild(inout Parts child,char[] name, double dist = 0.1, int childKind = NORMAL, Matrix link = null ,Matrix pose = null){
		childs.length = childs.length + 1;
		childs[childs.length - 1] = child;
		addChildHash(child ,name);
		
		child.parent = this;
		child.childKind = childKind;
		child.dist = dist;
//		child.setColor(R ,G ,B ,alpha);
		if(childKind == FOLLOW || childKind == LOCATION || childKind == DISRELATION)child.setLinkBase();
		else if(link !is null)child.setLinkBase(link);
		else child.setLinkBase();//child.setLinkBase(this.rlink);
		
		if(childKind == FOLLOW || childKind == LOCATION || childKind == DISRELATION)child.setPoseBase();
		else if(pose !is null)child.setPoseBase(pose);
		else child.setPoseBase();//(new Matrix());
		
		
		
	}
	
	
	//
	/*
	public void removeChildHash(Parts child){
		childHash.remove(name);
		if(parent !is null)parent.removeChildHash(name);
	}
	*/
	
	public void addChildHash(Parts child ,char[] name){
		childHash[name] = child;
		if(parent !is null)parent.addChildHash(child ,name);
	}
	/*
	public void count(){
		
	}
	*/
	public override void move(){
		cnt ++;
		char[][] names = animes.keys;
		foreach(char[] name;names){
			Animation an = animes[name];

			if (an !is null && an.exists && an.active){

      	an.run();
				if(an.exists == false)an = null;
			}
		}
		switch(childKind){
			case FOLLOW:
			if(parent !is null && parent.exists && parent.drawn){
				double d = pos.dist(parent.rpos);
				if(d < dist){
//					pos =pos;
				}
				else pos = parent.rpos + (pos - parent.rpos) * dist / d;
			}
			break;
			
			default:break;
		}
		
		setRpose();
		setRlink();
//		moveImpl();
	}
//	public void moveImpl(){}
	public void changeScale(double s ,bool transmit = false){
		size = size * s;
		collisionRange *= s;
		dist *= s;
		if(transmit){
			foreach(Parts child;childs){
				child.changeScale(s ,transmit);
			}
		}
		
	}
	public void vibrate(real radZ ,real radX ,real v){
		vpos = new Vector3( sin(radZ) * cos(radX), sin(radZ) * sin(radX), cos(radX)) * v * size;
		foreach(Parts child;childs){
			if(child !is null && child.exists){
				child.vibrate(radZ ,radX ,v);
			}
		}
	}
	
	private void setRpose(){
//		rposeX = poseX;
//		rposeY = poseY;
//		rposeZ = poseZ;
		if(childKind != FOLLOW)poseXYZ = matRotateXYZ(poseX ,poseY ,poseZ);
		if(parent !is null){
			switch(childKind){
			case ENGAGED:

			if(poseBased)rpose = poseXYZ * poseBase * rlink;// * parent.rpose;
			else rpose = poseXYZ * rlink;
			break;
			case FOLLOW:
			double deg2;
			double dist;
			if((dist = rpos.dist(parent.rpos)) > (1e-6))deg2 = acos((parent.rpos.z - rpos.z) / rpos.dist(parent.rpos)) * 180.0 / PI;
			else deg2 = 0.0;
			
			double deg = atan2(parent.rpos.y - rpos.y ,parent.rpos.x - rpos.x) * 180.0 / PI;

			if(poseBased)rpose = matRotateXYZ(poseX + deg2 ,poseY ,poseZ + 180 + deg) * poseBase;
			else rpose = matRotateXYZ(poseX + deg2 ,poseY ,poseZ + 180 + deg);
			break;
			
			default:
			if(poseBased)rpose = poseXYZ * poseBase;
			else rpose = cast(Matrix)poseXYZ.clone();
			break;
			}
		}else{
//			rposeX = poseX;
//			rposeY = poseY;
//			rposeZ = poseZ;
			if(poseBased)rpose = poseXYZ * poseBase;
			else rpose = cast(Matrix)poseXYZ.clone();
		}
	}
	
	private void setRlink(){
//		rlinkX = linkX;
//		rlinkY = linkY;
//		rlinkZ = linkZ;
		linkXYZ = matRotateXYZ(linkX ,linkY ,linkZ);
		if(parent !is null){
			switch(childKind){
				case NORMAL:
				case ENGAGED:
//				Vector3 dpos = vec3translate(pos ,linkBase);
//				rlinkX = linkX + parent.rlinkX;
//				rlinkY = linkY + parent.rlinkY;
//				rlinkZ = linkZ + parent.rlinkZ;
//				rlink = 
				if(linkBased)rlink = linkXYZ * linkBase * parent.rlink;
				else rlink = linkXYZ * parent.rlink;
				Vector3 loc = new Vector3(rlink.m[0][0] * (dist + pos.x) ,rlink.m[0][1] * (dist+pos.y) ,rlink.m[0][2] * (dist+pos.z));
				rpos.x = parent.rpos.x + loc.x;
				rpos.y = parent.rpos.y + loc.y;
				rpos.z = parent.rpos.z + loc.z;
					/*
				if((1e-6) <= dist){
					rlink = matRotateXYZ(rlinkX ,rlinkY ,rlinkZ) * linkBase * parent.rlink;
//					Matrix l = matRotateXYZ(rlinkX ,rlinkY ,rlinkZ) * linkBase;
					//Matrix l = matRotateX(rlinkX) * matRotateY(rlinkY) * matRotateZ(rlinkZ) * linkBase;
					
					Vector3 loc = new Vector3(rlink.m[0][0] * (dist + pos.x) ,rlink.m[0][1] * (dist+pos.y) ,rlink.m[0][2] * (dist+pos.z));
					rpos.x = parent.rpos.x + loc.x;// + dpos.x;
					rpos.y = parent.rpos.y + loc.y;// + dpos.y;
					rpos.z = parent.rpos.z + loc.z;// + dpos.z;
				}else{
					
					rpos.x = parent.rpos.x;// + dpos.x;
					rpos.y = parent.rpos.y;// + dpos.y;
					rpos.z = parent.rpos.z;// + dpos.z;
				}
				*/
				break;
				
				case FOLLOW:
//				rlinkX = linkX + parent.rlinkX;
//				rlinkY = linkY + parent.rlinkY;
//				rlinkZ = linkZ + parent.rlinkZ;
				rpos.x = pos.x;
				rpos.y = pos.y;
				rpos.z = pos.z;
				rlink = linkXYZ * linkBase;
				/*
				double d = rpos.dist(parent.rpos);
				if(d < (1e-6)){
					rpos = parent.rpos + vpos;
				}else if(d < dist){
					rpos =rpos;
				}
				else rpos = parent.rpos + (rpos - parent.rpos) * dist / d + vpos;
				*/
				break;
				case LOCATION:
				rpos.x = parent.rpos.x + pos.x;
				rpos.y = parent.rpos.y + pos.y;
				rpos.z = parent.rpos.z + pos.z;
				if(linkBased)rlink = linkXYZ * linkBase;
				else rlink = cast(Matrix)linkXYZ.clone();
				break;
				default:
				
				rpos.x = pos.x;
				rpos.y = pos.y;
				rpos.z = pos.z;
				if(linkBased)rlink = linkXYZ * linkBase;
				else rlink = cast(Matrix)linkXYZ.clone();
//				rlinkX = linkX;
//				rlinkY = linkY;
//				rlinkZ = linkZ;
				break;
			}
		}else{
			rpos.x = pos.x;
			rpos.y = pos.y;
			rpos.z = pos.z;
			if(linkBased)rlink = linkXYZ * linkBase;
			else rlink = cast(Matrix)linkXYZ.clone();
//			rlinkX = linkX;
//			rlinkY = linkY;
//			rlinkZ = linkZ;
		}
	}
	public override void draw(){
		
		if((1e-6) < size){
			
			if(cnt < 1)return;
			
	//		glEnable(GL_DEPTH_TEST);
			glDisable(GL_BLEND);
			
			
			//glLoadIdentity();
			if(parent !is null){
				if(parent.drawn == false)return;
			}
		
			
			
	//		rpose = matRotateXYZ(poseX ,poseY ,poseZ) * poseBase * parent.rpose;
			
			
	//		rpose = matRotateXYZ(rposeX ,rposeY ,rposeZ) * poseBase;
			
			ubyte dr;
			
			/*
			if(!inCollisionRange)dr = WIRE;
			else dr = drawing;
			
			if(!linkBased)dr = WIRE;
			else dr = drawing;
			*/
			dr = drawing;
	//		Matrix drawpose = rpose
			//Matrix p = matRotateX(rposeX) * matRotateY(rposeY) * matRotateZ(rposeZ) * matScale(size * scaleX ,size * scaleY ,size * scaleZ) * poseBase;
			glTranslatef(rpos.x + vpos.x ,rpos.y + vpos.y ,rpos.z + vpos.z);
			
			
			glPushMatrix();
			glScalef(scaleX * size ,scaleY * size ,scaleZ * size);
			
			
			//glScaled(size * scaleX ,size * scaleY ,size * scaleZ);
			
			GLfloat color[] = [ R, G, B, alpha ];
			glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, cast(float*)color);
			
			
			
//			shape.transformMatrix(rpose);
//			double[16] rp = rpose.m
			glMultMatrixd(cast(double*)(rpose.m[0]~rpose.m[1]~rpose.m[2]~rpose.m[3]));
			
			
//			Vector3[] av = shape.v;
			
			
//			glEnable(GL_POLYGON_SMOOTH);
			
//			Vector3 ave;
			if(dr & POLYGON){
				drawPolygons();
				/*
				glEnable(GL_LIGHTING);
			glEnable(GL_DEPTH_TEST);
				polygon[] pr = shape.polygons;
				for(int i = 0; i < pr.length ;i ++){
					Vector3 normal = shape.polygons[i].normal;//getNormal(n);
					
					glBegin(GL_POLYGON);
					
					glNormal3f(normal.x ,normal.y ,normal.z);
						ave = new Vector3((av[pr[i].v[0]] + av[pr[i].v[1]] + av[pr[i].v[2]]) / 3.0);
//						double a = fmax(0.0,(1.0 + (rpos.z + ave.z) / 1600.0));// * alpha);
//						glColor3d(R * a ,G * a ,B * a);
					//glColor4d(R ,G ,B ,fmax(0.0,(1.0 + (pos.z + ave.z) / 1600.0) * alpha)); //glColor4f(1.0f ,1.0f ,1.0f,(1.0 + (pos.z + ave.z) / 1600.0) * 0.6);
				
					
					for(int j = 0;j < 3 ;j ++){
						glVertex3f(av[pr[i].v[j]].x,av[pr[i].v[j]].y,av[pr[i].v[j]].z);
					}
					
					
					
					
					
					glEnd();
					
					
					
					
					
				}
				glDisable(GL_LIGHTING);
					glDisable(GL_DEPTH_TEST);
					*/
			}
//			glDisable(GL_POLYGON_SMOOTH);
			
			
			
			
		
			if(dr & WIRE){
				drawWires();
				/+
				/*
				if(fabs(rpos.z+800) < 200.0){
					glLineWidth(2);
				}else glLineWidth(1);
				*/
				glEnable(GL_BLEND);
				glEnable(GL_LINE_SMOOTH);
				int[][] wires=shape.wires;
//				double a = fmin(1.0 ,fmax(0.0 ,alpha));
				for(int i = 0; i < wires.length ;i ++){
//					glColor3d(R * a ,G * a ,B * a);
//					glColor4d(R ,G ,B ,fmax(0.0,alpha));
					glBegin(GL_LINE_STRIP);
		  		 	 for(int j = 0; j < wires[i].length;j ++){
						double a;
//						if(abs(rpos.z+ av[wires[i][j]].z + 800) < 100.0)a = walpha * fmax(0.0,(1.0 - fabs(rpos.z + av[wires[i][j]].z+800) / 800.0));
//						else 
						a = alpha *0.5 * fmax(0.0,(1.0 - fabs(rpos.z + av[wires[i][j]].z+800) / 800.0));
						glColor4d(wR ,wG ,wB ,fmax(0.0,a));
	 	 		 	 	  glVertex3f(av[wires[i][j]].x ,av[wires[i][j]].y ,av[wires[i][j]].z);
						//glVertex3f(av[wires[i][j + 1]].x ,av[wires[i][j + 1]].y ,av[wires[i][j + 1]].z);
	  			  	}
					glEnd();
				}
//				glLineWidth(1);
				glDisable(GL_LINE_SMOOTH);
				glDisable(GL_BLEND);
				+/
			}
			
			
//			shape.resetVertex();
			
		}
		glPopMatrix();
//		glPushMatrix();
//		drawCollision();
//		glPopMatrix();
		drawn = true;
		
	}
	public void drawPolygons(){
		Vector3[] av = shape.v;
		
		
		if(shape !is null){
			glEnable(GL_LIGHTING);
			glEnable(GL_DEPTH_TEST);
			polygon[] pr = shape.polygons;
			for(int i = 0; i < pr.length ;i ++){
				Vector3 normal = shape.polygons[i].normal;//getNormal(n);
				
				glBegin(GL_POLYGON);
				
				glNormal3f(normal.x ,normal.y ,normal.z);
//					ave = new Vector3((av[pr[i].v[0]] + av[pr[i].v[1]] + av[pr[i].v[2]]) / 3.0);
				
				for(int j = 0;j < 3 ;j ++){
					glVertex3f(av[pr[i].v[j]].x,av[pr[i].v[j]].y,av[pr[i].v[j]].z);
				}
				
				glEnd();
			}
			glDisable(GL_LIGHTING);
			glDisable(GL_DEPTH_TEST);
		}
	}
	
	public void drawWires(){
		Vector3[] av = shape.v;
		Vector3 ave;
		if(shape !is null){
			glEnable(GL_BLEND);
			glEnable(GL_LINE_SMOOTH);
			int[][] wires=shape.wires;
	//				double a = fmin(1.0 ,fmax(0.0 ,alpha));
			for(int i = 0; i < wires.length ;i ++){
	//					glColor3d(R * a ,G * a ,B * a);
	//					glColor4d(R ,G ,B ,fmax(0.0,alpha));
				glBegin(GL_LINE_STRIP);
	  		 	 for(int j = 0; j < wires[i].length;j ++){
					double a;
	//						if(abs(rpos.z+ av[wires[i][j]].z + 800) < 100.0)a = walpha * fmax(0.0,(1.0 - fabs(rpos.z + av[wires[i][j]].z+800) / 800.0));
	//						else 
					a = alpha *0.5 * fmax(0.0,(1.0 - fabs(rpos.z + av[wires[i][j]].z+800) / 800.0));
					glColor4d(wR ,wG ,wB ,fmax(0.0,a));
	 		 	 	  glVertex3f(av[wires[i][j]].x ,av[wires[i][j]].y ,av[wires[i][j]].z);
					//glVertex3f(av[wires[i][j + 1]].x ,av[wires[i][j + 1]].y ,av[wires[i][j + 1]].z);
			  	}
				glEnd();
			}
			glDisable(GL_LINE_SMOOTH);
			glDisable(GL_BLEND);
		}
	}
	private void drawCollision(){
		if(collisionShape is null){
			collisionShape = new SH_Sphere(8);
		}
		
		Shape shape = collisionShape;
		
		glTranslatef(rpos.x + vpos.x ,rpos.y + vpos.y ,rpos.z + vpos.z);
		glScalef(collisionRange*2 ,collisionRange*2 ,collisionRange*2 );
		Vector3[] av = shape.v;
		int[][] wires = shape.wires;
				
//				double a = fmin(1.0 ,fmax(0.0 ,alpha));
				for(int i = 0; i < wires.length ;i ++){
//					glColor3d(R * a ,G * a ,B * a);
//					glColor4d(R ,G ,B ,fmax(0.0,alpha));
					glBegin(GL_LINE_STRIP);
		  		 	 for(int j = 0; j < wires[i].length;j ++){
						double a;
						if(abs(rpos.z+ av[wires[i][j]].z + 800) < 100.0)a = alpha * fmax(0.0,(1.0 - fabs(rpos.z + av[wires[i][j]].z+800) / 800.0));
						else a = alpha *0.5 * fmax(0.0,(1.0 - fabs(rpos.z + av[wires[i][j]].z+800) / 800.0));
						glColor4d(R ,G ,B ,fmax(0.0,a));
	 	 		 	 	  glVertex3f(av[wires[i][j]].x ,av[wires[i][j]].y ,av[wires[i][j]].z);
						//glVertex3f(av[wires[i][j + 1]].x ,av[wires[i][j + 1]].y ,av[wires[i][j + 1]].z);
	  			  	}
					glEnd();
				}
	}
	
	public void rotate(int object ,double deg ,double alpha = 0.0 ){
		//rotate(linkX ,deg ,alpha ,engaged);
		double *oDeg1 ,oDeg2;
		switch(object){
			case LINKX:oDeg1 = &linkX;break;
			case LINKY:oDeg1 = &linkY;break;
			case LINKZ:oDeg1 = &linkZ;break;
			case POSEX:oDeg1 = &poseX;break;
			case POSEY:oDeg1 = &poseY;break;
			case POSEZ:oDeg1 = &poseZ;break;
			case BOTHX:oDeg1 = &linkX;oDeg2 = &poseX;break;
			case BOTHY:oDeg1 = &linkY;oDeg2 = &poseY;break;
			case BOTHZ:oDeg1 = &linkZ;oDeg2 = &poseZ;break;
			default:return;
		}
		if(oDeg1 !is null)*oDeg1 += deg;
		if(oDeg2 !is null)*oDeg2 += deg;
		//pose *= matRotate(rad ,x ,y ,z);
		if(0 < childs.length){
			for(int i = 0;i < childs.length;i ++){
				if(childKind == ENGAGED){
//					childs[i].rotate((object % 3) + 6 ,deg ,0.0);
				}
				if((1e-6) <= abs(alpha))
					childs[i].rotate(object ,deg * alpha ,alpha);
			}
		}
		
	}
	
	public void rotateAll(Matrix m ){
		
		poseBase *= m;
		linkBase *= m;
		/*
		if(0 < childs.length){
			for(int i = 0;i < childs.length;i ++){
				childs[i].rotateAll(m);
			}
		}
		*/
	}
	public void scale(double sx ,double sy ,double sz){
		this.scaleX = sx;
		this.scaleY = sy;
		this.scaleZ = sz;
		//pose *= matScale(sx ,sy ,sz);
	}
	public double poseDirection(){
		Vector3 poseV = vec3translate(new Vector3(1 ,0 ,0), rpose);
		return atan2(poseV.y ,poseV.x);
	}
	public Vector3 poseVector(){
		Vector3 poseV = vec3translate(new Vector3(1 ,0 ,0), rpose);
		return poseV;
	}
	/*
	public bool exists(){
		return _exists;
	}
	public bool exists(bool value) {
    return _exists = value;
  }
  */
  	public bool inCollisionRange(){
		return (abs(rpos.z + 800) < collisionRange);/* && (rpos.y+size < screen.GAME_DOWN || screen.GAME_UP < rpos.y-size ||
			screen.GAME_RIGHT  < rpos.x-size || rpos.x+size < screen.GAME_LEFT );*/
  	}
	
	public bool attractPoseX(int object ,double deg ,double ddeg){
		if(abs(poseX - deg) < ddeg){
			poseX = deg;
			return false;
		}else if(poseX < deg){
			poseX += ddeg;
			return true;
		}else{
			poseX -= ddeg;
			return true;
		}
	}
	public bool attract(int object ,double deg ,double ddeg){
		double *oDeg1 ,oDeg2;
		switch(object){
			case LINKX:oDeg1 = &linkX;break;
			case LINKY:oDeg1 = &linkY;break;
			case LINKZ:oDeg1 = &linkZ;break;
			case POSEX:oDeg1 = &poseX;break;
			case POSEY:oDeg1 = &poseY;break;
			case POSEZ:oDeg1 = &poseZ;break;
			case BOTHX:oDeg1 = &linkX;oDeg2 = &poseX;break;
			case BOTHY:oDeg1 = &linkY;oDeg2 = &poseY;break;
			case BOTHZ:oDeg1 = &linkZ;oDeg2 = &poseZ;break;
			default:return false;
		}
		bool active = false;
		if(oDeg1 !is null){
			if(abs(*oDeg1 - deg) < ddeg){
				*oDeg1 = deg;
			}else if(*oDeg1 < deg){
				*oDeg1 += ddeg;
				active = true;
			}else{
				*oDeg1 -= ddeg;
				active = true;
			}
		}
		if(oDeg2 !is null){
			if(abs(*oDeg2 - deg) < ddeg){
				*oDeg2 = deg;
			}else if(*oDeg2 < deg){
				*oDeg2 += ddeg;
				active = true;
			}else{
				*oDeg2 -= ddeg;
				active = true;
			}
		}
		return active;
	}
	public void reportCollision(int kind ,Parts p){
//		destroy();
	}
	public void destroy(){
		vanish();
		
//		destroyImpl();
	}
//	public void destroyImpl(){}
	public void vanish(){
		super.vanish();
		if(parent !is null){
			Parts c;
			for(int i = 0;i < parent.childs.length;i++){
				c = parent.childs[i];
				if(c is this){
					for(int j = i;j<parent.childs.length-1;j++){
						parent.childs[j]=parent.childs[j+1];
					}
					parent.childs.length = parent.childs.length - 1;
				}
			}
		}
		for(int i = 0;i < childs.length;i ++){
			childs[i].parent = null;
			childs[i].pos = cast(Vector3)childs[i].rpos.clone();
			childs[i].poseX = childs[i].rposeX;
			childs[i].poseY = childs[i].rposeY;
			childs[i].poseZ = childs[i].rposeZ;
		}
	}
	
	public double* pDeg(uint degType){
		switch(degType){
			case LINKX:return &linkX;
			case LINKY:return &linkY;
			case LINKZ:return &linkZ;
			case POSEX:return &poseX;
			case POSEY:return &poseY;
			case POSEZ:return &poseZ;
			case BOTHX:return &linkX;
			case BOTHY:return &linkY;
			case BOTHZ:return &linkZ;
			default:return null;
		}
		return null;
	}
	
	public void drawing(ubyte d){
		_drawing = d;
	}
	public ubyte drawing(){
		return _drawing;
	}
	
	public void size(double s){
		_size = s;
	}
	public double size(){
		return _size;
	}

}

 PartsManager partsManager;
public class PartsManager:ActorManager{
	public:
//	Parts[] parts;
	//List!(Parts) parts;
	protected:
	//List!(int) van = new List!(int)();
//  int partsIdx = 0;
//	const int maxParts;
	
	public this() {
		super();
		/*
		maxParts = 16;
		parts.length = maxParts;
		//parts = new List!(Parts)();
		partsIdx = 0;
		*/
	}

  public this(int n) {
  		super(n);
  	/*
		maxParts = n;
		parts.length = maxParts;
		//parts = new List!(Parts)();
		partsIdx = 0;
		maxParts = n;
		*/
  }
/+
	public bool add(Parts p){
		for(int i = 0;i < parts.length;i ++){
			if(parts[partsIdx] is null || !parts[partsIdx].exists()){
				parts[partsIdx] = p;
				return true;
			}
			partsIdx ++;
			if(parts.length <= partsIdx)partsIdx = 0;
		}
		return false;
		//if(maxParts <= parts.size)return false;
		//parts.push_back(p);
		
	}
	public void move() {
    //List!(int) van = new List!(int)();
		for (int i = 0;i < parts.length;i ++){
			Parts pa = parts[i];
      if (pa !is null && pa.exists){
				
        pa.move();
				pa.count();
				if(pa.exists == false)pa = null;
      }else{
				//van.push_back(i);
      }
    }
		
		/*
		for (int i = van.size - 1;0 <= i;i --){
			parts.erase(van.at(i));
		}
		*/
  }


  public void draw() {
		
    for (int i = 0;i < parts.length;i ++){
			Parts pa = parts[i];
      if (pa !is null && pa.exists){
				glPushMatrix();
       	pa.draw();
				glPopMatrix();
      }
    }
		
  }
+/
  public void allDestroy() {
		foreach(inout Actor p;actor){
			if(p !is null && p.exists){
				(cast(Parts)p).destroy();
			}
		}
   	//parts.length = 0;
    actorIdx = 0;
  }
  /*
  public void clear(){
  	foreach(inout Parts p;parts){
			if(p !is null && p.exists){
				p.vanish();
			}
		}
   	//parts.length = 0;
    partsIdx = 0;
  }
  */
   public void allParentsDestroy(){
  	foreach(inout Actor p;actor){
		if(p !is null && p.exists && (cast(Parts)p).parent is null){
			(cast(Parts)p).destroy();
		}
	}
   	//parts.length = 0;
    actorIdx = 0;
  }
  
}
/+
public void drawLocus(Shape shape ,Vector3 rpos0 ,Vector3 rpos1 ,Matrix rpose0 ,Matrix rpose1 ,double R ,double G ,double B ,double alpha){
	shape.resetVertex();
	shape.transformMatrix(rpose0/* * matTranslate(rpos0.x ,rpos0.y ,rpos0.z)*/);
	Vector3[] av0 = shape.v;
	shape.resetVertex();
	shape.transformMatrix(rpose1/* * matTranslate(rpos1.x ,rpos1.y ,rpos1.z)*/);
	Vector3[] av1 = shape.v;
	
	int[][] wires = shape.wires;
	glTranslatef(rpos0.x ,rpos0.y ,rpos0.z);
	
			
			for(int i = 0; i < wires.length ;i ++){
				glColor4d(R ,G ,B ,alpha);
				glBegin(GL_LINE_STRIP);
	   	 for(int j = 0; j < wires[i].length;j ++){
 	  	 	  glVertex3f(av0[wires[i][j]].x ,av0[wires[i][j]].y ,av0[wires[i][j]].z);
					//glVertex3f(av[wires[i][j + 1]].x ,av[wires[i][j + 1]].y ,av[wires[i][j + 1]].z);
  	  	}
				glEnd();
			}
			shape.resetVertex();
	/*
	for(int i = 0; i < wires.length ;i ++){
		
		glBegin(GL_POLYGON);
		glColor4d(R ,G ,B ,alpha);
 		glVertex3f(av0[wires[i][0]].x ,av0[wires[i][0]].y ,av0[wires[i][0]].z);
		glVertex3f(av0[wires[i][wires[i].length - 1]].x ,av0[wires[i][wires[i].length - 1]].y ,av0[wires[i][wires[i].length - 1]].z);
		glColor4d(R ,G ,B ,0.0);
		glVertex3f(av1[wires[i][wires[i].length - 1]].x ,av1[wires[i][wires[i].length - 1]].y ,av1[wires[i][wires[i].length - 1]].z);
		glVertex3f(av1[wires[i][0]].x ,av1[wires[i][0]].y ,av1[wires[i][0]].z);
		glEnd();
		for(int j = 0; j < wires[i].length - 1;j ++){
			glBegin(GL_POLYGON);
			glColor4d(R ,G ,B ,alpha);
 		  glVertex3f(av0[wires[i][j]].x ,av0[wires[i][j]].y ,av0[wires[i][j]].z);
			glVertex3f(av0[wires[i][j+1]].x ,av0[wires[i][j+1]].y ,av0[wires[i][j+1]].z);
			glColor4d(R ,G ,B ,0.0);
			glVertex3f(av1[wires[i][j+1]].x ,av1[wires[i][j+1]].y ,av1[wires[i][j+1]].z);
			glVertex3f(av1[wires[i][j]].x ,av1[wires[i][j]].y ,av1[wires[i][j]].z);
			glEnd();
  	}
		shape.resetVertex();
	}
	*/
}
+/