/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOUSE. See the GNU
 * General Public License for more details.
 *
 * You should have recieved a copy of the GNU General Public License
 * along with this program; if not write to the Free Software
 * Foundation, inc., 59 Temple Place, Suite 330, Boston MA 02111-1307
 * USA
 */
package j3d;
import java.awt.*;
import java.io.*;
import java.util.*;

import javax.swing.*;
import javax.swing.tree.*;
import java.awt.event.*;
import javax.swing.table.*;

import gui.*;
import j3d.nurbs.*;

/**
 * Insert the type's description here.
 *
 * @author: Yuriy Mikhaylovskiy
 */

public class _NurbSurface implements _Object, Serializable{
  private Canvas3D J3D;
  private PreProcessor PreP;
  private Vector nodesDB = new Vector();
  private Vector elementsDB = new Vector();
  protected String Id;
  public float msh_size;
  public float thickness;
  public Material material = null;
  public Color color = Color.black;
  private String msh_name = "Contact_Triangle";
  private int msh_type = Canvas3D.MESH_Contact_Triangle;
  protected int order;
  protected boolean selected = false;
  protected int numKnots, numUControlPoints, numVControlPoints;
  private _Point[][] mesh;
  private _Node[][] nodes;
  protected _CtrlPoint[] controlPoints;
  protected Nurbs surface;
  protected float[] uknots, vknots;
  private TableModel tableModel;
  private Vector dat;
  private String[] hdr;
  private int U,V;
  private JTable Table;
  private JTextField tf_u,tf_v,tf_size,tf_t;
  private JComboBox cb_type,cb_mat;

  public _NurbSurface(_CtrlPoint[] cP, int unum, int vnum, float msize, int mtype, Material m, float t) {
    msh_type=mtype;
    msh_size=msize;
    material=m;
    thickness=t;
    controlPoints = cP;
    numUControlPoints = unum;
    numVControlPoints = vnum;
    reset();
  }
  private void writeObject(ObjectOutputStream out)  throws IOException{
    out.writeObject(nodesDB);
    out.writeObject(elementsDB);
    out.writeObject(controlPoints);
    out.writeInt(numUControlPoints);
    out.writeInt(numVControlPoints);
    out.writeFloat(msh_size);
    out.writeFloat(thickness);
    out.writeObject(material);
    out.writeObject(color);
    out.writeObject(msh_name);
    out.writeInt(msh_type);
  }
  private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{
    nodesDB = (Vector)in.readObject();
    elementsDB = (Vector)in.readObject();
    controlPoints = (_CtrlPoint[])in.readObject();
    numUControlPoints = in.readInt();
    numVControlPoints = in.readInt();
    msh_size = in.readFloat();
    thickness = in.readFloat();
    material = (Material)in.readObject();
    color = (Color)in.readObject();
    selected = false;
    msh_name = (String)in.readObject();
    msh_type = in.readInt();
    for(int i=0; i<nodesDB.size(); i++) ((_Object)nodesDB.elementAt(i)).set_Id(i+"");
    for(int i=0; i<elementsDB.size(); i++) ((_Object)elementsDB.elementAt(i)).set_Id(i+"");
  }

  private void reset(){
    nodesDB.removeAllElements();
    elementsDB.removeAllElements();
    if(msh_type==Canvas3D.MESH_Contact_Triangle)msh_name = "Contact_Triangle";
    else if(msh_type==Canvas3D.MESH_Shell_C0_3)msh_name = "Shell_C0_3";
    else if(msh_type==Canvas3D.MESH_Shell_BT_4)msh_name = "Shell_BT_4";
    int k=0;
    float usum=0,vsum=0;
    for(int i=0; i<numUControlPoints; i++){
      for(int j=0; j<numVControlPoints-1; j++){
        vsum+=controlPoints[k].distance(controlPoints[k+1]);
        k++;
      }
    }
    k=0;
    for(int i=0; i<numVControlPoints; i++){
      for(int j=0; j<numUControlPoints-1; j++){
        usum+=controlPoints[k].distance(controlPoints[k+numVControlPoints]);
        k++;
      }
    }
    int udiv=(int)Math.round(usum/numVControlPoints/msh_size);
    int vdiv=(int)Math.round(vsum/numUControlPoints/msh_size);
    int order = 3;

    numKnots = numUControlPoints + order;
    uknots = new float[numKnots];
    for (int i=0; i<numKnots; i++) uknots[i] = i;
    uknots[0] = 0;
    uknots[1] = 0;
    uknots[2] = 0;
    uknots[numKnots-3] = numKnots-1;
    uknots[numKnots-2] = numKnots-1;
    uknots[numKnots-1] = numKnots-1;
    numKnots = numVControlPoints + order;
    vknots = new float[numKnots];
    for (int i=0; i<numKnots; i++)  vknots[i] = i;
    vknots[0] = 0;
    vknots[1] = 0;
    vknots[2] = 0;
    vknots[numKnots-3] = numKnots-1;
    vknots[numKnots-2] = numKnots-1;
    vknots[numKnots-1] = numKnots-1;
    this.generate(udiv,vdiv);
    nodes = new _Node[mesh.length][mesh[0].length];
    for(int i=0; i<mesh.length; i++)
      for(int j=0; j<mesh[0].length; j++){
        nodes[i][j]= new _Node(mesh[i][j].x,mesh[i][j].y,mesh[i][j].z){ public String get_Id(){ return Id+"."+super.get_Id(); }};
        nodesDB.addElement(nodes[i][j]);
      }
      for(int i=0; i<nodes.length-1; i++)
        for(int j=0; j<nodes[0].length-1; j++)
          if(msh_type==Canvas3D.MESH_Contact_Triangle || msh_type==Canvas3D.MESH_Shell_C0_3){
            elementsDB.addElement(new _Element3(nodes[i][j],nodes[i][j+1],nodes[i+1][j+1],msh_type,material,thickness){
              public String get_Id(){ return Id+"."+super.get_Id(); }
            });
            elementsDB.addElement(new _Element3(nodes[i][j],nodes[i+1][j+1],nodes[i+1][j],msh_type,material,thickness){
              public String get_Id(){ return Id+"."+super.get_Id(); }
            });
          }else if(msh_type==Canvas3D.MESH_Shell_BT_4){
            elementsDB.addElement(new _Element4(nodes[i][j],nodes[i][j+1],nodes[i+1][j+1],nodes[i+1][j],material,thickness){
              public String get_Id(){ return Id+"."+super.get_Id(); }
            });
          }
      for(int i=0; i<nodesDB.size(); i++) ((_Object)nodesDB.elementAt(i)).set_Id(i+"");
      for(int i=0; i<elementsDB.size(); i++) ((_Object)elementsDB.elementAt(i)).set_Id(i+"");
  }

  /*public _NurbSurface(_CtrlPoint[] cP, int unum, int vnum, int udiv, int vdiv) {
    int order = 3;
    color = Color.BLACK;
    this.controlPoints = cP;
    this.numUControlPoints = unum;
    this.numVControlPoints = vnum;
    numKnots = unum + order;
    uknots = new float[numKnots];
    for (int i=0; i<numKnots; i++) uknots[i] = i;
    uknots[0] = 0;
    uknots[1] = 0;
    uknots[2] = 0;
    uknots[numKnots-3] = numKnots-1;
    uknots[numKnots-2] = numKnots-1;
    uknots[numKnots-1] = numKnots-1;
    numKnots = vnum + order;
    vknots = new float[numKnots];
    for (int i=0; i<numKnots; i++)  vknots[i] = i;
    vknots[0] = 0;
    vknots[1] = 0;
    vknots[2] = 0;
    vknots[numKnots-3] = numKnots-1;
    vknots[numKnots-2] = numKnots-1;
    vknots[numKnots-1] = numKnots-1;
    this.generate(udiv,vdiv);
  }*/

  public void generate(int udiv, int vdiv) {
    numKnots         = uknots.length;
    order = numKnots - numUControlPoints;
    Knot u = new Knot(order, numUControlPoints);
    u.setKnots(uknots);
    numKnots         = vknots.length;
    order = numKnots - numVControlPoints;
    Knot v = new Knot(order, numVControlPoints);
    v.setKnots(vknots);
    Point4[][] points = new Point4[u.getNumControlPoints()][v.getNumControlPoints()];
    for (int i=0; i<u.getNumControlPoints(); i++) {
      for (int j=0; j<v.getNumControlPoints(); j++) {
        int index = j + i*v.getNumControlPoints();
        points[i][j] = new Point4(controlPoints[index].x,controlPoints[index].y,controlPoints[index].z,controlPoints[index].w);
      }
    }
    ControlNet controlNet = new ControlNet(u.getNumControlPoints(), v.getNumControlPoints(), points);
    Nurbs temp = new Nurbs(u, v, controlNet);
    surface = temp.tessellate(udiv,vdiv);
    mesh = new _Point[surface.getNumUControlPoints()][surface.getNumVControlPoints()];
    for (int i=0; i<surface.getNumUControlPoints(); i++)
      for (int j=0; j<surface.getNumVControlPoints(); j++)
        mesh[i][j] = surface.toCoordinateNode()[i][j];
  }

  public Object[] get_Array(Canvas3D j3d){
    int ncl=0;
    for(int i=0; i<nodesDB.size(); i++){
      _Node nd = (_Node)nodesDB.elementAt(i);
      if(nd.constraint!=null && j3d.SHOW_ID_CONSTRAINTS)ncl++;
      if(nd.load!=null && j3d.SHOW_ID_LOADS)ncl++;
    }
    if(j3d.SHOW_ID_LOADS){
      for(int i=0; i<elementsDB.size(); i++){
        if(elementsDB.elementAt(i) instanceof _Element3){
          _Element3 el3 = (_Element3)elementsDB.elementAt(i);
          if(el3.load!=null && el3.msh_type==Canvas3D.MESH_Shell_C0_3)ncl++;
        }else if(elementsDB.elementAt(i) instanceof _Element4 && ((_Element4)elementsDB.elementAt(i)).load!=null)ncl++;
      }
    }
    Object[] arr = new Object[nodesDB.size()*(j3d.SHOW_ID_NODE?2:1)+elementsDB.size()*(j3d.SHOW_ID_ELEMENT?2:1)+ncl];
    int k=0;
    for(int i=0; i<nodesDB.size(); i++){
      _Node nd = (_Node)nodesDB.elementAt(i);
      arr[k]= nd.get_Array(j3d)[0];
      k++;
      if(j3d.SHOW_ID_NODE){
        arr[k]=new shpOrientedText(nd.get_Id(),nd.x,nd.y,nd.z);
        k++;
      }
    }
    for(int i=0; i<elementsDB.size(); i++){
      arr[k]=((_Object)elementsDB.elementAt(i)).get_Array(j3d)[0];
      k++;
      if(j3d.SHOW_ID_ELEMENT){
        if(msh_type==j3d.MESH_Shell_BT_4){
          _Element4 el = (_Element4)elementsDB.elementAt(i);
          arr[k]=new shpOrientedText(el.get_Id(),(el.node1.x+el.node2.x+el.node3.x+el.node4.x)/4,(el.node1.y+el.node2.y+el.node3.y+el.node4.y)/4,(el.node1.z+el.node2.z+el.node3.z+el.node4.z)/4);
        }else{
          _Element3 el = (_Element3)elementsDB.elementAt(i);
          arr[k]=new shpOrientedText(el.get_Id(),(el.node1.x+el.node2.x+el.node3.x)/3,(el.node1.y+el.node2.y+el.node3.y)/3,(el.node1.z+el.node2.z+el.node3.z)/3);
        }
        k++;
      }
    }
    for(int i=0; i<nodesDB.size(); i++){
      _Node nd = (_Node)nodesDB.elementAt(i);
      if(nd.constraint!=null && j3d.SHOW_ID_CONSTRAINTS){
        arr[k]=new shpOrientedText(nd.constraint.name,nd.x,nd.y,nd.z,nd.constraint.color);
        k++;
      }
      if(nd.load!=null && j3d.SHOW_ID_LOADS){
        arr[k]=new shpOrientedText(nd.load.name,nd.x,nd.y,nd.z,nd.load.color);
      k++;
      }
    }
    if(j3d.SHOW_ID_LOADS){
      for(int i=0; i<elementsDB.size(); i++){
        if(elementsDB.elementAt(i) instanceof _Element3){
          _Element3 el3 = (_Element3)elementsDB.elementAt(i);
          if(el3.load!=null && el3.msh_type==Canvas3D.MESH_Shell_C0_3){
            arr[k]=new shpOrientedText(el3.load.name,(el3.node1.x+el3.node2.x+el3.node3.x)/3,(el3.node1.y+el3.node2.y+el3.node3.y)/3,(el3.node1.z+el3.node2.z+el3.node3.z)/3,el3.load.color);
            k++;
          }
        }else if(elementsDB.elementAt(i) instanceof _Element4 && ((_Element4)elementsDB.elementAt(i)).load!=null){
          _Element4 el4 = (_Element4)elementsDB.elementAt(i);
          arr[k]=new shpOrientedText(el4.load.name,(el4.node1.x+el4.node2.x+el4.node3.x+el4.node4.x)/4,(el4.node1.y+el4.node2.y+el4.node3.y+el4.node4.y)/4,(el4.node1.z+el4.node2.z+el4.node3.z+el4.node4.z)/4,el4.load.color);
          k++;
        }
      }
    }
    return arr;
  }

  public void mesh(int type, float size){}
  public boolean delete(){
    for(int i=0; i<elementsDB.size(); i++){
      if(((_Object)elementsDB.elementAt(i)).isSelected()){
        elementsDB.removeElementAt(i);
        i--;
      }
    }
    for(int i=0; i<nodesDB.size(); i++){
      _Node nd = (_Node)nodesDB.elementAt(i);
      if(nd.isSelected()){
        boolean using = false;
        for(int j=0; j<elementsDB.size(); j++){
          _Node[] nds = ((_Object)elementsDB.elementAt(j)).get_Nodes();
          for(int k=0; k<nds.length; k++){
            if(nds[k].equals(nd))using=true;
          }
        }
        if(!using){
          nodesDB.removeElementAt(i);
          i--;
        }
      }
    }
    return selected;
  }
  public void Select(String st){
    selected=st!=null;
    if(st==null){
      for(int i=0; i<nodesDB.size(); i++) ((_Node)nodesDB.elementAt(i)).Select(st);
      for(int i=0; i<elementsDB.size(); i++) ((_Object)elementsDB.elementAt(i)).Select(st);
    }
  }
  public void transform3D(Matrix3D t){
    for(int i=0; i<nodesDB.size(); i++) ((_Node)nodesDB.elementAt(i)).transform3D(t);
  }

  public String get_Id(){ return Id; }
  public void set_Id(String id){  Id=id; }
  public String toString(){ return "Nubr Surface ID="+Id;}
  public boolean isSelected(){return selected;}

  public MutableTreeNode get_TreeNode(){
    DefaultMutableTreeNode node = new DefaultMutableTreeNode(this);
    DefaultMutableTreeNode n_nodes = new DefaultMutableTreeNode("Nodes");
    DefaultMutableTreeNode n_elements = new DefaultMutableTreeNode("Elements");
    //DefaultMutableTreeNode n_control_points = new DefaultMutableTreeNode("Control Points");
    for(int i=0; i<nodesDB.size(); i++) n_nodes.add(((_Object)nodesDB.elementAt(i)).get_TreeNode());
    for(int i=0; i<elementsDB.size(); i++) n_elements.add(((_Object)elementsDB.elementAt(i)).get_TreeNode());
    //for(int i=0; i<controlPoints.length; i++) n_control_points.add(controlPoints[i].get_TreeNode());
    //node.add(n_control_points);
    node.add(n_nodes);
    node.add(n_elements);
    return node;
  }
  public void error(Object st){
    JOptionPane.showMessageDialog(null,"Error: "+st,"Error!",JOptionPane.ERROR_MESSAGE);
    if(st instanceof Exception) ((Exception)st).printStackTrace(); else System.out.println("Error: "+st);
  }
  public JPanel getEditPanel(Canvas3D j3d, PreProcessor pp){
    J3D=j3d;
    PreP = pp;
    JPanel p = new JPanel(new BorderLayout());
    JPanel p1 = new JPanel(new BorderLayout());
    JPanel p2 = new JPanel();
    JPanel p3 = new JPanel(new BorderLayout());
    JPanel p4 = new JPanel(new GridLayout(1,4));
    JPanel p5 = new JPanel(new GridLayout(4,1));
    JPanel p6 = new JPanel(new BorderLayout());
    JPanel p7 = new JPanel(new BorderLayout());
    JPanel p8 = new JPanel(new BorderLayout());
    JPanel p9 = new JPanel(new BorderLayout());
    tf_u = new JTextField(numUControlPoints+"");
    tf_v = new JTextField(numVControlPoints+"");
    tf_size = new JTextField(msh_size+"");
    String[] type = {"Contact_Triangle","Shell_C0_3","Shell_BT_4"};
    cb_type = new JComboBox(type);
    cb_type.setSelectedItem(msh_name);
    cb_mat = new JComboBox();
    cb_mat.setSelectedItem(material.name);
    tf_t = new JTextField(thickness+"");
    dat = new Vector();
    for(int i=0; i<controlPoints.length; i++){
      try{
        dat.addElement(new Float(controlPoints[i].x));
        dat.addElement(new Float(controlPoints[i].y));
        dat.addElement(new Float(controlPoints[i].z));
        dat.addElement(new Float(controlPoints[i].w));
      }catch(Exception e) {}
    }
    U=numUControlPoints;
    V=numVControlPoints;
    hdr = new String[5];
    hdr[0]="U,V"; hdr[1]="X"; hdr[2]="Y"; hdr[3]="Z"; hdr[4]="W";
    tableModel = new DefaultTableModel() {
      public int getColumnCount() { return 5; }
      public int getRowCount() {
        try {
          return U*V;
        }catch(Exception e) { return 0;}
      }
      public Object getValueAt(int row, int col){
        if(col==0){ return ((int)row/V)+","+(row-((int)row/V)*V); }
        if(dat==null || dat.size()<row*4+col) return null;
        if(dat.elementAt(row*4+col-1)==null) return null; else return (Float)dat.elementAt(row*4+col-1);
      }
      public void setValueAt(Object aValue, int row, int col) {
        dat.setSize(getRowCount()*4);
        dat.setElementAt(aValue,row*4+col-1);
      }
      public String getColumnName(int col) {return hdr[col];}
      public boolean isCellEditable(int row, int col) {if(col==0)return false; else return true;}
      public Class getColumnClass(int c) {if(c==0)return "".getClass(); else return new Float(0).getClass();}
    };
    Table = new JTable(tableModel);
    JScrollPane sp = new JScrollPane();
    sp.setPreferredSize(new Dimension(200, 200));
    JLabel lb = new JLabel("Nurb Surface");
    lb.setForeground(Color.blue);
    JButton b_upd = new JButton("Update");
    b_upd.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        try{
          if(U*V<9)return;
          float t = Float.parseFloat(tf_t.getText());
          float size = Float.parseFloat(tf_size.getText());
          _CtrlPoint[] points = new _CtrlPoint[U*V];
          int k=0;
          for(int i=0; i<dat.size()/4; i++) points[i] = new _CtrlPoint(((Float)dat.elementAt(i*4)).floatValue(),((Float)dat.elementAt(i*4+1)).floatValue(),((Float)dat.elementAt(i*4+2)).floatValue(),((Float)dat.elementAt(i*4+3)).floatValue(),Color.GRAY);
          if(cb_type.getSelectedIndex()==0)msh_type=Canvas3D.MESH_Contact_Triangle;
          else if(cb_type.getSelectedIndex()==1)msh_type=Canvas3D.MESH_Shell_C0_3;
          else if(cb_type.getSelectedIndex()==2)msh_type=Canvas3D.MESH_Shell_BT_4;
          material=(Material)PreP.MatDB.get(cb_mat.getSelectedItem()+"");
          thickness=t;
          msh_size = size;
          controlPoints = points;
          numUControlPoints = U;
          numVControlPoints = V;
          reset();
          J3D.tree_reset();
          J3D.view_reset();
        }catch(Exception e1) {error(e1);}
      }
    });
    tf_v.addKeyListener(new java.awt.event.KeyAdapter() {
      public void keyReleased(KeyEvent e) {
        try {
          V=Integer.parseInt(tf_v.getText());
        }catch(Exception e1) {return;}
        Table.revalidate();
      }
    });
    tf_u.addKeyListener(new java.awt.event.KeyAdapter() {
      public void keyReleased(KeyEvent e) {
        try {
          U=Integer.parseInt(tf_u.getText());
        }catch(Exception e1) {return;}
        Table.revalidate();
      }
    });
    p.add(sp, BorderLayout.CENTER);
    p.add(p1, BorderLayout.SOUTH);
    p1.add(p2, BorderLayout.CENTER);
    p2.add(b_upd);
    p1.add(p5, BorderLayout.NORTH);
    p5.add(p6);
    p6.add(new JLabel("Mesh size"), BorderLayout.WEST);
    p6.add(tf_size, BorderLayout.CENTER);
    p5.add(p9);
    p9.add(new JLabel("Mesh type"), BorderLayout.WEST);
    p9.add(cb_type, BorderLayout.CENTER);
    p5.add(p8);
    p8.add(new JLabel("Material"), BorderLayout.WEST);
    p8.add(cb_mat, BorderLayout.CENTER);
    p5.add(p7);
    p7.add(new JLabel("Thickness"),  BorderLayout.WEST);
    p7.add(tf_t, BorderLayout.CENTER);
    p.add(p3, BorderLayout.NORTH);
    p3.add(lb, BorderLayout.WEST);
    p3.add(p4, BorderLayout.CENTER);
    p4.add(new JLabel("U",JLabel.RIGHT));
    p4.add(tf_u);
    p4.add(new JLabel("V",JLabel.RIGHT));
    p4.add(tf_v);
    sp.getViewport().add(Table, null);
    for(Enumeration en = PreP.MatDB.keys(); en.hasMoreElements();)  cb_mat.addItem(en.nextElement());
    p.validate();
    return p;
  }
  public _Node[] get_Nodes(){
    _Node[] arr = new _Node[nodesDB.size()];
    for(int i=0; i<nodesDB.size(); i++) arr[i] = (_Node)nodesDB.elementAt(i);
    return arr;
  }
  public _Object[] get_Elements(){
    _Object[] arr = new _Object[elementsDB.size()];
    for(int i=0; i<elementsDB.size(); i++) arr[i] = (_Object)elementsDB.elementAt(i);
    return arr;
  }

}







