package march;

import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;

public class MarchApp implements MouseMotionListener, MouseListener{
    public static float TPI = (float)(Math.PI * 2.0);

    public Color back_color = Color.black;
    public int Ia = (new Color(80,85,85)).getRGB();
    public int Il = (new Color(255,255,255)).getRGB();
    public Vec  L = new Vec(0.0f, 1.0f, -0.3f);


    public static int WIRE = 0;
    public static int ZBUF = 1;

    public static float RD = 100;
    public static float THE_D = (float)(Math.PI/40.0);
    public static float PHI_D = (float)(Math.PI/40.0);
    
    public static Vec   init_fp  = new Vec(170, 300, 180);
    public static float init_R   = 8000.0f;
    public static float init_the = (float)(Math.PI/2.0 * 3.0);
    public static float init_phi = 0.0f;

    public static int DIV_U = 500;
    public static int DIV_V = 50;

    public Color object_color = new Color(120,150,120);

    public MarchingCube march;
    public TriangleStrip ts;

    public Frame  f;
    public BCanvas canv;
    public Button  zbuf_button;
    public Button zi_button;
    public Button  zo_button;
    
    public Vec fp     = init_fp;
    public float R   = init_R;
    public float the = init_the;
    public float phi = init_phi;

    public int md_x;
    public int md_y;

    public int disp_state = WIRE;

    public MarchApp(float[][][] slice, float T,
                    float sx, float sy, float sz,
                    float dx, float dy, float dz){
        canv   = new BCanvas(400, 400);
        f      = new Frame("Marching Cube");
        f.setLayout(null);
        f.setSize(440,480);

        f.move(100, 100);

        canv.setBackground(Color.black);
        canv.move(10, 10);
        f.add(canv);

        f.setVisible(true);
        canv.setVisible(true);
        
        zbuf_button = new Button("ZBuffer");
        zi_button   = new Button("Zoom in");
        zo_button   = new Button("Zoom out");
        
        zbuf_button.move(20, 430);
        zbuf_button.setSize(70, 20);
        zbuf_button.setVisible(true);
        f.add(zbuf_button);

        zi_button.move(100, 430);
        zi_button.setSize(55, 20);
        zi_button.setVisible(true);
        f.add(zi_button);

        zo_button.move(200, 430);
        zo_button.setSize(55, 20);
        zo_button.setVisible(true);
        f.add(zo_button);

        canv.makeOffScreen();
        
        // initialize march
        march = new NMarch(slice, T, sx, sy, sz, dx, dy, dz);
        // generate surface
        ts    = march.march();
        ts.setAllColor(object_color);

        canv.addMouseMotionListener(this);
        canv.addMouseListener(this);
        zbuf_button.addMouseListener(this);
        zi_button.addMouseListener(this);
        zo_button.addMouseListener(this);

        f.addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent ev){
                System.exit(0);
            }
        });
        draw();
    }

    public void mouseEntered(MouseEvent me){}
    public void mouseExited(MouseEvent me){}
    public void mouseReleased(MouseEvent me){}


    public void mousePressed(MouseEvent me){
        Component c = me.getComponent();
        if(c == canv){
            md_x = me.getX();
            md_y = me.getY();
        }
    }

    public void mouseClicked(MouseEvent me){
        Component c = me.getComponent();
        if(c == zbuf_button){
            if(disp_state == WIRE){
                disp_state = ZBUF;
                zbuf_button.setLabel("Wire");
            }
            else{
                disp_state = WIRE;
                zbuf_button.setLabel("ZBuffer");
            }
            draw();
        }
        else if(c == zi_button){
            R -= RD;
            draw();
        }
        else if(c == zo_button){
            R += RD;
            draw();
        }
    }

    public void mouseMoved(MouseEvent me){}

    public void mouseDragged(MouseEvent me){

        if(me.getComponent() != canv){
            return;
        }
        int x = me.getX();
        int y = me.getY();

        int dx = (x-md_x);
        int dy = (y-md_y);
        int dxx = dx*dx;
        int dyy = dy*dy;

        if(dxx > dyy){
            if(dx < 0){
                the -= THE_D;
            }
            else{
                the += THE_D;
            }
        }
        else{
            if(dy < 0){
                phi -= PHI_D;
            }
            else{
                phi += PHI_D;
            }
        }

        if(the < 0){
            the += TPI;
        }
        if(the > TPI){
            the -= TPI;
        }
        
        if(phi < 0){
            phi += TPI;
        }
        if(phi > TPI){
            phi -= TPI;
        }

        draw();
        md_x = x;
        md_y = y;
    }

    public void draw(){
        canv.clearOffScreen();

        Graphics offsg = canv.getOffScreenGraphics();
        offsg.translate((int)(canv.size().width/2.0), 
                        (int)(canv.size().height/2.0));

        int width  = canv.size().width;
        int height = canv.size().height;
        
        if(disp_state == WIRE){
            offsg.setColor(Color.white);
            ts.drawWireFrame(offsg, fp, R, the, phi);
        }
        else{
            MemoryImageSource mis =
              ts.drawZBuffer(offsg, -10000000.0f, back_color.getRGB(),
                             - width/2, - height/2,
                             width/2,  height/2,
                             Ia, Il, L, fp, R, the, phi);
            Toolkit tk = Toolkit.getDefaultToolkit();
            Image image = tk.createImage(mis);
            offsg.drawImage(image,- width/2, - height/2, canv);
        }

        String info1 = "fp = (" + fp.x + "," + fp.y + "," + fp.z +")";
        String info2 = "R = " + (float)R + " ";
        info2 += "the = " + (float)the + " ";
        info2 += "phi = " + (float)phi;
        
        Graphics g = canv.getOffScreenGraphics();
        g.setFont(new Font("Serif", Font.PLAIN, 12));
        g.drawString(info1, 8, 30);
        g.drawString(info2, 8, 48);
        canv.paint(canv.getGraphics());
        
    }

    public static void main(String[] argv){
        float[][][] slice = null;
        if(argv.length ==0 || argv[0].equals("sphere")){
            slice = makeSphereSlice(64, 64, 64);
            MarchApp ma = new MarchApp(slice, 0.5f, 
                                       10.0f, 10.0f, 10.0f, 
                                       6.0f, 6.0f, 6.0f);
        }
        else if(argv[0].equals("metaball")){
            slice = makeMetaBallSlice(64, 64, 64);
            MarchApp ma = new MarchApp(slice, 0.6f, 
                                       10.0f, 10.0f, 10.0f, 
                                       6.0f, 6.0f, 6.0f);
        }
        else if(argv[0].equals("metaball2")){
            slice = makeMetaBallSlice(128, 128, 128);
            MarchApp ma = new MarchApp(slice, 0.6f, 
                                       10.0f, 10.0f, 10.0f, 
                                       3.0f, 3.0f, 3.0f);
        }

    } 

    public static float[][][] makeSphereSlice(int xw, int yw, int zw){
        float[][][] ret = new float[zw][xw][yw];
        int cx = xw/2;
        int cy = yw/2;
        int cz = zw/2;
        double r = (xw/2 * 0.8);
        for(int k = 0; k < zw; k++){
            for(int i = 0; i < xw; i++){
                for(int j = 0; j < yw; j++){
                    double d = Math.sqrt((i-cx)*(i-cx)+(j-cy)*(j-cy)+(k-cz)*(k-cz));
                    if(d < r){
                        ret[k][i][j] = 0.9f;
                    }
                }
            }
        }
        return ret;
    }

    public static float[][][] makeCubeSlice(int xw, int yw, int zw){
        float[][][] ret = new float[zw][xw][yw];
        int cx = xw/2;
        int cy = yw/2;
        int cz = zw/2;
        double r = (xw/2 * 0.8);
        for(int k = 0; k < cz; k++){
            for(int i = 0; i < cx; i++){
                for(int j = 0; j < cy; j++){
                    ret[k][i][j] = 0.9f;
                }
            }
        }
        return ret;
    }

    public static float[][][] makeMetaBallSlice(int xw, int yw, int zw){
        float[][][] ret = new float[zw][xw][yw];
        Vec c1 = new Vec(xw/3.0f, yw/1.8f, zw/2.0f);
        Vec c2 = new Vec(xw/1.5f, yw/2.2f, zw/2.0f);
        float r  = (xw/2.0f * 0.6f);
        float d1, d2;

        for(int k = 0; k < zw; k++){
            for(int i = 0; i < xw; i++){
                for(int j = 0; j < yw; j++){
                    d1 = (float)Math.sqrt((i-c1.x)*(i-c1.x)+(j-c1.y)*(j-c1.y)+(k-c1.z)*(k-c1.z));
                    d2 = (float)Math.sqrt((i-c2.x)*(i-c2.x)+(j-c2.y)*(j-c2.y)+(k-c2.z)*(k-c2.z));
                    if(d1 < r){
                        ret[k][i][j] += (r*r - d1*d1)*(r*r - d1*d1);
                    }
                    if(d2 < r){
                        ret[k][i][j] += (r*r - d2*d2)*(r*r - d2*d2);
                    }
                }
            }
        }
        return ret;
    }

}


