package engineering;

import math.Maths;
import math.geometry.IsoscelesTriangle;
import math.geometry.Point2D;
import math.matrix.Matrix3D;
import math.matrix.Vector3D;
import stdlib.Color;
import awt.Draw;
import exception.IllegalDimensionException;
import exception.IllegalSetupException;

public class Stress {
	/**
	 * stress tensor
	 */
	private Matrix3D sigma;
	
	/**
	 * principle stresses
	 */
	private Vector3D l;
	
	private double s1, s2, s3, c12, c23, c31, r12, r23, r31;
	
	/**
	 * principle directions
	 */
	private Matrix3D X;
	
	/**
	 * normal vector
	 */
	private Vector3D n;
	
	private Draw mohr = new Draw();

	/**
	 * null object
	 */
	public Stress(){
	}
	
	public Stress(double[][] s){
		sigma = new Matrix3D(s);
	}
	
	public Stress(Matrix3D s){
		sigma = s;
	}
	
	public Stress(double xx, double yy, double zz, double yz, double zx, double xy){
		sigma = new Matrix3D(new double[][]{
				{xx, xy, zx},
				{xy, yy, yz},
				{zx, yz, zz}
		});
	}
	
	public Matrix3D get(){ return sigma; }
	void set(double xx, double yy, double zz, double yz, double zx, double xy){
		sigma = new Matrix3D(new double[][]{
				{xx, xy, zx},
				{xy, yy, yz},
				{zx, yz, zz}
		});
	}
	public void println(){ sigma.setName("σ").println(); }
	
	public double getXX(){ return sigma.get(0, 0); }
	public double getYY(){ return sigma.get(1, 1); }
	public double getZZ(){ return sigma.get(2, 2); }
	public double getXY(){ return sigma.get(0, 1); }
	public double getYZ(){ return sigma.get(1, 2); }
	public double getZX(){ return sigma.get(0, 2); }

	void setXX(double s){sigma.set(0,0,s);}
	void setYY(double s){sigma.set(1,1,s);}
	void setZZ(double s){sigma.set(2,2,s);}
	void setXY(double s){sigma.set(0,1,s);sigma.set(1,0,s);}
	void setYZ(double s){sigma.set(1,2,s);sigma.set(2,1,s);}
	void setZX(double s){sigma.set(0,2,s);sigma.set(2,0,s);}
	
	private void eigendecomposition() {
		l = (Vector3D) sigma.eigenvaluesRe();
		X = (Matrix3D) sigma.eigenvectors();
		int imax = l.indexOfMax();
		int imin = l.indexOfMin();
		l.swap(imin, imax);
		X.swap(imin, imax, true);
//		Diagonal L = new Diagonal(l);
//		System.out.println("XLX'" + X.times(L).times(X.transpose()));
		s1 = l.get(0);
		s2 = l.get(1);
		s3 = l.get(2);
		c12 = (s1+s2)/2;             // center of circle 12
		r12 = (s1-s2)/2;             // radius of circle 12
		c23 = (s2+s3)/2;             // center of circle 23
		r23 = (s2-s3)/2;             // radius of circle 23
		c31 =          (s3+s1)/2;    // center of circle 31
		r31 = Math.abs((s3-s1)/2);   // radius of circle 31
	}

	public Vector3D getPrincipleStresses() {
		if(l == null) eigendecomposition();
		return l;
	}
	public void printlnPrincipleStresses(){ l.setName("σH").println(); }
	
	public double getPrincipleStress1() {
		return getPrincipleStresses().get(0);
	}
	public double getPrincipleStress2() {
		return getPrincipleStresses().get(1);
	}
	public double getPrincipleStress3() {
		return getPrincipleStresses().get(2);
	}
	
	public Matrix3D getPrincipleDirections() {
		if(X == null) eigendecomposition();
		return X;
	}
	public void printlnPrincipleDirections(){ X.setName("X").println(); }
	
	public Vector3D getPrincipleDirection1(){
		return (Vector3D) getPrincipleDirections().getN(0);
	}
	public Vector3D getPrincipleDirection2(){
		return (Vector3D) getPrincipleDirections().getN(1);
	}
	public Vector3D getPrincipleDirection3(){
		return (Vector3D) getPrincipleDirections().getN(2);
	}
	
	public Vector3D getNormalVector(){
		return n;
	}
	public void setNormalVector(double n1, double n2, double n3){
		n = (Vector3D) new Vector3D(n1,n2,n3).normalize();
	}
	public void printlnNormalVector() throws IllegalSetupException{
		if(n==null) throw new IllegalSetupException("Normal vector is not set up.");
		n.setName("n").println();
	}

/* * Tn * */
	private Vector3D stressVector(Vector3D n) {
		if(!n.isNormalized()) n.normalize();
		Vector3D result = null;
		try {
			result = (Vector3D) get().times(n);
		} catch (IllegalDimensionException e) { // will be checked earlier.
			e.printStackTrace();
		}
		return result;
	}
	public Vector3D getStressVector() throws IllegalSetupException {
		if(n==null) throw new IllegalSetupException("Normal vector is not set up.");
		return stressVector(n);
	}
	public void printlnStressVector() throws IllegalSetupException {
		getStressVector().setName("Tn").println();
	}
	/**
	 * @param n normal vector
	 * @return <b>T</b><sup>(n)</sup>=<b>&sigma;</b>&middot;<b>n</b>=
	 * &sigma;<sub>ij</sub>n<sub>i</sub><b>e</b><sub>j</sub> or
	 * T<sub>j</sub><sup>(n)</sup>=&sigma;<sub>ij</sub>n<sub>i</sub>
	 * @throws IllegalDimensionException
	 */
	public Vector3D getStressVector(Vector3D n) throws IllegalDimensionException {
		if(!n.isR3()) throw new IllegalDimensionException("vector not from R3");
		return stressVector(n);
	}
	public void printlnStressVector(Vector3D n) throws IllegalDimensionException{
		getStressVector(n).setName("Tn").println();
	}

/* * σn * */
	private double normalStress(Vector3D n) {
		double result = Double.NaN;
		try {
			result = getStressVector(n).dot(n);
		} catch (IllegalDimensionException e) { // will be checked earlier.
			e.printStackTrace();
		}
		return result;
	}
	public double getNormalStress() throws IllegalSetupException {
		if(n==null) throw new IllegalSetupException("Normal vector is not set up.");
		return normalStress(n);
	}
	public void printlnNormalStress() throws IllegalSetupException{
		System.out.println("σn = " + getNormalStress());
	}
	/**
	 * @param n
	 * @return &sigma;<sub>n</sub>=<b>T</b><sup>(n)</sup>&middot;<b>n</b>=
	 * T<sub>i</sub><sup>(n)</sup>n<sub>i</sub>=
	 * &sigma;<sub>ij</sub>n<sub>i</sub>n<sub>j</sub>=
	 * (<b>&sigma;</b>&middot;<b>n</b>)&middot;<b>n</b>
	 * @throws IllegalDimensionException
	 */
	public double getNormalStress(Vector3D n) throws IllegalDimensionException {
		return normalStress(n);
	}
	public void printlnNormalStress(Vector3D n) throws IllegalDimensionException{
		System.out.println("σn = " + getNormalStress(n));
	}

/* * τn * */
	private double shearStress(Vector3D n) {
		double result = Double.NaN;
		try {
			Vector3D Tn = getStressVector(n);
			double sn = getNormalStress(n);
			result = Math.sqrt(Tn.dot(Tn) -	(sn*sn));
		} catch (IllegalDimensionException e) { // will be checked earlier.
			e.printStackTrace();
		}
		return result;
	}
	public double getShearStress() throws IllegalSetupException {
		if(n==null) throw new IllegalSetupException("Normal vector is not set up.");
		return shearStress(n);
	}
	public void printlnShearStress() throws IllegalSetupException {
		System.out.println("τn = " + getShearStress());
	}
	/**
	 * @param n
	 * @return &tau;<sub>n</sub>=
	 * &radic;[(<b>T</b><sup>(n)</sup>)<sup>2</sup>-&sigma;<sub>n</sub><sup>2</sup>]=
	 * &radic;(<b>T</b><sup>(n)</sup>&middot;<b>T</b><sup>(n)</sup>-&sigma;<sub>n</sub><sup>2</sup>)
	 * @throws IllegalDimensionException
	 */
	public double getShearStress(Vector3D n) throws IllegalDimensionException{
		return shearStress(n);
	}
	public void printlnShearStress(Vector3D n) throws IllegalDimensionException{
		System.out.println("τn = " + getShearStress(n));
	}
	
	public Point2D getStressPoint() throws IllegalSetupException{
		return new Point2D(getNormalStress(), getShearStress());
	}
	public Point2D getStressPoint(Vector3D n) throws IllegalDimensionException{
		return new Point2D(getNormalStress(n), getShearStress(n));
	}
	
	@Override
	public String toString(){
		return sigma.toString();
	}
	
	public void plot() throws IllegalDimensionException{
		if(sigma==null) demo2();
		else{
			getPrincipleStresses();
			
			// set canvas range
			double max = Math.max(s1, 0);
			double min = Math.min(s3, 0);
			double dif = max-min;
			double add = dif*0.1;
			mohr.setScale(min-add, max+add, -dif/2-add, dif/2+add);
			
			mohr.setPenColor(Color.BLUE_GREEN);
			Draw.filledCircle(c31, 0, r31);
			mohr.setPenColor(Draw.getClearColor());
			Draw.filledCircle(c12, 0, r12);
			Draw.filledCircle(c23, 0, r23);
			mohr.setPenColor(Color.GRAY);
//			mohr.line(s3, 0, s1, 0);
			mohr.markX(s1);
			mohr.markX(s2);
			mohr.markX(s3);
			mohr.markY(r12);
			mohr.markY(r23);
			mohr.markY(r31);
			mohr.markY(-r12);
			mohr.markY(-r23);
			mohr.markY(-r31);
			mohr.cross();
			Draw.show();
			mohr.plot();
			plotNormalVector(new Vector3D(1,0,0));
			plotNormalVector(new Vector3D(0,1,0));
			plotNormalVector(new Vector3D(0,0,1));
			if(n!=null){
				plotNormalVector(n);
//				plotNormalVector(new Vector3D(1,1,1./100));
//				plotNormalVector(new Vector3D(1,1,1./1.5));
//				plotNormalVector(new Vector3D(1,1,1));
//				plotNormalVector(new Vector3D(1,1,2));
//				plotNormalVector(new Vector3D(1,1,3));
//				plotNormalVector(new Vector3D(1,1,5));
//				plotNormalVector(new Vector3D(1,1,10));
//				plotNormalVector(new Vector3D(1,1,100));
				plotArcs();
			}
		}
	}
	
	private void plotNormalVector(Vector3D n) throws IllegalDimensionException{
		double sigma = getNormalStress(n);
		double tau = getShearStress(n);
		mohr.mark(sigma, tau);
		mohr.markX(sigma);
		mohr.markY(tau);
	}
	
	private void plotArcs() throws IllegalDimensionException{
		// http://www.rockmechs.com/stress-strain/stress/mohr-circle-3d/
		// https://www.uwgb.edu/dutchs/structge/MOHR-3D.HTM
		// s1
		double a1 = Math.abs(90-X.getN(0).angleD(n));
//		System.out.println(a1);
		double h112 = IsoscelesTriangle.hypotenuseSideAngleD(r12, a1);
		double h131 = IsoscelesTriangle.hypotenuseSideAngleD(r31, a1);
		Point2D p112 = Point2D.polarToCartesianD(h112, a1);
		Point2D p131 = Point2D.polarToCartesianD(h131, a1);
//		mohr.mark(s1-p112.x(), p112.y());
//		mohr.mark(s1-p131.x(), p131.y());
//		mohr.line(s1-p112.x(), p112.y(), s1-p131.x(), p131.y());
		mohr.arc(c23, 0, getStressPoint(n).distance(c23,0), 
				Maths.atand(p112.y(),(s1-c23-p112.x())), 
				Maths.atand(p131.y(),(s1-c23-p131.x())));
		// s2
		double a2 = 90-X.getN(1).angleD(n);
//		System.out.println(a2);
//		System.out.println(Math.signum(a2));
		double h212 = IsoscelesTriangle.hypotenuseSideAngleD(r12, a2);
		double h223 = IsoscelesTriangle.hypotenuseSideAngleD(r23, a2);
		Point2D p212 = Point2D.polarToCartesianD(h212, a2);
		Point2D p223 = Point2D.polarToCartesianD(h223, a2);
//		mohr.mark(s2+p212.x(), -p212.y());
//		mohr.mark(s2-p223.x(), p223.y());
//		mohr.line(s2+p212.x(), -p212.y(), s2-p223.x(), p223.y());
//		mohr.arc(c31, 0, getStressPoint(n).distance(c31,0), 
//				Maths.atand(-p212.y(), (p212.x()-c31+s2)), 
//				Maths.atand(p223.y(), -(p223.x()+c31-s2)));
		mohr.arc(c31, 0, getStressPoint(n).distance(c31,0), 
				Maths.atand(Math.signum(a2)*p212.y(),(s2-c31+p212.x())), 
				Maths.atand(Math.signum(a2)*p223.y(),(s2-c31-p223.x())));
		// s3
		double a3 = Math.abs(90-X.getN(2).angleD(n));
//		System.out.println(a3);
		double h323 = IsoscelesTriangle.hypotenuseSideAngleD(r23, a3);
		double h331 = IsoscelesTriangle.hypotenuseSideAngleD(r31, a3);
		Point2D p323 = Point2D.polarToCartesianD(h323, a3);
		Point2D p331 = Point2D.polarToCartesianD(h331, a3);
//		mohr.mark(s3+p323.x(), p323.y());
//		mohr.mark(s3+p331.x(), p331.y());
//		mohr.line(s3+p323.x(), p323.y(), s3+p331.x(), p331.y());
		mohr.arc(c12, 0, getStressPoint(n).distance(c12,0), 
				Maths.atand(p331.y(),(s3-c12+p331.x())), 
				Maths.atand(p323.y(),(s3-c12+p323.x())));
	}
	
	public void demo1() throws IllegalDimensionException{
		set(100, 80, 90, 20, 20, 0);
		plot();
	}
	
	public void demo2() throws IllegalDimensionException{
		set(-71.861, 0, 26.256, 0, -32.549, 0);
		plot();
	}
	
	/**
	 * Demo
	 * @param args
	 */
	public static void main(String[] args) {
		Stress stress = new Stress();
		stress.set(5, 3, 1, 4, 6, 2);
		stress.setNormalVector(1./3,1./2,Math.sqrt(23)/6);
		stress.set(5, 2, 1, 0, 0, 0);
//		stress.setNormalVector(1./2,1./2,Math.sqrt(2)/2);
		try {
			stress.plot();
		} catch (IllegalDimensionException e) {
			e.printStackTrace();
		}
		stress.println();
		stress.printlnPrincipleStresses();
//		stress.printlnPrincipleDirections();
//		n = stress.getPrincipleDirection1();
		try {
			stress.printlnNormalVector();
			stress.printlnStressVector();
			stress.printlnNormalStress();
			stress.printlnShearStress();
		} catch (IllegalSetupException e) {
			e.printStackTrace();
		}
	}

}
