package p5;
import anar.*;



import java.util.*;

import processing.core.PApplet;

/**
 * @author goo
 */
public class SpaceColony00bLastFaceB extends PApplet {

  /*
   * Example for Anar library by Guillaume LaBelle + Julien Nembrini
   * http://anar.ch
   */


  Sliders parameters;

  Pt      a, b, c, d, e, f;

  Obj     obj;

  int     nIteration = 0;

  public void setup(){
    size(800,400,OPENGL);
    Anar.init(this);

    Scene.autoSeek = false;

    obj = new Obj();

    a = Anar.Pt( -30,30);
    b = Anar.Pt( -25,0);
    c = Anar.Pt( -30, -30);
    d = Anar.Pt(30, -30);
    e = Anar.Pt(25,0);
    f = Anar.Pt(30,30);

    initForm();
    initSpacePoints();

    Pts.globalRender = new RenderPtsAll();

  }


  void reset(){
    obj.faces.clear();
    obj.pts.ptList.clear();

    initForm();
    initSpacePoints();

    nIteration = 0;
  }

  void initForm(){


    Pts facePts = new Pts();
    facePts.add(a);
    facePts.add(b);
    facePts.add(c);
    facePts.add(d);
    facePts.add(e);
    facePts.add(f);
    Face f = new Face(facePts);

    obj.add(f);

    parameters = new Sliders(a);
    // println(parameters);

  }

  void initSpacePoints(){

    float maxX = 80;
    float maxY = 80;
    float maxZ = 400;

    for (int i = 0; i<200; i++){
      // obj.add(Anar.Pt( (float)Math.random()*2*maxX - maxX,
      // (float)Math.random()*2*maxY - maxY,
      // (float)Math.random()*maxZ));
      obj.add(Anar.Pt((float)Math.sin(i/(float)Math.PI)*maxX,(float)Math.cos(i/(float)Math.PI)*maxY,i/2));
    }

  }

  void spaceColonyStep(){

    float actionDist = 60;
    float deleteDist = 50;

    float coeff = 5;
    float zz = 5;


    // get last face
    Face f = obj.faces.get(obj.faces.size()-1);

    // make a translated copy
    Face newFace = new Face();

    Iterator it = f.iterator();
    while (it.hasNext()){
      Pt pp = (Pt)it.next();
      Pt ppp = Anar.Pt(pp.x(),pp.y(),pp.z());
      ppp.translate(0,0,zz);
      newFace.add(ppp);

    }
    obj.add(newFace);

    // now perform spaceColony attraction on all the vertices in the object
    // for each space point select nearest and attract it.
    Pts newSpacePoints = new Pts();

    ArrayList listT = new ArrayList();

    it = obj.pts.iterator();
    while (it.hasNext()){
      Pt spaceP = (Pt)it.next();
      boolean delete = false;

      int index = 0;

      ListIterator itF = obj.faces.listIterator();
      while (itF.hasNext()){
        f = (Face)itF.next();
        Iterator itP = f.iterator();
        while (itP.hasNext()){
          Pt faceP = (Pt)itP.next();
          // first round populate transform list
          if(listT.size()<index+1){
            listT.add(new AllTransform());

          }
          if(spaceP.length(faceP)<actionDist){

            ((AllTransform)listT.get(index)).add(new Translate(Anar.Pt( (spaceP.x()-faceP.x())/(float)Math.pow(spaceP.length(faceP),2),
                (spaceP.y()-faceP.y())/(float)Math.pow(spaceP.length(faceP),2),0)));
            if(faceP.length(spaceP)<deleteDist)
              delete = true;
          }

          index++;

        } // end while pt
      }// end while face

      if( !delete)
        newSpacePoints.add(spaceP);

    } // end while space point

    // now update transform on all points
    int index = 0;
    Iterator itF = obj.faces.iterator();
    while (itF.hasNext()){
      f = (Face)itF.next();
      Iterator itP = f.iterator();
      while (itP.hasNext()){
        Pt faceP = (Pt)itP.next();

        Pt v = ((AllTransform)listT.get(index)).toUnit().getVertex();
        faceP.translate(coeff*v.x(),coeff*v.y(),coeff*v.z());

        index++;

      } // end while pt
    }// end while face


    println("Space points "+newSpacePoints.numOfPts());

    Obj newObj = new Obj();

    itF = obj.faces.iterator();
    while (itF.hasNext()){
      newObj.add((Face)itF.next());
    }

    it = newSpacePoints.iterator();
    while (it.hasNext()){
      newObj.add((Pt)it.next());
    }

    obj = newObj;

  }


  void spaceColonyLastStep(){

    float actionDist = 60;
    float deleteDist = 50;

    float coeff = 1;
    float zz = 2;


    // get last face
    Face f = obj.faces.get(obj.faces.size()-1);

    // make a translated copy
    Face newFace = new Face();

    Iterator it = f.iterator();
    while (it.hasNext()){
      Pt pp = (Pt)it.next();
      Pt ppp = Anar.Pt(pp.x(),pp.y(),pp.z());
      ppp.translate(0,0,zz);
      newFace.add(ppp);

    }
    obj.add(newFace);

    // now perform spaceColony attraction on the vertices of the last facein the
    // object
    // for each space point select nearest and attract it.
    Pts newSpacePoints = new Pts();

    ArrayList listT = new ArrayList();

    it = obj.pts.iterator();
    while (it.hasNext()){
      Pt spaceP = (Pt)it.next();
      boolean delete = false;

      int index = 0;

      f = obj.faces.get(obj.faces.size()-1);
      Iterator itP = f.iterator();
      while (itP.hasNext()){
        Pt faceP = (Pt)itP.next();
        // first round populate transform list
        if(listT.size()<index+1){
          listT.add(new AllTransform());

        }
        if(spaceP.length(faceP)<actionDist){

          ((AllTransform)listT.get(index)).add(new Translate(Anar.Pt( (spaceP.x()-faceP.x())/(float)Math.pow(spaceP.length(faceP),2), (spaceP.y()-faceP.y())
              /(float)Math.pow(spaceP.length(faceP),2),0)));
          if(faceP.length(spaceP)<deleteDist)
            delete = true;
        }

        index++;

      } // end while pt

      if( !delete)
        newSpacePoints.add(spaceP);

    } // end while space point

    // now update transform on all points
    int index = 0;
    f = obj.faces.get(obj.faces.size()-1);
    Iterator itP = f.iterator();
    while (itP.hasNext()){
      Pt faceP = (Pt)itP.next();

      Pt v = ((AllTransform)listT.get(index)).toUnit().getVertex();
      faceP.translate(coeff*v.x(),coeff*v.y(),coeff*v.z());

      index++;

    } // end while pt


    println("Space points "+newSpacePoints.numOfPts());

    Obj newObj = new Obj();

    Iterator itF = obj.faces.iterator();
    while (itF.hasNext()){
      newObj.add((Face)itF.next());
    }

    it = newSpacePoints.iterator();
    while (it.hasNext()){
      newObj.add((Pt)it.next());
    }

    obj = newObj;

  }


  class AllTransform {

    ArrayList trans;

    AllTransform(){
      trans = new ArrayList();
    }

    void add(Translate t){
      trans.add(t);
    }

    Translate toUnit(){
      Pt v = Anar.Pt(0,0,0);

      Iterator it = trans.iterator();
      while (it.hasNext()){
        Pt p = ((Translate)it.next()).getVertex();
        v.set(v.x()+p.x(),v.y()+p.y(),v.z()+p.z());
      }

      // unit length
      if(v.length()>0)
        v.set(v.x()/v.length(),v.y()/v.length(),v.z()/v.length());

      return new Translate(v);
    }

  }

  void weavingIteration(){

    nIteration++;
    println(nIteration);

    float z = 2f;

    ArrayList newFaces = new ArrayList();

    for (int k = 0; k<obj.numOfFaces(); k++){
      Face f = obj.face(k);

      // float param;
      // if (positive) param = z / (nIteration * nIteration) ;
      // else param = - z / (nIteration * nIteration) ;

      float param = z;

      Pts pp = new Pts();
      Pts subdiv = new Pts();

      // layout of pp in quad
      // 3 6 7 2
      //			 
      //			 
      // 0 4 5 1

      // get the vertices of the triangle
      pp.add(f.pt(0));
      pp.add(f.pt(1));
      pp.add(f.pt(2));
      pp.add(f.pt(3));

      // get the middle points
      Pts AB = new PtsMid(pp.pt(0),pp.pt(1),3);
      Pts BC = new PtsMid(pp.pt(1),pp.pt(2),3);
      Pts CD = new PtsMid(pp.pt(2),pp.pt(3),3);
      Pts DA = new PtsMid(pp.pt(3),pp.pt(0),3);

      pp.add(Anar.Pt(AB.pt(1)));
      pp.add(Anar.Pt(AB.pt(2)));

      pp.add(Anar.Pt(CD.pt(2)));
      pp.add(Anar.Pt(CD.pt(1)));

      // interior points
      Pts AB1CD2 = new PtsMid(pp.pt(4),pp.pt(6),3);
      Pts AB2CD1 = new PtsMid(pp.pt(5),pp.pt(7),3);

      // layout of the quad subdivision in subdiv
      // up down
      // 0 1 2 3
      // 4 5 6 7 16 17 18 19
      // 8 9 10 11 20 21 22 23
      // 12 13 14 15

      // 1st horizontal
      // 3 6 7 2
      subdiv.add(pp.pt(3));
      subdiv.add(pp.pt(6));
      subdiv.add(pp.pt(7));
      subdiv.add(pp.pt(2));

      // 2nd horizontal
      subdiv.add(Anar.Pt(DA.pt(1)));

      subdiv.add(Anar.Pt(AB1CD2.pt(2)));
      subdiv.add(Anar.Pt(AB2CD1.pt(2)));

      subdiv.add(Anar.Pt(BC.pt(2)));

      // 3rd horizontal
      subdiv.add(Anar.Pt(DA.pt(2)));

      subdiv.add(Anar.Pt(AB1CD2.pt(1)));
      subdiv.add(Anar.Pt(AB2CD1.pt(1)));

      subdiv.add(Anar.Pt(BC.pt(1)));


      // 4th horizontal
      // 0 4 5 1
      subdiv.add(pp.pt(0));
      subdiv.add(pp.pt(4));
      subdiv.add(pp.pt(5));
      subdiv.add(pp.pt(1));


      // duplicate points
      // 2nd horizontal
      subdiv.add(Anar.Pt(DA.pt(1)));

      subdiv.add(Anar.Pt(AB1CD2.pt(2)));
      subdiv.add(Anar.Pt(AB2CD1.pt(2)));

      subdiv.add(Anar.Pt(BC.pt(2)));

      // 3rd horizontal
      subdiv.add(Anar.Pt(DA.pt(2)));

      subdiv.add(Anar.Pt(AB1CD2.pt(1)));
      subdiv.add(Anar.Pt(AB2CD1.pt(1)));

      subdiv.add(Anar.Pt(BC.pt(1)));


      // up
      for (int i = 4; i<12; i++){
        Pt p = subdiv.pt(i);
        p.translate(0f,0f,param);
      }

      // down
      for (int i = 16; i<24; i++){
        Pt p = subdiv.pt(i);
        p.translate(0f,0f, -param);
      }

      // add first batch (regular ones)
      int[] angle = {0, 2, 5, 8, 10, 16, 18};
      // int[] angle = {0, 2, 5, 8, 10};
      for (int i = 0; i<angle.length; i++){
        Pts fps = new Pts();
        fps.add(subdiv.pt(angle[i]+4));
        fps.add(subdiv.pt(angle[i]+5));
        fps.add(subdiv.pt(angle[i]+1));
        fps.add(subdiv.pt(angle[i]));
        newFaces.add(new Face(fps));
      }

      // add second batch (irregular ones)
      Pts fps = new Pts();
      fps.add(subdiv.pt(17));
      fps.add(subdiv.pt(18));
      fps.add(subdiv.pt(2));
      fps.add(subdiv.pt(1));
      newFaces.add(new Face(fps));

      fps = new Pts();
      fps.add(subdiv.pt(13));
      fps.add(subdiv.pt(14));
      fps.add(subdiv.pt(22));
      fps.add(subdiv.pt(21));
      newFaces.add(new Face(fps));


    }

    obj = new Obj();

    for (int i = 0; i<newFaces.size(); i++)
      obj.add((Face)newFaces.get(i));

    Anar.camTarget(obj);

  }


  public void draw(){
    background(153);

    // ParametricValue v = parameters.pt(0);
    // v.set(v.pt()+ random(10)-5);

    obj.draw();
    if(drawPoints)
      obj.pts.draw();
  }

  public static void main(String[] args){
    PApplet.main(new String[]{SpaceColony00bLastFaceB.class.getName()});
  }

  boolean positive   = false;
  boolean drawPoints = false;

  public void keyPressed(){

    switch(key){
      case 'q':
        spaceColonyStep();
      break;
      case 'w':
        spaceColonyLastStep();
      break;
      case 'e':
        reset();
      break;
      case 'r':
        drawPoints = !drawPoints;
      break;
      case 't':
        weavingIteration();
      break;


      case 'a':
        TextIO.write( ((Object)this).getClass().getName()+".lsp",obj.toAutocad());
      break;


    }


  }

}

