package jp.sourceforge.acerola3d.j3dbvh;

import jp.sourceforge.acerola3d.*;
import jp.sourceforge.acerola3d.a3.*;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class J3DBVH implements ActionListener, ChangeListener {
    JFrame frame;
    JMenuBar menuBar;
    JMenu configMenu;
    JMenuItem consoleMenuItem;
    JMenuItem resetViewMenuItem;
    JMenuItem snapshotMenuItem;
    JMenu measureMenu;
    JMenuItem measureXZMenuItem;
    JMenuItem measureXYMenuItem;
    JMenuItem measureYZMenuItem;
    JMenuItem measureNoneMenuItem;
    JMenuItem helpMenuItem;
    JMenuItem aboutMenuItem;
    JTextField urlTextField;
    JButton refButton;
    JFileChooser fileChooser;
    A3Canvas a3canvas;
    JButton resetViewButton;
    JCheckBox defaultPoseCheckBox;
    JComboBox boneComboBox;
    JLabel statusBarLabel;
    JFrame consoleFrame;
    JTextArea consoleTextArea;
    JButton clearConsoleButton;
    JCheckBox pauseCheckBox;
    JSlider slider;
    int sliderLength=100;
    double motionLength;
    A3PHtmlViewer htmlViewer;
    A3PHtmlViewer helpViewer;
    Action3D measure;

    Skeleton skeleton;
    String actionName = "0";
    MyExamController controller;
    ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);

    public J3DBVH(String args[]) {
        frame = new JFrame("J3DBVH");
        frame.getContentPane().setLayout(new BorderLayout());
        MyVBox baseBox = new MyVBox();
        frame.getContentPane().add(baseBox,BorderLayout.CENTER);

        menuBar = new JMenuBar();
        frame.setJMenuBar(menuBar);
        menuBar.add(Box.createHorizontalGlue());
        configMenu = new JMenu("Config");
        menuBar.add(configMenu);
        consoleMenuItem = new JMenuItem("Console");
        consoleMenuItem.addActionListener(this);
        configMenu.add(consoleMenuItem);
        resetViewMenuItem = new JMenuItem("ResetView");
        resetViewMenuItem.addActionListener(this);
        configMenu.add(resetViewMenuItem);
        snapshotMenuItem = new JMenuItem("Snapshot");
        snapshotMenuItem.addActionListener(this);
        configMenu.add(snapshotMenuItem);
        measureMenu = new JMenu("measure");
        configMenu.add(measureMenu);
        measureXZMenuItem = new JMenuItem("measure XZ");
        measureXZMenuItem.addActionListener(this);
        measureMenu.add(measureXZMenuItem);
        measureXYMenuItem = new JMenuItem("measure XY");
        measureXYMenuItem.addActionListener(this);
        measureMenu.add(measureXYMenuItem);
        measureYZMenuItem = new JMenuItem("measure YZ");
        measureYZMenuItem.addActionListener(this);
        measureMenu.add(measureYZMenuItem);
        measureNoneMenuItem = new JMenuItem("measure None");
        measureNoneMenuItem.addActionListener(this);
        measureMenu.add(measureNoneMenuItem);
        helpMenuItem = new JMenuItem("help");
        helpMenuItem.addActionListener(this);
        configMenu.add(helpMenuItem);
        aboutMenuItem = new JMenuItem("about");
        aboutMenuItem.addActionListener(this);
        configMenu.add(aboutMenuItem);

        consoleFrame = new JFrame("Console");
        consoleFrame.getContentPane().setLayout(new BorderLayout());
        MyVBox b = new MyVBox();
        consoleTextArea = new JTextArea(20,60);
        consoleTextArea.setEditable(false);
        JScrollPane sp = new JScrollPane(consoleTextArea);
        b.myAdd(sp,1.0);
        clearConsoleButton = new JButton("Clear");
        clearConsoleButton.addActionListener(this);
        b.myAdd(clearConsoleButton,0.0);
        consoleFrame.getContentPane().add(b,BorderLayout.CENTER);
        consoleFrame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent we) {
                consoleFrame.setVisible(false);
            }
        });
        PrintStream systemOut = System.out;
        JTextAreaOutputStream jtaos = new JTextAreaOutputStream(consoleTextArea,systemOut);
        PrintStream ps = new PrintStream(jtaos,true);
        System.setOut(ps);
        System.setErr(ps);
        consoleFrame.pack();
        
        MyHBox addressBox = new MyHBox();
        baseBox.myAdd(addressBox,0.0);
        addressBox.myAdd(new JLabel("URL:"),0.0);
        urlTextField = new JTextField();
        try {
            File f = null;
            if ((args!=null)&&(args.length==2)&&(args[0].equals("-open")))
                f  = new File(args[1]);
            else
                f = new File(args[0]);
            urlTextField.setText(f.toURI().toString());
        } catch (Exception e) {
            urlTextField.setText("x-res:///jp/sourceforge/acerola3d/j3dbvh/resources/test.bvh");
        }
        urlTextField.addActionListener(this);
urlTextField.setMaximumSize(new Dimension(3000,30));
        addressBox.myAdd(urlTextField,1.0);
        refButton = new JButton("ref");
        refButton.addActionListener(this);
        addressBox.myAdd(refButton,0.0);

        MyHBox mainBox = new MyHBox();
        baseBox.myAdd(mainBox,1.0);

        a3canvas = A3Canvas.createA3Canvas(300,300);
        controller = new MyExamController();
        a3canvas.setA3Controller(controller);
        mainBox.myAdd(a3canvas,1.0);

        MyVBox controlBox = makeControlBox();
        mainBox.myAdd(controlBox,0.0);

        Box box3 = Box.createHorizontalBox();
        baseBox.myAdd(box3,0.0);
        pauseCheckBox = new JCheckBox("puase");
        pauseCheckBox.setSelected(false);
        pauseCheckBox.addActionListener(this);
        box3.add(pauseCheckBox);
        slider = new JSlider();
        slider.setMinimum(0);
        slider.setMaximum(sliderLength);
        slider.addChangeListener(this);
        box3.add(slider);

        statusBarLabel = new JLabel("Status:");
        baseBox.myAdd(statusBarLabel,0.0);

        try {
            measure = new Action3D("x-res:///jp/sourceforge/acerola3d/j3dbvh/resources/measure.a3");
        } catch (Exception e) {
            e.printStackTrace();
        }

        frame.setTransferHandler(new BvhTransferHandler(this));
        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent we) {
                frame.dispose();
                System.exit(0);
            }
        });
        frame.setSize(700,500);
        frame.setVisible(true);

        htmlViewer = new A3PHtmlViewer(frame);
        helpViewer = new A3PHtmlViewer(null);

        Runnable r = new Runnable() {
            public void run() {
                loadBVH();
            }
        };
        executor.schedule(r,100,TimeUnit.MILLISECONDS);
    }

    MyVBox makeControlBox() {
        MyVBox controlBox = new MyVBox();

        resetViewButton = new JButton("Reset View");
        resetViewButton.addActionListener(this);
        controlBox.myAdd(resetViewButton,0.0);

        defaultPoseCheckBox = new JCheckBox("default pose");
        defaultPoseCheckBox.setSelected(false);
        defaultPoseCheckBox.addActionListener(this);
        controlBox.myAdd(defaultPoseCheckBox,0.0);

        boneComboBox = new JComboBox();
        boneComboBox.addActionListener(this);
        boneComboBox.addItem("[none]");
        controlBox.myAdd(boneComboBox,0.0);

        controlBox.myAdd(new JLabel("Left drag: rotation"),0.0);
        controlBox.myAdd(new JLabel("Center drag: translation"),0.0);
        controlBox.myAdd(new JLabel("Right drag: scale"),0.0);

        controlBox.myAdd(Box.createVerticalGlue(),1.0);

        String buildDate = null;
        try {
            URL url = getClass().getClassLoader().getResource("buildDate.txt");
            InputStreamReader isr = new InputStreamReader(url.openStream(),"UTF-8");
            BufferedReader br = new BufferedReader(isr);
            buildDate = br.readLine();
        } catch (Exception e) {
            buildDate = "???";
        }
        controlBox.myAdd(new JLabel("build date:"),0.0);
        controlBox.myAdd(new JLabel("   "+buildDate),0.0);

        return controlBox;
    }

    public void actionPerformed(ActionEvent ae) {
        if (ae.getSource() == urlTextField) {
            Runnable r = new Runnable() {
                public void run() {
                    loadBVH();
                }
            };
            executor.schedule(r,100,TimeUnit.MILLISECONDS);
        } else if (ae.getSource() == resetViewButton) {
        	controller.reset(skeleton);
        } else if (ae.getSource() == defaultPoseCheckBox) {
        	if (defaultPoseCheckBox.isSelected())
        	    skeleton.defaultPose();
        	else
        		skeleton.start();
        } else if (ae.getSource() == refButton) {
            ref();
        } else if (ae.getSource() == consoleMenuItem) {
            consoleFrame.setVisible(true);
        } else if (ae.getSource() == clearConsoleButton) {
            consoleTextArea.setText("");
        } else if (ae.getSource() == resetViewMenuItem) {
        	controller.reset(skeleton);
        } else if (ae.getSource() == snapshotMenuItem) {
            try {
                File f = new File("snapshot.png");
                a3canvas.saveImage(f);
            } catch (Exception e) {
                System.out.println("A3Viewer:snapshot");
                e.printStackTrace();
            }
        } else if (ae.getSource() == measureXZMenuItem) {
            measure.setRot(-Math.PI/2.0,0.0,0.0);
            a3canvas.add(measure);
        } else if (ae.getSource() == measureXYMenuItem) {
            measure.setRot(0.0,0.0,0.0);
            a3canvas.add(measure);
        } else if (ae.getSource() == measureYZMenuItem) {
            measure.setRot(0.0,Math.PI/2.0,0.0);
            a3canvas.add(measure);
        } else if (ae.getSource() == measureNoneMenuItem) {
            a3canvas.del(measure);
        } else if (ae.getSource() == helpMenuItem){
            openHelp();
        } else if (ae.getSource() == aboutMenuItem){
            openAbout();
        } else if (ae.getSource() == pauseCheckBox) {
            if (skeleton == null)
                return;
            if (pauseCheckBox.isSelected()) {
            	skeleton.pause();
                double t = motionLength*((double)slider.getValue()/(double)sliderLength);
                skeleton.setPauseTime(t);
            } else {
                skeleton.start();
                double t = motionLength*((double)slider.getValue()/(double)sliderLength);
                skeleton.setPauseTime(t);
            }
        } else if (ae.getSource() == boneComboBox) {
        	selectBone();
        } else {
            System.out.append("gaha???");
        }
    }
    void selectBone() {
    	String bn = (String)boneComboBox.getSelectedItem();
if (bn==null) {
System.out.println("gaha1");
return;
}
    	if (bn.equals("[none]")) {
    		resetBone();
    	} else {
    		resetBone();
    	    skeleton.setBoneSelected(bn,true);
    	}
    	
    }
    void resetBone() {
    	if (skeleton==null)
    		return;
    	String bn = skeleton.getRootBone();
    	resetBone2(bn);
    }
    void resetBone2(String bn) {
    	skeleton.setBoneSelected(bn,false);
    	for (String nbn:skeleton.getChildBones(bn)) {
    		resetBone2(nbn);
    	}
    }

	@Override
	public void stateChanged(ChangeEvent ce) {
        if (ce.getSource()==slider) {
            if (skeleton==null)
                return;
            double t = motionLength*((double)slider.getValue()/(double)sliderLength);
            skeleton.setPauseTime(t);
        }
	}

	void loadBVH() {
        statusBarLabel.setText("Status: Loading ... ");
        statusBarLabel.repaint();
        if (skeleton!=null)
            a3canvas.del(skeleton);
        String urlString = urlTextField.getText();
        try {
            A23.initA23();
            skeleton = new Skeleton(urlString);
            controller.reset(skeleton);
        } catch(Exception e) {
            System.out.println("J3DVrml.loadVRML. error!");
            e.printStackTrace();
        }
        a3canvas.add(skeleton);

        boneComboBox.removeAllItems();
        boneComboBox.addItem("[none]");
        for (String bn:getBoneNames()) {
        	boneComboBox.addItem(bn);
        }
        if (pauseCheckBox.isSelected()) {
        	skeleton.pause();
        } else {
        	skeleton.start();
        }
        slider.setValue(0);
        motionLength = skeleton.getMotionLength();

        skeleton.setLocImmediately(0.0,0.0,0.0);
        skeleton.setQuatImmediately(0.0,0.0,0.0,1.0);
        skeleton.setScaleImmediately(1.0);

        Runnable r = new Runnable() {
            public void run() {
                controller.reset(skeleton);
            }
        };
        executor.schedule(r,100,TimeUnit.MILLISECONDS);

        statusBarLabel.setText("Status: Loading ... done.");
    }
	String[] getBoneNames() {
		if (skeleton==null)
			return new String[0];
		ArrayList<String> names = new ArrayList<String>();
		getBoneNames2(skeleton.getRootBone(),names);
		return names.toArray(new String[0]);
	}
	void getBoneNames2(String bn,ArrayList<String> names) {
		names.add(bn);
		for (String nbn:skeleton.getChildBones(bn)) {
			getBoneNames2(nbn,names);
		}
	}
    void loadBVH(File f) throws Exception {
        urlTextField.setText(f.toURI().toURL().toString());
        Runnable r = new Runnable() {
            public void run() {
                loadBVH();
            }
        };
        executor.schedule(r,100,TimeUnit.MILLISECONDS);
    }

    void ref() {
        if (fileChooser == null) {
            fileChooser = new JFileChooser(".");
        }
        int returnVal = fileChooser.showOpenDialog(frame);
        if(returnVal != JFileChooser.APPROVE_OPTION)
            return;

        File f = fileChooser.getSelectedFile();
        URL url = null;
        try{url=new URL(f.toURI().toString());}catch(Exception e){;}
        if (url == null)
            return;
        urlTextField.setText(url.toExternalForm());

        Runnable r = new Runnable() {
            public void run() {
                loadBVH();
            }
        };
        executor.schedule(r,100,TimeUnit.MILLISECONDS);
    }
    void openHelp() {
        URL url = null;
        try {
            url = new URL("x-res:///jp/sourceforge/acerola3d/j3dbvh/resources/ja/help.html");
        } catch(Exception e) {
            e.printStackTrace();
        }
        helpViewer.open(url);
    }
    void openAbout() {
        URL url = null;
        try {
            url = new URL("x-res:///jp/sourceforge/acerola3d/j3dbvh/resources/ja/about.html");
        } catch(Exception e) {
            e.printStackTrace();
        }
        htmlViewer.open(url);
    }
    public static void main(String args[]) {
        // セキュリティマネージャが、
        // LinuxのJava Web Startの時だけ
        // Exceptionを出すので、とりあえずnullに
        // しておく。
        System.setSecurityManager(null);
        final String argsCopy[] = args;
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new J3DBVH(argsCopy);
            }
        });
    }
}
