import processing.core.*; 
import processing.xml.*; 

import controlP5.*; 
import javax.media.opengl.*; 
import javax.media.opengl.*; 
import javax.media.opengl.glu.*; 
import com.sun.opengl.util.*; 
import processing.opengl.*; 
import processing.opengl.*; 
import java.nio.*; 

import java.applet.*; 
import java.awt.*; 
import java.awt.image.*; 
import java.awt.event.*; 
import java.io.*; 
import java.net.*; 
import java.text.*; 
import java.util.*; 
import java.util.zip.*; 
import java.util.regex.*; 

public class CloudView_15_fir2 extends PApplet {

//
//








GL gl;

float zoom = 50.0f;
float zoff = 3000;
int opacity = 1;
boolean positive = true;
boolean negative = true;
boolean paint = true;


float ax=0, ay=0;        //rotation
float xoff=0, yoff=0;    //movement
float mx_, my_;          //old value
float mxstart, mystart;  //start mouse

public void setup(){
  size(800,800, OPENGL);
  frameRate(90);
  ortho(-width, width, -height, height, 0, 10000);
  gl = ((PGraphicsOpenGL)g).gl;
  //loadgeom();
  //loadgeom_ATOMS();
  loadgeom_BOTH();
  load();
  camera(0, 0, -zoff, // eyeX, eyeY, eyeZ
  0.0f, 0.0f, 0.0f, // centerX, centerY, centerZ
  0.0f, 1.0f, 0); // upX, upY, upZ
  background(0);
  //gencloud(points);
  setupgui(); 

}

public void draw(){
  gui.draw();
  
  //background(0);
  /*
  fill(255,50);
  box(100000);
  */
  
  gl.glEnable (gl.GL_BLEND); 
  gl.glDisable(gl.GL_AUTO_NORMAL);
  gl.glDisable(gl.GL_COLOR_MATERIAL);
  gl.glDisable(gl.GL_LIGHTING);
  gl.glDisable(gl.GL_CULL_FACE);
  
  //TESTS
  gl.glDisable(gl.GL_DEPTH_TEST);
  gl.glDisable(gl.GL_ALPHA_TEST);
  
  //BLEND FUNC
  //gl.glBlendEquation(gl.GL_ADD);
  gl.glBlendEquation(gl.GL_MAX);
  
  //BLEND EQUATION
  //gl.glBlendFunc (gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA);
  //gl.glBlendFunc (gl.GL_SRC_ALPHA, gl.GL_ONE);
  gl.glBlendFunc (gl.GL_SRC_ALPHA, gl.GL_ZERO);

  gl.glTranslatef(xoff, yoff, 0);
  
  gl.glTranslatef(0, 0, -zoff);
  gl.glRotatef(ax, 0, 1, 0);
  gl.glRotatef(ay, -1, 0, 0);
  gl.glTranslatef(0, 0, +zoff);
   
  if(!mousePressed && paint) gencloud(points);
  
  gl.glBlendFunc (gl.GL_ONE, gl.GL_ZERO);
  strokeWeight(3);
  stroke(128,255);
  for (int i=0;i<atoms;i++){
  //point{atoms[i].x,atoms[i].y,atoms[i].z}
  //println(geom[i].element);
  
  point(
  (geom[i].x-xsize/2)*zoom,
  (geom[i].y-ysize/2)*zoom,
  (geom[i].z-zsize/2)*zoom
  );
   }
  strokeWeight(1);
  
  if (clear){background(0);clear = false;}
}

public void mousePressed(){
  background(0);
  if(mouseButton == LEFT){
  mxstart=mouseX;
  mystart=mouseY;
  mx_=ax;
  my_=ay;
  }
  if(mouseButton == RIGHT){
  mxstart=mouseX;
  mystart=mouseY;
  mx_=xoff;
  my_=yoff;
  }
};

public void mouseDragged(){
  background(0);
  if(mouseButton == LEFT){
  ax=mx_+mouseX-mxstart;
  ay=my_+mouseY-mystart;
  }
  if(mouseButton == RIGHT){
  xoff=mx_+mouseX-mxstart;
  yoff=my_+mouseY-mystart;
  }
};

public void keyPressed(){

if(key=='i'){save(year()+"-"+month()+"-"+day()+"-"+minute()+"-"+second());};
  
if(key=='+'){zoom*=1.61803399f;gencloud(points);background(0);}
if(key=='-'){zoom/=1.61803399f;gencloud(points);background(0);}

if(key=='a'){positive=!positive;}
if(key=='s'){negative=!negative;}

if(key==' '){paint=!paint;}
};
ControlP5 gui;
ControlWindow guiW;
Range rangeX;
Range rangeY;
Range rangeZ;
Textfield fmaxField;

boolean clear = false;

public void setupgui(){
  gui = new ControlP5(this);
  gui.setAutoDraw(false);
  guiW = gui.addControlWindow("controlP5window",0,800,800,100);
  
  rangeX = gui.addRange(" X RANGE ",0,xs, 0,xs, 10,20,400,20);
  rangeX.setWindow(guiW);
  rangeY = gui.addRange(" Y RANGE ",0,ys, 0,ys, 10,40,400,20);
  rangeY.setWindow(guiW);
  rangeZ = gui.addRange(" Z RANGE ",0,zs, 0,zs, 10,60,400,20);
  rangeZ.setWindow(guiW);
  
  fmaxField = gui.addTextfield("f_max",500,20,100,20);
  fmaxField.setText(""+fmax);
  fmaxField.setAutoClear(false);
  fmaxField.setWindow(guiW);
  
};

public void controlEvent(ControlEvent event) {
  if(event.controller()==rangeX) {
    xDown = PApplet.parseInt(event.controller().arrayValue()[0]);
    xUp = PApplet.parseInt(event.controller().arrayValue()[1]);
  }
  if(event.controller()==rangeY) {
    yDown = PApplet.parseInt(event.controller().arrayValue()[0]);
    yUp = PApplet.parseInt(event.controller().arrayValue()[1]);
  }
  if(event.controller()==rangeZ) {
    zDown = PApplet.parseInt(event.controller().arrayValue()[0]);
    zUp = PApplet.parseInt(event.controller().arrayValue()[1]);
  }
    if(event.controller()==fmaxField) {
    fmax = PApplet.parseFloat(event.controller().stringValue());
  }
  clear = true;
}

float[][][] xyz;
float[][] abc;
int xs,ys,zs,nums;
int xUp,yUp,zUp;
int xDown,yDown,zDown;

int words = 5;


class atom{
int element;
float x,y,z;
}
atom[] geom;
int atoms=0;

///////////////////////////////
/// ----- LOAD 3D FUNC ----- //
///////////////////////////////

public void load(){
  abc=new float[3][3];
  String[] lines;
  String[] pieces;

  lines = loadStrings("grid.xsf");

  String[] m=null;
  int i = 0;
  while(m==null){
    m = match(lines[i], "DATAGRID_3D_");  //FIND START OF GRID
    i++;
  }
  pieces=splitTokens(lines[i]," \t");    //LOAD GRID SIZE
  xs=PApplet.parseInt(pieces[0]);
  ys=PApplet.parseInt(pieces[1]);
  zs=PApplet.parseInt(pieces[2]);
  nums=xs*ys*zs;
  //println(xs+" "+ys+" "+zs+" "+nums);
  i+=2;
  xyz = new float[xs][ys][zs];            //ALLOCATE GRID
  for (int ii=0;ii<3;ii++){              //LOAD ABC MATRIX
    pieces=splitTokens(lines[i+ii]," \t");
    for (int iv=0;iv<3;iv++){
      abc[ii][iv]=PApplet.parseFloat(pieces[iv]);
    }
  }

//print out ABC matrix
for (int ii=0;ii<3;ii++){
  String s=""; 
  for (int iv=0;iv<3;iv++){
  s+="\t"+abc[ii][iv];
}
println(s);
}
i+=3;
//fireball?
print(lines[i].length()+": ");
if(lines[i].length()!=0){words=1;i--;
println("fireball");
}else{println("vasp");}

i++;
  int lmax = (nums/words);              //LOAD XYZ GRID
  int num = 0;
  for(int ii=0;ii<lmax;ii++){
    pieces=splitTokens(lines[i+ii]," \t");
    for(int iw=0;iw<words;iw++){
      int x=num%xs;
      int y=(num/xs)%ys;
      int z=num/(xs*ys);
   //   println(x+" "+y+" "+z+" | "+num+" : "+pieces[iw]);
      xyz[x][y][z]=PApplet.parseFloat(pieces[iw]);
      num++;
    }
  }
  println("Grid of size "+xs+" x "+ys+" x "+zs+" = "+nums+" datapoints loaded");
  
  //size of scene in rho_1 metric
  xsize = sqrt(sq(abc[0][0])+sq(abc[1][0])+sq(abc[2][0]));
  ysize = sqrt(sq(abc[0][1])+sq(abc[1][1])+sq(abc[2][1]));
  zsize = sqrt(sq(abc[0][2])+sq(abc[1][2])+sq(abc[2][2]));
  rsize = sqrt(sq(xsize)+sq(ysize)+sq(zsize));
  println("Cell size "+xsize+" x "+ysize+" x "+zsize+" | "+rsize+" ");
  
xUp=xs;yUp=ys;zUp=zs;
xDown=0;yDown=0;zDown=0;
  
}



///////////////////////////////
/// ---- LOAD GEOMETRY ----- //
///////////////////////////////


//////////// PRIMCORD ///////////////

public void loadgeom(){
String[] lines;
String[] pieces;
lines = loadStrings("grid.xsf");
String[] m=null;
int i = 0;
while(m==null){
 m = match(lines[i], "PRIMCOORD");  //FIND START OF GRID
   i++;}
pieces=splitTokens(lines[i]," \t");
atoms = PApplet.parseInt(pieces[0]);
geom = new atom[atoms];
i++;
for(int ii=0;ii<atoms;ii++){
geom[ii] = new atom();
pieces=splitTokens(lines[i+ii]," \t");
geom[ii].element = PApplet.parseInt(pieces[0]);
geom[ii].x = PApplet.parseFloat(pieces[1]);
geom[ii].y = PApplet.parseFloat(pieces[2]);
geom[ii].z = PApplet.parseFloat(pieces[3]);
}
}


///////////////// ATOMS ///////////////////

public void loadgeom_ATOMS(){
String[] lines;
String[] pieces;
lines = loadStrings("grid.xsf");
String[] m=null;
int i = 0;
while(m==null){
 m = match(lines[i], "ATOMS");  //FIND START OF ATOMS
   i++;}
int iATOMS = i; // save start of block
atoms = 0; // find number of atoms
while(lines[i].length()>6){  
i++; atoms++;
}
println(atoms);
geom = new atom[atoms];

i=iATOMS; // back to start
//load atoms
for(int ii=0;ii<atoms;ii++){
 println(ii);
geom[ii] = new atom();
pieces=splitTokens(lines[i+ii]," \t");
geom[ii].element = PApplet.parseInt(pieces[0]);
geom[ii].x = PApplet.parseFloat(pieces[1]);
geom[ii].y = PApplet.parseFloat(pieces[2]);
geom[ii].z = PApplet.parseFloat(pieces[3]);
}
}



/////////////// PRIMCORD OR ATOMS ///////////////

public void loadgeom_BOTH(){
String[] lines;
String[] pieces;
lines = loadStrings("grid.xsf");
String[] m1=null;
String[] m2=null;
int i = 0;
while((m1==null)&&(m2==null)){
 m1 = match(lines[i], "ATOMS");  //FIND START OF ATOMS
 m2 = match(lines[i], "PRIMCOORD");  //FIND START OF PRIMCORD
   i++;}

if(m1 == null){
// "PRIMCORD" case
pieces=splitTokens(lines[i]," \t");
atoms = PApplet.parseInt(pieces[0]);
geom = new atom[atoms];
i++;
println("PRIMCORD: "+atoms);
}else{
// "ATOMS" case
int iATOMS = i; // save start of block
atoms = 0; // find number of atoms
while(lines[i].length()>6){  
i++; atoms++;
}
println("ATOMS: "+atoms);
geom = new atom[atoms];
i=iATOMS; // back to start
}

//load atoms
for(int ii=0;ii<atoms;ii++){
 println(ii);
geom[ii] = new atom();
pieces=splitTokens(lines[i+ii]," \t");
geom[ii].element = PApplet.parseInt(pieces[0]);
geom[ii].x = PApplet.parseFloat(pieces[1]);
geom[ii].y = PApplet.parseFloat(pieces[2]);
geom[ii].z = PApplet.parseFloat(pieces[3]);
}
}




int points = 5000; //points in each frame
//float fcut = 0.5;
float fmax = 0.1f;
float xsize,ysize,zsize,rsize;


//////////////////////////////
/// --- PAINT MC POINTS --- //
//////////////////////////////

public void gencloud(int points){

  int genpts=0;
  for (int i =0;i<points;i++){
    float fx=random(xDown+0.01f,xUp-1.01f);
    float fy=random(yDown+0.01f,yUp-1.01f);
    float fz=random(zDown+0.01f,zUp-1.01f);
    float f = inter(fx,fy,fz);
    //float f = xyz[int(fx)][int(fy)][int(fz)];
    
    fx/=(xs-1);
    fy/=(ys-1);
    fz/=(zs-1);

    float x = fx*abc[0][0]+fy*abc[1][0]+fz*abc[2][0];
    float y = fx*abc[0][1]+fy*abc[1][1]+fz*abc[2][1];
    float z = fx*abc[0][2]+fy*abc[1][2]+fz*abc[2][2];

    x-=xsize*0.5f;
    y-=ysize*0.5f;
    z-=zsize*0.5f;
   
   // OPAQUE COLOR(VAL)
   //if(abs(f)<fcut){
   if((f>0)&&(positive)){
      stroke(0,0,255*f/fmax,255);
      point(x*zoom,y*zoom,z*zoom);
      genpts++;
    }else if(negative){
      stroke(-255*f/fmax,0,0,-255);
      point(x*zoom,y*zoom,z*zoom);
      genpts++;
    }
   //}
   
    /*
    //PROBABILITY(VAL) ALPHA=OPACITY
    if(f>random(0,fmax)){
      stroke(0,0,255,opacity);
      point(x*zoom,y*zoom,z*zoom);
      genpts++;
    }else if(-f>random(0,fmax)){
      stroke(255,0,0,opacity);
      point(x*zoom,y*zoom,z*zoom);
      genpts++;
    }

    // ALPHA(VAL)
    if(f>0){
      stroke(0,0,255,255*f/fmax);
      point(x*zoom,y*zoom,z*zoom);
      genpts++;
    }else{
      stroke(255,0,0,-255*f/fmax);
      point(x*zoom,y*zoom,z*zoom);
      genpts++;
    }  
   */
  }
  //println("MotneCarlo generated "+genpts+" points of "+points+" trials ");
}


/////////////////////////////////////////
/// --- INTERPOLATED FUCTION POINT --- //
////////////////////////////////////////


public float inter(float fx,float fy,float fz){
  int x=0;
  int y=0;
  int z=0;
  while(x<=fx){ x++; }
  while(y<=fy){ y++; }
  while(z<=fz){ z++; }
  fx-=(x-1);
  fy-=(y-1);
  fz-=(z-1);
  
  float val =
    xyz[x-1][y-1][z-1]*(1-fx)*(1-fy)*(1-fz)+
    xyz[x-1][y-1][z  ]*(1-fx)*(1-fy)*   fz +
    xyz[x-1][y  ][z-1]*(1-fx)*   fy *(1-fz)+
    xyz[x-1][y  ][z  ]*(1-fx)*   fy *   fz +
    xyz[x  ][y-1][z-1]*   fx *(1-fy)*(1-fz)+
    xyz[x  ][y-1][z  ]*   fx *(1-fy)*   fz +
    xyz[x  ][y  ][z-1]*   fx *   fy *(1-fz)+
    xyz[x  ][y  ][z  ]*   fx *   fy *   fz ;
   
    /*
    float val =
    xyz[x-1][y-1][z-1]*    fx*    fy*   fz+
    xyz[x-1][y-1][z  ]*    fx*    fy*(1-fz)+
    xyz[x-1][y  ][z-1]*    fx*(1-fy)*   fz +
    xyz[x-1][y  ][z  ]*    fx*(1-fy)*(1-fz)+
    xyz[x  ][y-1][z-1]*(1-fx)*    fy*   fz +
    xyz[x  ][y-1][z  ]*(1-fx)*    fy*(1-fz)+
    xyz[x  ][y  ][z-1]*(1-fx)*(1-fy)*   fz +
    xyz[x  ][y  ][z  ]*(1-fx)*(1-fy)*(1-fz);
   */
  return(val);
};

  static public void main(String args[]) {
    PApplet.main(new String[] { "--bgcolor=#ECE9D8", "CloudView_15_fir2" });
  }
}
