package march;

import java.util.*;

public class March{
    private int NI, NJ, NK;
    private float start_x, start_y, start_z;
    private float delta_x, delta_y, delta_z;

    private float threshold;
    private float[][][]  slice;
    private boolean[][][] outsideslice;
    private boolean[] inout = new boolean[8];

    private int[][] xedge; 
    private int[][] yedge; 
    private int[]   zedge; 
    private int[]   topyedge; 
    private int old10pt, old6pt;

    private int[][] facet_data = Cases.facet_data;
    private int[][] edge_data  = Cases.edge_data;
    // Vector of Vertex
    private Vector vertex_table;
    private Vector surface_list;

    public March(float[][][] slice, float threshold, 
                 float start_x, float start_y, float start_z,
                 float delta_x, float delta_y, float delta_z){
       
        this.slice = slice;
        
        this.threshold = threshold;

        NK = slice.length;
        NI = slice[0].length;
        NJ = slice[0][0].length;

        this.start_x = start_x;
        this.start_y = start_y;
        this.start_z = start_z;
        this.delta_x = delta_x;
        this.delta_y = delta_y;
        this.delta_z = delta_z;

        init_outsideslice(threshold);

        xedge = new int[NI][NJ];
        yedge = new int[NI][NJ];
        zedge = new int[NJ];
        topyedge = new int[NJ];
        vertex_table = new Vector();
        surface_list = new Vector();
    }

    public TriangleStrip march(){        
        for(int k = 0; k < NK-1; k++){
            System.err.println("k = " + k);
            if(k == 0){
                init_xedgeandyedge();
            }
            
            boolean[][] lowslice = outsideslice[k];
            boolean[][] highslice = outsideslice[k+1];
            boolean[]   inout = new boolean[8];
            
            for(int i = 0; i < NI-1; i++){
                if(i == 0){
                    init_zedgeandtopyedge(k);
                }
                for(int j = 0; j < NJ-1; j++){
                    if(j == 0){
                        init_old10ptandold6pt(k,i);
                    }
               
                    set_inout(inout, lowslice, highslice, i, j);
                   
                    int cube_index = Cases.cubeIndex(inout);
                    if(cube_index != 0 && cube_index != 255){
                        load_facet(cube_index, i, j, k);
                    }
                }
            }
        }
        return (new TriangleStrip(vertex_table, surface_list));
    }
    
    private void set_inout(boolean[] inout, 
                           boolean[][] lowslice, boolean[][] highslice,
                           int i, int j){
        inout[0] = lowslice[i][j];
        inout[1] = lowslice[i+1][j];
        inout[2] = lowslice[i+1][j+1];
        inout[3] = lowslice[i][j+1];
        inout[4] = highslice[i][j];
        inout[5] = highslice[i+1][j];
        inout[6] = highslice[i+1][j+1];
        inout[7] = highslice[i][j+1];
    }
            
    private void init_xedgeandyedge(){
        boolean[][] lowslice = outsideslice[0];
        boolean[][] highslice = outsideslice[1];
        
        for(int i = 0; i < NI-1; i++){
            for(int j = 0; j < NJ-1; j++){
                set_inout(inout, lowslice, highslice, i, j);
                int cube_index = Cases.cubeIndex(inout);
                int[] edges = edge_data[cube_index];
                for(int l = 0; l < 12; l++){
                    int edge = edges[l];
                    if(edge == -1){
                        break;
                    }
                    else if(edge == 0){
                        float s = slice[0][i][j];
                        float e = slice[0][i+1][j];
                        float x = start_x + delta_x*i;
                        float y = start_y + delta_y*j;
                        float z = start_z;
                        float xx = x + delta_x*(threshold-s)/(e-s);
                        Vertex pt = new Vertex(xx,y,z);
                        vertex_table.add(pt);
                        xedge[i][j] = vertex_table.size()-1;
                    }
                    else if(edge == 3){
                        float s = slice[0][i][j];
                        float e = slice[0][i][j+1];
                        float x = start_x + delta_x*i;
                        float y = start_y + delta_y*j;
                        float z = start_z;
                        float yy = y + delta_y*(threshold-s)/(e-s);
                        Vertex pt = new Vertex(x,yy,z);
                        vertex_table.add(pt);
                        yedge[i][j] = vertex_table.size()-1;
                    }
                }
            }
        }
    }
                    
    private void init_zedgeandtopyedge(int k){
        boolean[][] lowslice = outsideslice[k];
        boolean[][] highslice = outsideslice[k+1];
        for(int j = 0; j < NJ-1; j++){
            set_inout(inout, lowslice, highslice, 0, j);
            int cube_index = Cases.cubeIndex(inout);
            int[] edges = edge_data[cube_index];
            for(int l = 0; l < 12; l++){
                int edge = edges[l];
                if(edge == -1){
                    break;
                }
                else if(edge == 8){
                    float s = slice[k][0][j];
                    float e = slice[k+1][0][j];
                    float x = start_x;
                    float y = start_y + delta_y*j;
                    float z = start_z + delta_z*k;
                    float zz = z + delta_z*(threshold-s)/(e-s);
                    Vertex pt = new Vertex(x,y,zz);
                    vertex_table.add(pt);
                    zedge[j] = vertex_table.size()-1;
                }
                else if(edge == 7){
                    float s = slice[k+1][0][j];
                    float e = slice[k+1][0][j+1];
                    float x = start_x;
                    float y = start_y + delta_y*j;
                    float z = start_z + delta_z*k;
                    float yy = y + delta_y*(threshold-s)/(e-s);
                    Vertex pt = new Vertex(x,yy,z);
                    vertex_table.add(pt);
                    topyedge[j] = vertex_table.size()-1;
                }
            }
        }
    }
    
    private void init_old10ptandold6pt(int k, int i){
        boolean[][] lowslice = outsideslice[k];
        boolean[][] highslice = outsideslice[k+1];
        set_inout(inout, lowslice, highslice, i, 0);
        int cube_index = Cases.cubeIndex(inout);
        int   edge;
        int[] edges = edge_data[cube_index];
        for(int l = 0; l < 12; l++){
            edge = edges[l];
            if(edge == -1){
                break;
            }
            else if(edge == 4){
                float s = slice[k+1][i][0];
                float e = slice[k+1][i+1][0];
                float x = start_x + delta_x*i;
                float y = start_y;
                float z = start_z + delta_z*(k+1);
                float xx = x + delta_x*(threshold-s)/(e-s);
                Vertex pt = new Vertex(xx,y,z);
                vertex_table.add(pt);
                old6pt = vertex_table.size()-1;
            }
            else if(edge == 9){
                float s = slice[k][i+1][0];
                float e = slice[k+1][i+1][0];
                float x = start_x + delta_x*(i+1);
                float y = start_y;
                float z = start_z + delta_z*k;
                float zz = z + delta_z*(threshold-s)/(e-s);
                Vertex pt = new Vertex(x,y,zz);
                vertex_table.add(pt);
                old10pt = vertex_table.size()-1;
            }
        }
    }

    private void init_outsideslice(float T){
        outsideslice = new boolean[NK][NI][NJ];

        for(int k = 0; k < NK; k++){
            for(int i = 0; i < NI; i++){
                for(int j = 0; j < NJ; j++){
                    if(slice[k][i][j] < T){
                        outsideslice[k][i][j] = true;
                    }
                }
            }
        }
    }
    
    private void load_facet(int cube_index, 
                            int imarch, int jmarch, int kmarch){
        int[] edge_table = new int[12];
        int edge;
        int[] vertex_list = new int[3];
        int vert_index;
        int vert_offset;
        int ioffset;
        int joffset;
        
        int[][] facet_data = Cases.facet_data;
        int[][] edge_data = Cases.edge_data;
        
        int i = 0;
        
        Vertex pt;
        
        while((edge = edge_data[cube_index][i]) != -1){
            vert_offset = 0;
            switch(edge){
              case 0:
                vert_offset = xedge[imarch][jmarch];
                break;
              case 1:
                vert_offset = yedge[imarch+1][jmarch];
                break;
              case 2:
                vert_offset = xedge[imarch][jmarch+1];
                break;
              case 3:
                vert_offset = yedge[imarch][jmarch];
                break;
              case 4:
                vert_offset = old6pt;
                xedge[imarch][jmarch] = vert_offset;
                break;
              case 5:
                pt = contour_intersect(5, imarch, jmarch, kmarch);
                vertex_table.add(pt);
                vert_index = vertex_table.size()-1;
                topyedge[jmarch] = vert_index;
                vert_offset = vert_index;
                if(imarch == (NI-2)){
                    yedge[NI-1][jmarch] = vert_index;
                }
                break;
              case 6:
                pt = contour_intersect(6, imarch, jmarch, kmarch);
                vertex_table.add(pt);
                vert_index = vertex_table.size()-1;
                old6pt = vert_index;
                if(jmarch == (NJ-2)){
                    xedge[imarch][NJ-1] = vert_index;
                }
                break;
              case 7:
                vert_offset = topyedge[jmarch];
                yedge[imarch][jmarch] = vert_offset;
                break;
              case 8:
                vert_offset = zedge[jmarch];
                break;
              case 9:
                vert_offset = old10pt;
                zedge[jmarch] = vert_offset;
                break;
              case 10:
                pt = contour_intersect(10, imarch, jmarch, kmarch);
                vertex_table.add(pt);
                vert_index = vertex_table.size()-1;
                old10pt = vert_index;
                vert_offset = vert_index;
                if(imarch == (NJ-2)){
                    zedge[NJ-1] = vert_offset;
                }
                break;
              case 11:
                vert_offset = zedge[jmarch+1];
                break;
            }
            edge_table[edge] = vert_offset;
            i++;
        }
        i = 0;
        int facet;
        while((i != 12)&&(facet_data[cube_index][i] != -1)){
            
            /**
            String s = "facet_data[" + i + "] = new int[]{";
            for(int j = 0; j < 12; j++){
                s += (facet_data[cube_index][j]);
                if(j != 11){
                    s += ", ";
                }
            }
            s += "}";
            System.err.println(s);
            */

            int v1,v2,v3;
            v1 = edge_table[facet_data[cube_index][i]];
            v2 = edge_table[facet_data[cube_index][i+1]];
            
            v3 = edge_table[facet_data[cube_index][i+2]];
            Vertex vv1 = (Vertex)vertex_table.elementAt(v1);
            Vertex vv2 = (Vertex)vertex_table.elementAt(v2);
            Vertex vv3 = (Vertex)vertex_table.elementAt(v3);
            surface_list.add(new Triangle(vv1,vv2,vv3));
            i += 3;
        }
    }

    private Vertex contour_intersect(int v, 
                                     int kmarch, int imarch, int jmarch){
        
        if(v == 5){
            float s = slice[kmarch+1][imarch+1][jmarch];
            float e = slice[kmarch+1][imarch+1][jmarch+1];
            float x = start_x + delta_x*(imarch+1);
            float y = start_y + delta_y*jmarch;
            float z = start_z + delta_z*(kmarch+1);
            float yy = y + delta_y*(threshold-s)/(e-s);
            return new Vertex(x,yy,z);
        }
        else if(v == 6){
            float s = slice[kmarch+1][imarch][jmarch+1];
            float e = slice[kmarch+1][imarch+1][jmarch+1];
            float x = start_x + delta_x*(imarch);
            float y = start_y + delta_y*(jmarch+1);
            float z = start_z + delta_z*(kmarch+1);
            float xx = x + delta_x*(threshold-s)/(e-s);
            return new Vertex(xx,y,z);
        }
        else if(v == 10){
            float s = slice[kmarch][imarch+1][jmarch+1];
            float e = slice[kmarch+1][imarch+1][jmarch+1];
            float x = start_x + delta_x*(imarch+1);
            float y = start_y + delta_y*(jmarch+1);
            float z = start_z + delta_z*kmarch;
            float zz = z + delta_z*(threshold-s)/(e-s);
            return new Vertex(x,y,zz);
        }
        return new Vertex(100.0f, 100.0f, 100.0f);
    } 

}



