module util.shape;
private import std.math;
private import util.cloneable;
private import std.string;
private import util.vector;
private import util.matrix;
private import util.log;
public struct polyRect{
	public int[4] v;
	//public Vector3[4] v;
	
	//public Vector3 normal;
}
private Vector3[] multiple(Vector3[] a,float m){
	Vector3[] result;
	result.length = a.length;
	for (int i = 0;i < a.length;i ++){
		result[i] = a[i] * m;
	}
	return result;
}
private Vector3[] scale(Vector3[] a,float sx ,float sy ,float sz){
	Vector3[] result;
	result.length = a.length;
	for (int i = 0;i < a.length;i ++){
		result[i] = new Vector3(a[i].x * sx ,a[i].y * sy ,a[i].z * sz);
	}
	return result;
}
public class Shape:Cloneable{
	
	private Vector3[] fixedV;
	public Vector3[] v;
	public int[][] wires;
	public polyRect[] rects;

	public this(){
		
	}
	public this(Vector3[] v ,int[][] wires, polyRect[] rects){
		set(v ,wires ,rects);
	}
	public Shape multi(float mul){
		v = multiple(v ,mul);
		return this;
	}
	public Shape scalef(float sx ,float sy ,float sz){
		scale(v ,sx ,sy ,sz);
		return this;
	}
	public void set(Vector3[] v ,int[][] wires, polyRect[] rects){
		//this.fixedV = v.dup;
		this.fixedV.length = v.length;
		this.v.length = fixedV.length;
		for(int i = fixedV.length - 1;0 <= i;i --){
			this.fixedV[i] = new Vector3(v[i]);
			this.v[i] = new Vector3(fixedV[i]);
		}
		this.wires.length = wires.length;
		for(int i = wires.length - 1;0 <= i;i --){
			this.wires[i] = wires[i].dup;
		}
		this.rects.length = rects.length;
		for(int i = rects.length - 1;0 <= i;i --){
			for(int j = rects[i].v.length - 1;0 <= j;j --){
				this.rects[i].v[j] = rects[i].v[j];
			}
		}
		
	}
	/*
	public Shape opAdd(Shape s){
		Shape result = new Shape();
		
		result.fixedV = this.fixedV.dup ~ s.fixedV.dup;
		result.v.length = result.fixedV.length;
		
		for(int i = fixedV.length - 1;0 <= i;i --){
			result.v[i] = new Vector3(fixedV[i]);
		}
		
		result.wires.length = this.wires.length + s.wires.length;
		for(int i = this.wires.length - 1;0 <= i;i --){
			result.wires[i] = this.wires[i].dup;
		}
		
		for(int i = result.wires.length - 1 ;this.wires.length <= i;i --){
			result.wires[i].length = s.wires[i - this.wires.length].length;
			for(int j = s.wires[i - this.wires.length].length; 0 <= j;j --){
				result.wires[i][j] = 0;//s.wires[i - this.wires.length][j] + this.v.length;
			}
		}
		
		result.rects.length = this.rects.length + s.rects.length;
		for(int i = this.rects.length - 1;0 <= i;i --){
			for(int j = this.rects[i].v.length - 1;0 <= j;j --){
				result.rects[i].v[j] = 0;//this.rects[i].v[j];
			}
		}
		for(int i = result.rects.length - 1;this.wires.length <= i;i --){
			for(int j = s.rects[i - this.wires.length].v.length - 1;0 <= j;j --){
				result.rects[i].v[j] = 0;//this.rects[i].v[j] + this.v.length;
			}
		}
		
		return result;
	}
	*/
	public Object clone(){
    Shape shape = new Shape(v ,wires ,rects);
    
    return shape;
  }
	public void resetVertex(){
		for(int i = v.length - 1;0 <= i;i --){
			v[i] = new Vector3(fixedV[i]);
		}
	}
	public Shape reverseX(){
		foreach(inout vec;v){
			vec.x = -vec.x;
		}
		return this;
	}
	public Shape reverseY(){
		foreach(inout vec;v){
			vec.y = -vec.y;
		}
		return this;
	}
	public Shape reverseZ(){
		foreach(inout vec;v){
			vec.z = -vec.z;
		}
		return this;
	}
	public void rotated(double rad ,double x,double y ,double z){
		
		
		Vector3 u = vec3Normalize(new Vector3(x ,y ,z));
		double sin = sin(rad);
		double cos = cos(rad);
		double[3][3] UU;
		UU[0][0] = u.x * u.x;UU[0][1] = u.x * u.y;UU[0][2] = u.x * u.z;
		UU[1][0] = u.y * u.x;UU[1][1] = u.y * u.y;UU[1][2] = u.y * u.z;
		UU[2][0] = u.z * u.x;UU[2][1] = u.z * u.y;UU[2][2] = u.z * u.z;
		double[3][3] T ;
		T[0][0] = cos + (1 - cos) * UU[0][0]; T[0][1] = (1 - cos) * UU[0][1] - u.z * sin; T[0][2] = (1 - cos) * UU[0][2] + u.y * sin;
		T[1][0] = (1 - cos) * UU[1][0] + u.z * sin; T[1][1] = cos + (1 - cos) * UU[1][1]; T[1][2] = (1 - cos) * UU[1][2] - u.x * sin;
		T[2][0] = (1 - cos) * UU[2][0] - u.y * sin; T[2][1] = (1 - cos) * UU[2][1] + u.x * sin; T[2][2] = cos + (1 - cos) * UU[2][2];
		for(int i = v.length - 1;0 <= i;i --){
			v[i] = new Vector3(
				T[0][0] * v[i].x + T[0][1] * v[i].y + T[0][2] * v[i].z ,
				T[1][0] * v[i].x + T[1][1] * v[i].y + T[1][2] * v[i].z ,
				T[2][0] * v[i].x + T[2][1] * v[i].y + T[2][2] * v[i].z
				);
		}
		
	}
	/*
	public Shape translate(Vector3 dvec){
		
		for(int i = fixedV.length - 1;0 <= i;i --){
			fixedV[i] += dvec;
		}
		return this;
	}
	*/
	
	public void transformMatrix(Matrix t){
		for(int i = v.length - 1;0 <= i;i --){
			v[i] = vec3translate(v[i] , t);
		}
	}
	
	public void rollX(float d) {
		
		float sin = sin(d);
		float cos = cos(d);

		
		for(int i = v.length - 1;0 <= i;i --){
			float ty = v[i].y * cos - v[i].z * sin;
    	v[i].z = v[i].y * sin + v[i].z * cos;
    	v[i].y = ty;
		}

  }

  public void rollY(float d) {
		

		float sin = sin(d);
		float cos = cos(d);

		for(int i = v.length - 1;0 <= i;i --){
			float tx = v[i].x * cos - v[i].z * sin;
    	v[i].z = v[i].x * sin + v[i].z * cos;
    	v[i].x = tx;
		}
 
  }

  public void rollZ(float d) {
		float sin = sin(d);
		float cos = cos(d);

		for(int i = v.length - 1;0 <= i;i --){
    	float tx = v[i].x * cos - v[i].y * sin;
    	v[i].y = v[i].x * sin + v[i].y * cos;
    	v[i].x = tx;
		}
  }
	

}

public class SH_Pole:Shape{

	public this(float[] a ,float[] b ,int segment){
		if(a.length != b.length || a.length < 1){
			Log_write("Pole  arguments error" );
			throw new Exception("Pole  arguments error" );
		}
		Vector3[][] vec;
		vec.length = segment;
		for(int i = segment - 1;0 <= i;i --){
			vec[i].length = a.length;
		}
		for(int i = 0;i < segment ;i ++){
      for(int j = 0;j < a.length ;j ++){
				vec[i][j] = new Vector3();
        vec[i][j].x = a[j];
        vec[i][j].y = b[j] * cos(cast(float)i / cast(float)segment * PI * 2.0f);
        vec[i][j].z = b[j] * sin(cast(float)i / cast(float)segment * PI * 2.0f);
      }
    }
		joint(vec);
		
	}
	
	public this(float[][] a ,float[][] b ,int segment){
		
		// make wire frame
		
		if(a.length != b.length || a.length < 1){
			Log_write("Pole  arguments error" );
			throw new Exception("Pole  arguments error" );
		}
		int maxNum = 0;
		 for(int i = a.length -1;0 <= i;i --){
      if(a[i].length != b[i].length || a[i].length < 1){
        Log_write("Pole  arguments error" );
				throw new Exception("Pole  arguments error" );
      }
      if(maxNum < a[i].length)maxNum = a[i].length;
    }
		
		Vector3[][] vec;
		vec.length = segment;
		for(int i = segment - 1;0 <= i;i --){
			vec[i].length = maxNum;
		}
		int k,l;
		k = 0;
		for(int i = 0;i < segment ;i ++){
      for(int j = 0;j < maxNum ;j ++){
				if(j < a[k].length)l = j;
        else l = a[k].length - 1;
				vec[i][j] = new Vector3();
        vec[i][j].x = a[k][l];
        vec[i][j].y = b[k][l] * cos(cast(float)i / cast(float)segment * PI * 2.0f);
        vec[i][j].z = b[k][l] * sin(cast(float)i / cast(float)segment * PI * 2.0f);
      }
			k ++;
      if(a.length <= k)k = 0;
    }
		joint(vec);
		
	}
	public void joint(Vector3[][] vec){
		Vector3[] tv;
		polyRect[] trects;
		int[] twires;
		
		int number = vec[0].length;
		for(int i = vec.length -1;0 <= i;i --){
      if(vec[i].length != number || number < 1){
        Log_write("Pole joint arguments error" );
				throw new Exception("Pole joint arguments error" );
      }
    }
		tv.length = vec.length * vec[0].length;
		for(int i = 0;i < vec.length ;i ++){
      for(int j = 0;j < vec[i].length ;j ++){
				tv[i * vec[0].length + j] = vec[i][j];
      }
    }
		///*
		twires.length = (2 * vec.length + 1) * vec[0].length;
		int num = 0;
		 for(int j = 0;j < vec[0].length ;j ++){
      twires[num] = j;
      num ++;
      for(int i = 0;i < vec.length ;i ++){
				twires[num] = i * vec[0].length + j;
        num ++;
      }
      twires[num] = j;
      num ++;
    }
		///*
		int seg = 0;
    while(true){
      seg ++;
      if(vec.length <= seg)break; 
      for(int j = vec[0].length - 1;0 <= j ;j --){
				twires[num] = seg * vec[0].length + j;
        num ++;
      }
      seg ++;
      if(vec.length <= seg)break; 
      for(int j = 0;j < vec[0].length ;j ++){
				twires[num] = seg * vec[0].length + j;
        num ++;
      }
    }
		//make polygon
		///*
		trects.length = vec.length  * (vec[0].length - 1);
		num = 0;
		for(int i = 0;i < vec[0].length -1 ;i ++){
			trects[num].v[0] = (vec.length - 1) * vec[0].length + i;
			trects[num].v[1] = (vec.length - 1) * vec[0].length + i + 1;
			trects[num].v[2] = i + 1;
			trects[num].v[3] = i;
		//	rects[num].normal = getNormal(rects[num].v);//vec3NormalLine(rects[num].v[0] ,rects[num].v[2], rects[num].v[3]);
			num ++;
		}
		///*
		for(int i = 0;i < vec.length - 1;i ++){
			for(int j = 0;j < vec[i].length - 1;j ++){
				trects[num].v[0] = i * vec[0].length + j;
				trects[num].v[1] = i * vec[0].length + j + 1;
				trects[num].v[2] = (i + 1) * vec[0].length + j + 1;
				trects[num].v[3] = (i + 1) * vec[0].length + j;
			//	rects[num].normal = getNormal(rects[num].v);//vec3NormalLine(rects[num].v[0] ,rects[num].v[2], rects[num].v[3]);
				num ++;
			}
		}
		
		int[][] ttwires;
		ttwires.length = 1;
		ttwires[0] = twires.dup;
		set(tv ,ttwires ,trects);
		
		
	
	}
	private Vector3 getNormal(Vector3[4] v){
		Vector3 v0,v1,v2;
		v0 = v[0];
		if((1e-6) <= v0.dist(v[1]))v1 = v[1];
		else v1 = v[2];
		v2 = v[3];
		return vec3NormalLine(v0 ,v1, v2);
	}

}

public class SH_Pot:Shape{
	public this(float[] a ,float[] b ,float[] z ,float[] scale){
		
		Vector3[][] vec;
		if(a.length != b.length){
			Log_write("Pot arguments error");
			throw new Exception("Pot arguments error");
		}
		if(z.length != scale.length){
			Log_write("Pot arguments error");
			throw new Exception("Pot arguments error");
		}
		
		vec.length = z.length;
		for(int i = 0;i < vec.length; i ++){
			vec[i].length = a.length;
		}
		
		for(int i = 0;i < vec.length ;i ++){
			for(int j = 0;j < vec[i].length ;j ++){
				vec[i][j] = new Vector3(a[j] * scale[i] ,b[j] * scale[i] ,z[i]);
			}
		}
		
		joint(vec);
		
	}
	public void joint(Vector3[][] vec){
		
		Vector3[] tv;
		polyRect[] trects;
		int[][] twires;
		
		int number = vec[0].length;
		for(int i = vec.length -1;0 <= i;i --){
      if(vec[i].length != number || number < 1){
        Log_write("Pot joint arguments error" );
				throw new Exception("Pot joint arguments error" );
      }
    }
		
		tv.length = vec.length * vec[0].length;
		for(int i = 0;i < vec.length ;i ++){
	    for(int j = 0;j < vec[0].length ;j ++){
				tv[i * vec[0].length + j] = vec[i][j];
	    }
	  }
		twires.length = vec.length + vec[0].length;
		for(int i = 0;i < vec.length;i ++){
			twires[i].length = vec[i].length + 1;
		}
		for(int i = vec.length;i < twires.length;i ++){
			twires[i].length = vec.length;
		}
		for(int i = 0;i < vec.length;i ++){
			for(int j = 0;j < vec[i].length;j ++){
				twires[i][j] = i * vec[0].length + j;
			}
			twires[i][vec[i].length] = i * vec[0].length + 0;
		}
		for(int j = 0;j < vec[0].length;j ++){
			for(int i = 0;i < vec.length;i ++){
				twires[vec.length + j][i] = i * vec[0].length + j;
			}
		}
		int num = 0;
		/*
		//
		twires.length = ((vec[0].length + 1) * vec.length) + vec.length + vec.length * (vec[0].length - 1) + (vec[0].length - 1);
		int num = 0;
		
		for(int i = 0;i < vec.length ;i ++){
  	  twires[num] = i * vec[0].length + 0;
  	  num ++;
  	  for(int j = 0;j < vec[0].length ;j ++){
				twires[num] = i * vec[0].length + j;
  	    num ++;
  	  }
  	  twires[num] = i * vec[0].length + 0;
  	  num ++;
  	}
		
		int k ,l;
		l = 1;
		
		while(true){
			if(vec[0].length <= l)break;
			for(k = vec.length - 1;0 <= k;k --){
				twires[num] = k * vec[0].length + l;
				num ++;
			}
			l ++;
			if(vec[0].length <= l)break;
			for(k = 0;k < vec.length;k ++){
				twires[num] = k * vec[0].length + l;
				num ++;
			}
			l ++;
		}
		*/
		//
		trects.length = (vec.length - 1) * vec[0].length;
		num = 0;
		for(int i = 0;i < vec.length - 1;i ++){
			for(int j = 0;j < vec[0].length - 1;j ++){
				trects[num].v[0] = i * vec[0].length + j;
				trects[num].v[1] = (i + 1) * vec[0].length + j;
				trects[num].v[2] = (i + 1) * vec[0].length + j + 1;
				trects[num].v[3] = i * vec[0].length + j + 1;
				num ++;
			}
		}
		for(int i = 0;i < vec.length - 1;i ++){
			trects[num].v[0] = i * vec[0].length + vec[0].length - 1;
			trects[num].v[1] = (i + 1) * vec[0].length + vec[0].length - 1;
			trects[num].v[2] = (i + 1) * vec[0].length + 0;
			trects[num].v[3] = i * vec[0].length + 0;
			num ++;
		}
		int length = vec.length;
		if((1e-6) <= vec[0][0].size()){
			polyRect[] cover;
			tv.length = tv.length + vec[0].length;
			cover.length =	vec[0].length;
			length ++;
			for(int i = 0;i < vec[0].length;i ++){
				tv[tv.length - vec[0].length + i] = new Vector3(0 ,0 ,vec[0][0].z);
			}
			num = 0;
			for(int j = 0;j < vec[0].length - 1;j ++){
				cover[num].v[0] = 0 * vec[0].length + j;
				cover[num].v[1] = (length - 1) * vec[0].length + j;
				cover[num].v[2] = (length - 1) * vec[0].length + j + 1;
				cover[num].v[3] = 0 * vec[0].length + j + 1;
				num ++;
			}
			cover[num].v[0] = 0 * vec[0].length + vec[0].length - 1;
			cover[num].v[1] = (length - 1) * vec[0].length + vec[0].length - 1;
			cover[num].v[2] = (length - 1) * vec[0].length + 0;
			cover[num].v[3] = 0 * vec[0].length + 0;
			num ++;
			trects = trects~cover;
		}
		if((1e-6) <= vec[vec.length - 1][0].size()){
			polyRect[] cover;
			tv.length = tv.length + vec[0].length;
			cover.length =	vec[0].length;
			length ++;
			for(int i = 0;i < vec[0].length;i ++){
				tv[tv.length - vec[0].length + i] = new Vector3(0 ,0 ,vec[vec.length - 1][0].z);
			}
			num = 0;
			for(int j = 0;j < vec[0].length - 1;j ++){
				cover[num].v[0] = (vec.length - 1) * vec[0].length + j;
				cover[num].v[1] = (length - 1) * vec[0].length + j;
				cover[num].v[2] = (length - 1) * vec[0].length + j + 1;
				cover[num].v[3] = (vec.length - 1) * vec[0].length + j + 1;
				num ++;
			}
			cover[num].v[0] = (vec.length - 1) * vec[0].length + vec[0].length - 1;
			cover[num].v[1] = (length - 1) * vec[0].length + vec[0].length - 1;
			cover[num].v[2] = (length - 1) * vec[0].length + 0;
			cover[num].v[3] = (vec.length - 1) * vec[0].length + 0;
			num ++;
			trects = trects~cover;
		}
		
		int[][] ttwires;
		//ttwires.length = 1;
		//ttwires[0] = twires.dup;
		set(tv ,twires ,trects);
	
	}
}
public class SH_Sphere:SH_Pole{
	public this(double scale ,int segment){
		float[] a ,b;
		a.length = segment;
		b.length = segment;
		for(int i = 0;i < segment;i ++){
			a[i] = cos(cast(double)i / cast(double)(segment - 1) * PI) * scale;
			b[i] = sin(cast(double)i / cast(double)(segment - 1) * PI) * scale;
		}
		super(a ,b ,segment);
	}
}

public class SH_Oval:SH_Pot{
	public this(double width ,double height ,int segment){
		float[] a ,b,z,scale;
		a.length = segment;
		b.length = segment;
		z.length = 1;
		scale.length = 1;
		z[0] = 0.0;
		scale[0] = 1.0;
		for(int i = 0;i < segment;i ++){
			a[i] = cos(cast(double)i / cast(double)(segment - 1) * 2.0 *PI) * width;
			b[i] = sin(cast(double)i / cast(double)(segment - 1) * 2.0 * PI) * height;
		}
		super(a ,b ,z ,scale);
	}
}
