/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package jp.mydns.masahase.abaqus;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import jp.mydns.masahase.AbaqusResult.AbaqusResultType;
import jp.mydns.masahase.AbaqusResult.ElementType;
import jp.mydns.masahase.AbaqusResult.NodeRefType;
import jp.mydns.masahase.AbaqusResult.NodeType;
import jp.mydns.masahase.InherentStrain.InherentStrainType;
import jp.mydns.masahase.operateXML.AbaqusResultUnMarshaller;
import jp.mydns.masahase.operateXML.InherentStrainUnMarshaller;
import jp.mydns.masahase.sdes.common.FemFunc;
import jp.mydns.masahase.util.Pair;
import jp.mydns.masahase.weldinglines.CompositeLine;
import jp.mydns.masahase.weldinglines.CurveLine;
import jp.mydns.masahase.weldinglines.StraightLine;
import jp.mydns.masahase.weldinglines.WeldingLine;
import jp.mydns.masahase.weldinglines.WeldingLines;

/**
 * 固有ひずみを溶接線に沿ってメッシュに貼り付けその結果をAbaqus用のサブルーチンとして出力
 * 
 * @author MASA.H
 */
public class PastePE {
	public PastePE() {
		WeldLines = new ArrayList<PastePE.infoWeldLine>();
		TargetMesh = null;
	}

	/**
	 * 溶接線の追加
	 * 
	 * @param wl
	 *            溶接線情報
	 * @param time
	 *            溶接開始時間
	 * @return 成功したかどうか
	 */
	public boolean addWeldLine(jp.mydns.masahase.abaqus.weldline.IWeldLine wl,
			double time) {
		boolean ret = false;
		infoWeldLine tmp = new infoWeldLine();
		tmp.wl = wl;
		tmp.time = time;
		ret = WeldLines.add(tmp);
		return ret;
	}

	/**
	 * 貼り付け対象メッシュの設定
	 * 
	 * @param art
	 *            対象となるメッシュを含んだ結果ファイル
	 */
	public void setMesh(AbaqusResultType art) {
		TargetMesh = art;
		ele_cent_cache = null;
	}

	private AbaqusResultType TargetMesh;
	private final List<infoWeldLine> WeldLines;

	class infoWeldLine {
		jp.mydns.masahase.abaqus.weldline.IWeldLine wl;
		public double time;
	}

	/**
	 * 貼り付け開始
	 */
	public void paste() {
		PrintStream ps = new PrintStream(OutStrm);
		ps
				.println("      subroutine uexpan(expan,dexpandt,temp,time,dtime,predef,dpred,");
		ps.println("     $     statev,cmname,nstatv,noel)");
		ps.println("      include 'aba_param.inc'");
		ps.println("      character*80 cmname");
		ps
				.println("      dimension expan(*),dexpandt(*),temp(2),time(2),predef(*),");
		ps.println("     $     dpred(*),statev(nstatv)");
		ps.println("      EXPAN(1)=0D0");
		ps.println("      EXPAN(2)=0D0");
		ps.println("      EXPAN(3)=0D0");
		ps.println("      EXPAN(4)=0D0");
		ps.println("      EXPAN(5)=0D0");
		ps.println("      EXPAN(6)=0D0");
		ps.flush();
		if (TargetMesh != null) {
			for (infoWeldLine iwl : WeldLines) {
				paste(iwl);
			}
		}
		ps.println("      DEXPANDT(1)=0D0");
		ps.println("      DEXPANDT(2)=0D0");
		ps.println("      DEXPANDT(3)=0D0");
		ps.println("      DEXPANDT(4)=0D0");
		ps.println("      DEXPANDT(5)=0D0");
		ps.println("      DEXPANDT(6)=0D0");
		ps.println("      RETURN");
		ps.println("      end");
		ps.flush();
	}

	int thread_size = 3;
	ExecutorService e;

	private double[][] ele_cent_cache;

	private void paste(infoWeldLine iwl) {
		if (ele_cent_cache == null) {
			ele_cent_cache = new double[TargetMesh.getElement().size()][4];
		}
		e = Executors.newFixedThreadPool(thread_size);
		ArrayList<Future<Pair<Long, double[]>>> afpe = new ArrayList<Future<Pair<Long, double[]>>>();
		for (ElementType et : TargetMesh.getElement()) {
			afpe.add(e.submit(new Paste(iwl, et)));
		}
		PrintStream ps = new PrintStream(OutStrm);
		ps.printf("      IF(TIME(2) .LE. %g .AND. TIME(2) .GT. %g) THEN\n",
				iwl.time + 1, iwl.time);
		for (Future<Pair<Long, double[]>> fpe : afpe) {
			try {
				Pair<Long, double[]> pe = fpe.get();
				if (pe != null) {
                    if(FemFunc.calcEstimateStrain(pe.getB())>1.0e-6){
					ps.printf("      IF(NOEL .EQ. %d ) THEN\n", pe.getA()
							.longValue());
					for (int i = 0; i < 6; i++) {
						if (pe.getB()[i] != 0.0
								&& !java.lang.Double.isNaN(pe.getB()[i]))
							ps
									.printf(
											"      EXPAN(%d)=EXPAN(%d)+TEMP(2)*(%11.10e)\n",
											i + 1, i + 1, pe.getB()[i] / 100);
					}
					ps.println("      END IF");
                    }
				}
			} catch (InterruptedException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			} catch (ExecutionException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		}
		e.shutdown();
		ps.println("      END IF");
	}

	/**
	 * 出力ストリームの設定
	 * 
	 * @param os
	 *            出力ストリーム
	 */
	public void setOutput(OutputStream os) {
		OutStrm = os;
	}

	private OutputStream OutStrm;

	protected class Paste implements Callable<Pair<Long, double[]>> {
		public Paste(infoWeldLine iwl, ElementType et) {
			this.iwl = iwl;
			this.et = et;
		}

		private final infoWeldLine iwl;
		private final ElementType et;

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.util.concurrent.Callable#call()
		 */
		@Override
		public Pair<Long, double[]> call() throws Exception {
			Pair<Long, double[]> ret = null;
			synchronized (ele_cent_cache) {
				if (ele_cent_cache == null) {
					ele_cent_cache = new double[TargetMesh.getElement().size()][4];
				}
			}
			double[] x = new double[3];
			double[] pe;
			if (ele_cent_cache[(int) et.getId() - 1][3] != 0) {
				x = ele_cent_cache[(int) et.getId() - 1];
			} else if (et.getType().matches(".*C3D8.*")) {
				// 8節点レンガ型要素の場合
				double[][] tmp_x = new double[3][8];
				for (int i = 0; i < 8; i++) {
					int nid = (int) et.getNodeRef().get(i).getNodeId();
					NodeType nd = TargetMesh.getNode().get(nid - 1);
					if (nd.getId() != nid) {
						for (NodeType nt : TargetMesh.getNode()) {
							if (nt.getId() == nid) {
								nd = nt;
								break;
							}
						}
					}
					tmp_x[0][i] = nd.getC1();
					tmp_x[1][i] = nd.getC2();
					tmp_x[2][i] = nd.getC3();
				}
				x[0] = FemFunc.formFunction8Blick(0, 0, 0, tmp_x[0]);
				x[1] = FemFunc.formFunction8Blick(0, 0, 0, tmp_x[1]);
				x[2] = FemFunc.formFunction8Blick(0, 0, 0, tmp_x[2]);
			} else if (et.getType().matches(".*C2D4.*")) {
				// 4節点四角形要素の場合
				double[][] tmp_x = new double[3][4];
				for (int i = 0; i < 4; i++) {
					int nid = (int) et.getNodeRef().get(i).getNodeId();
					NodeType nd = TargetMesh.getNode().get(nid - 1);
					if (nd.getId() != nid) {
						for (NodeType nt : TargetMesh.getNode()) {
							if (nt.getId() == nid) {
								nd = nt;
								break;
							}
						}
					}
					tmp_x[0][i] = nd.getC1();
					tmp_x[1][i] = nd.getC2();
					tmp_x[2][i] = nd.getC3();
				}
				x[0] = FemFunc.formFunction4Square(0, 0, tmp_x[0]);
				x[1] = FemFunc.formFunction4Square(0, 0, tmp_x[1]);
				x[2] = FemFunc.formFunction4Square(0, 0, tmp_x[2]);
			} else {
				x[0] = 0;
				x[1] = 0;
				x[2] = 0;
				for (NodeRefType nrt : et.getNodeRef()) {
					NodeType nd = TargetMesh.getNode().get(
							(int) (nrt.getNodeId() - 1));
					if (nd.getId() != nrt.getNodeId()) {
						for (NodeType nt : TargetMesh.getNode()) {
							if (nt.getId() == nrt.getNodeId()) {
								nd = nt;
								break;
							}
						}
					}
					x[0] += nd.getC1();
					x[1] += nd.getC2();
					x[2] += nd.getC3();
				}
				x[0] /= et.getNodeRef().size();
				x[1] /= et.getNodeRef().size();
				x[2] /= et.getNodeRef().size();
			}
			synchronized (ele_cent_cache) {
				ele_cent_cache[(int) et.getId() - 1][0] = x[0];
				ele_cent_cache[(int) et.getId() - 1][1] = x[1];
				ele_cent_cache[(int) et.getId() - 1][2] = x[2];
				ele_cent_cache[(int) et.getId() - 1][3] = 1;
			}

			pe = iwl.wl.getPE(x[0], x[1], x[2]);
			if ((pe[0] != 0.0 || pe[1] != 0.0 || pe[2] != 0.0 || pe[3] != 0.0
					|| pe[4] != 0.0 || pe[5] != 0.0)
					&& (!java.lang.Double.isNaN(pe[0])
							&& !java.lang.Double.isNaN(pe[1])
							&& !java.lang.Double.isNaN(pe[2])
							&& !java.lang.Double.isNaN(pe[3])
							&& !java.lang.Double.isNaN(pe[4]) && !java.lang.Double
							.isNaN(pe[5]))) {
				ret = new Pair<Long, double[]>(Long.valueOf(et.getId()), pe);
			}
			return ret;
		}

	}

	/**
	 * 簡易UI
	 * 
	 * @param args
	 */
	@SuppressWarnings("unchecked")
	public static void main(String[] args) {
		if (args.length < 2) {
			System.err.println("Need a XML file and output file name.");
			System.exit(1);
		}
		ExecutorService exec = Executors.newCachedThreadPool();
		try {
			System.out.println("Initialize...");
			InputStream xmlfile = new FileInputStream(args[0]);
			JAXBContext jaxbcont = JAXBContext
					.newInstance("jp.mydns.masahase.weldinglines");
			Unmarshaller um = jaxbcont.createUnmarshaller();
			WeldingLines wls = ((JAXBElement<WeldingLines>) um
					.unmarshal(xmlfile)).getValue();
			PastePE ppe = new PastePE();
			Future<AbaqusResultType> fart = exec
					.submit(new AbaqusResultUnMarshaller(new FileInputStream(
							wls.getMeshData())));
			System.out.println("Set WeldingLine Information.");
            System.out.println("There're "+wls.getLine().size()+" line(s).");
			for (JAXBElement<? extends WeldingLine> wle : wls.getLine()) {
				if (wle.getValue() instanceof StraightLine) {
					StraightLine sl = (StraightLine) wle.getValue();
					double[] start, end, ref;
					Future<InherentStrainType> fist = exec
							.submit(new InherentStrainUnMarshaller(
									new FileInputStream(sl.getStrainDataFile())));
					start = new double[3];
					end = new double[3];
					ref = new double[3];
					start[0] = sl.getStart().getX();
					start[1] = sl.getStart().getY();
					start[2] = sl.getStart().getZ();
					end[0] = sl.getEnd().getX();
					end[1] = sl.getEnd().getY();
					end[2] = sl.getEnd().getZ();
					ref[0] = sl.getRef().getX();
					ref[1] = sl.getRef().getY();
					ref[2] = sl.getRef().getZ();
					InherentStrainType ist = fist.get();
					if (sl.isMirroring()) {
						ppe
								.addWeldLine(
										new jp.mydns.masahase.abaqus.weldline.StraightLineMirror(
												ist, start, end, ref), sl
												.getStartTime());
                        System.out.println("Add a StraightLineMirror.");
					} else {
						ppe
								.addWeldLine(
										new jp.mydns.masahase.abaqus.weldline.StraightLine(
												ist, start, end, ref), sl
												.getStartTime());
                        System.out.println("Add a StraightLine.");
					}
				} else if (wle.getValue() instanceof CurveLine) {
					CurveLine cl = (CurveLine) wle.getValue();
					double[] start, end, center;
					Future<InherentStrainType> fist = exec
							.submit(new InherentStrainUnMarshaller(
									new FileInputStream(cl.getStrainDataFile())));
					start = new double[3];
					end = new double[3];
					center = new double[3];
					start[0] = cl.getStart().getX();
					start[1] = cl.getStart().getY();
					start[2] = cl.getStart().getZ();
					end[0] = cl.getEnd().getX();
					end[1] = cl.getEnd().getY();
					end[2] = cl.getEnd().getZ();
					center[0] = cl.getCenter().getX();
					center[1] = cl.getCenter().getY();
					center[2] = cl.getCenter().getZ();
					InherentStrainType ist = fist.get();
					if (cl.isMirroring()) {
						ppe
								.addWeldLine(
										new jp.mydns.masahase.abaqus.weldline.CurveLineMirror(
												ist, start, end, center, cl
														.getAngle()), cl
												.getStartTime());
                       System.out.println("Add a CurveLineMirror.");
					} else {
						ppe
								.addWeldLine(
										new jp.mydns.masahase.abaqus.weldline.CurveLine(
												ist, start, end, center, cl
														.getAngle()), cl
												.getStartTime());
                       System.out.println("Add a CurveLine.");
					}
				} else if (wle.getValue() instanceof CompositeLine) {
					CompositeLine cl = (CompositeLine) wle.getValue();
					jp.mydns.masahase.abaqus.weldline.CompositeLine ol = new jp.mydns.masahase.abaqus.weldline.CompositeLine();
					for (JAXBElement<? extends WeldingLine> swle : cl.getSubLine()) {
						WeldingLine swl=swle.getValue();
						if (swl instanceof StraightLine) {
							StraightLine sl = (StraightLine) swl;
							double[] start, end, ref;
							Future<InherentStrainType> fist = exec
									.submit(new InherentStrainUnMarshaller(
											new FileInputStream(sl
													.getStrainDataFile())));
							start = new double[3];
							end = new double[3];
							ref = new double[3];
							start[0] = sl.getStart().getX();
							start[1] = sl.getStart().getY();
							start[2] = sl.getStart().getZ();
							end[0] = sl.getEnd().getX();
							end[1] = sl.getEnd().getY();
							end[2] = sl.getEnd().getZ();
							ref[0] = sl.getRef().getX();
							ref[1] = sl.getRef().getY();
							ref[2] = sl.getRef().getZ();
							InherentStrainType ist = fist.get();
							if (sl.isMirroring()) {
								ol
										.add(new jp.mydns.masahase.abaqus.weldline.StraightLineMirror(
												ist, start, end, ref));
							} else {
								ol
										.add(new jp.mydns.masahase.abaqus.weldline.StraightLine(
												ist, start, end, ref));
							}
						} else if (swl instanceof CurveLine) {
							CurveLine scl = (CurveLine) swl;
							double[] start, end, center;
							Future<InherentStrainType> fist = exec
									.submit(new InherentStrainUnMarshaller(
											new FileInputStream(scl
													.getStrainDataFile())));
							start = new double[3];
							end = new double[3];
							center = new double[3];
							start[0] = scl.getStart().getX();
							start[1] = scl.getStart().getY();
							start[2] = scl.getStart().getZ();
							end[0] = scl.getEnd().getX();
							end[1] = scl.getEnd().getY();
							end[2] = scl.getEnd().getZ();
							center[0] = scl.getCenter().getX();
							center[1] = scl.getCenter().getY();
							center[2] = scl.getCenter().getZ();
							InherentStrainType ist = fist.get();
							if (scl.isMirroring()) {
								ol
										.add(new jp.mydns.masahase.abaqus.weldline.CurveLineMirror(
												ist, start, end, center, scl
														.getAngle()));
							} else {
								ol
										.add(new jp.mydns.masahase.abaqus.weldline.CurveLine(
												ist, start, end, center, scl
														.getAngle()));
							}
						} else {
							System.err.println("Unsupport type:"
									+ swl.toString());
						}
					}
					ppe.addWeldLine(ol, cl.getStartTime());
                    System.out.println("Add a CompositeLine.");
				} else {
					System.err.println("Unsupport type:" + wle.getValue().toString());
				}
			}
			System.out.println("Set Mesh infomation.");
			AbaqusResultType art = fart.get();
			ppe.setMesh(art);
			System.out.println("Set Output.");
			OutputStream os = new FileOutputStream(args[1]);
			ppe.setOutput(os);
			System.out.println("Pasting...");
			ppe.paste();
			System.out.println("Done.");
			exec.shutdown();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (JAXBException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ExecutionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		exec.shutdown();
	}
}
