package jp.sourceforge.acerola3d.a3viewer;

import jp.sourceforge.acerola3d.*;
import jp.sourceforge.acerola3d.a3.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.URL;

import javax.swing.*;
import javax.swing.event.*;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class A3Panel extends JPanel implements ActionListener, ChangeListener {
    private static final long serialVersionUID = 1L;
    static String defaultURL = "x-res:///jp/sourceforge/acerola3d/a3viewer/resources/acerola3d.a3";
    JMenuBar menuBar;
    JMenu configMenu;
    JMenuItem consoleMenuItem;
    JMenuItem resetViewMenuItem;
    JMenuItem snapshotMenuItem;
    JMenuItem backgroundMenuItem;
    JMenuItem headLightMenuItem;
    JMenu balloonMenu;
    JMenuItem balloonRightMenuItem;
    JMenuItem balloonLeftMenuItem;
    JMenuItem balloonTopMenuItem;
    JMenuItem balloonBottomMenuItem;
    JMenuItem balloonDelMenuItem;
    JMenu labelMenu;
    JMenuItem labelMenuItem;
    JMenuItem labelDelMenuItem;
    JMenu measureMenu;
    JMenuItem measureXZMenuItem;
    JMenuItem measureXYMenuItem;
    JMenuItem measureYZMenuItem;
    JMenuItem measureNoneMenuItem;
    JMenuItem htmlMenuItem;
    JMenuItem metadataMenuItem;
    JMenuItem helpMenuItem;
    JMenuItem aboutMenuItem;
    JTextField urlTextField;
    JButton refButton;
    JFileChooser fileChooser;
    A3Canvas a3canvas;
    JComboBox actionNameCB;
    JCheckBox immediatelyCheckBox;
    JButton resetViewButton;
    JTextArea commentTextArea;
    JSlider volumeSlider;
    JLabel statusBarLabel;
    JFrame consoleFrame;
    JTextArea consoleTextArea;
    JButton clearConsoleButton;
    JCheckBox pauseCheckBox;
    JSlider slider;
    int sliderLength=100;
    double motionLength;
    boolean headLight = true;
    A3PHtmlViewer htmlViewer;
    A3PHtmlViewer helpViewer;
    Action3D measure;

    Action3D a3;
    String actionName = "0";
    MyExamController controller;
    ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);

    public A3Panel(Frame frame,URL url) {
        setLayout(new BorderLayout());

        MyVerticalBox baseBox = new MyVerticalBox();
        this.add(baseBox,BorderLayout.CENTER);
        menuBar = new JMenuBar();
        menuBar.setMinimumSize(new Dimension(100,20));//??????????
        baseBox.myAdd(menuBar,0.0);
        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);
        backgroundMenuItem = new JMenuItem("Background");
        backgroundMenuItem.addActionListener(this);
        configMenu.add(backgroundMenuItem);
        headLightMenuItem = new JMenuItem("HeadLight");
        headLightMenuItem.addActionListener(this);
        configMenu.add(headLightMenuItem);
        balloonMenu = new JMenu("Balloon");
        configMenu.add(balloonMenu);
        balloonRightMenuItem = new JMenuItem("Balloon on (right)");
        balloonRightMenuItem.addActionListener(this);
        balloonMenu.add(balloonRightMenuItem);
        balloonLeftMenuItem = new JMenuItem("Balloon on (left)");
        balloonLeftMenuItem.addActionListener(this);
        balloonMenu.add(balloonLeftMenuItem);
        balloonTopMenuItem = new JMenuItem("Balloon on (top)");
        balloonTopMenuItem.addActionListener(this);
        balloonMenu.add(balloonTopMenuItem);
        balloonBottomMenuItem = new JMenuItem("Balloon on (bottom)");
        balloonBottomMenuItem.addActionListener(this);
        balloonMenu.add(balloonBottomMenuItem);
        balloonDelMenuItem = new JMenuItem("Balloon off");
        balloonDelMenuItem.addActionListener(this);
        balloonMenu.add(balloonDelMenuItem);
        labelMenu = new JMenu("Label");
        configMenu.add(labelMenu);
        labelMenuItem = new JMenuItem("Label on");
        labelMenuItem.addActionListener(this);
        labelMenu.add(labelMenuItem);
        labelDelMenuItem = new JMenuItem("Label off");
        labelDelMenuItem.addActionListener(this);
        labelMenu.add(labelDelMenuItem);
        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);
        htmlMenuItem = new JMenuItem("Html");
        htmlMenuItem.addActionListener(this);
        configMenu.add(htmlMenuItem);
        metadataMenuItem = new JMenuItem("Metadata");
        metadataMenuItem.addActionListener(this);
        configMenu.add(metadataMenuItem);
        helpMenuItem = new JMenuItem("Help");
        helpMenuItem.addActionListener(this);
        configMenu.add(helpMenuItem);
        aboutMenuItem = new JMenuItem("About");
        aboutMenuItem.addActionListener(this);
        configMenu.add(aboutMenuItem);

        consoleFrame = new JFrame("Console");
        MyVerticalBox b = new MyVerticalBox();
        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().setLayout(new BorderLayout());
        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();

        MyHorizontalBox addressBox = new MyHorizontalBox();
        baseBox.myAdd(addressBox,0.0);
        addressBox.myAdd(new JLabel("URL:"),0.0);
        urlTextField = new JTextField();
        urlTextField.addActionListener(this);
        if (url==null)
            urlTextField.setText(defaultURL);
        else
            urlTextField.setText(url.toString());
        addressBox.myAdd(urlTextField,1.0);

        refButton = new JButton("ref");
        refButton.addActionListener(this);
        addressBox.myAdd(refButton,0.0);

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

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

        MyHorizontalBox pauseBox = new MyHorizontalBox();
        baseBox.myAdd(pauseBox,0.0);
        pauseCheckBox = new JCheckBox("pause");
        pauseCheckBox.setSelected(false);
        pauseCheckBox.addActionListener(this);
        pauseBox.myAdd(pauseCheckBox,0.0);
        slider = new JSlider();
        slider.setMinimum(0);
        slider.setMaximum(sliderLength);
        slider.addChangeListener(this);
        pauseBox.myAdd(slider,1.0);

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

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

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

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

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

        MyHorizontalBox actionBox = new MyHorizontalBox();
        controlBox.myAdd(actionBox,0.0);
        actionBox.myAdd(new JLabel("ActionName:"),0.0);
        actionNameCB = new JComboBox();
        actionNameCB.addActionListener(this);
        actionBox.myAdd(actionNameCB,1.0);

        immediatelyCheckBox = new JCheckBox("immediatelyChange");
        immediatelyCheckBox.setSelected(true);
        controlBox.myAdd(immediatelyCheckBox,0.0);
        
        resetViewButton = new JButton("Reset View");
        resetViewButton.addActionListener(this);
        controlBox.myAdd(resetViewButton,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(new JLabel("***Comment***"),0.0);
        commentTextArea = new JTextArea(10,30);
        commentTextArea.setEditable(false);
        JScrollPane sp = new JScrollPane(commentTextArea);
sp.setPreferredSize(new Dimension(50,150));
        controlBox.myAdd(sp,1.0);

        MyHorizontalBox volBox = new MyHorizontalBox();
        controlBox.myAdd(volBox,0.0);
        volBox.myAdd(new JLabel("SoundVol"),0.0);
        volumeSlider = new JSlider();
        volumeSlider.setMinimum(0);
        volumeSlider.setMaximum(sliderLength);
        volumeSlider.addChangeListener(this);
        volBox.myAdd(volumeSlider,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: "+buildDate),0.0);

        return controlBox;
    }

    public void actionPerformed(ActionEvent ae) {
        if (ae.getSource() == actionNameCB) {
            actionName = (String)actionNameCB.getSelectedItem();
if (actionName == null) {
System.out.println("gaha1");
return;
}
            System.out.println("actionName:"+actionName);
            repaint();
        } else if (ae.getSource() == urlTextField) {
            Runnable r = new Runnable() {
                public void run() {
                    loadAction3D();
                }
            };
            executor.schedule(r,100,TimeUnit.MILLISECONDS);
        } else if (ae.getSource() == resetViewButton) {
            controller.reset(a3);
        } 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(a3);
        } else if (ae.getSource() == snapshotMenuItem) {
            try {
                File f = new File("snapshot.png");
                a3canvas.saveImage(f);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else if (ae.getSource() == backgroundMenuItem){
            setBackground();
        } else if (ae.getSource() == headLightMenuItem){
            headLight = headLight?false:true;
            a3canvas.setHeadLightEnable(headLight);
        } else if (ae.getSource() == balloonRightMenuItem){
            if (a3==null)
                return;
            a3.setBalloonDir(A3Object.BalloonDir.RIGHT);
            a3.setBalloon("ふきだし");
        } else if (ae.getSource() == balloonLeftMenuItem){
            if (a3==null)
                return;
            a3.setBalloonDir(A3Object.BalloonDir.LEFT);
            a3.setBalloon("ふきだし");
        } else if (ae.getSource() == balloonTopMenuItem){
            if (a3==null)
                return;
            a3.setBalloonDir(A3Object.BalloonDir.TOP);
            a3.setBalloon("ふきだし");
        } else if (ae.getSource() == balloonBottomMenuItem){
            if (a3==null)
                return;
            a3.setBalloonDir(A3Object.BalloonDir.BOTTOM);
            a3.setBalloon("ふきだし");
        } else if (ae.getSource() == balloonDelMenuItem){
            if (a3==null)
                return;
            a3.setBalloon(null);
        } else if (ae.getSource() == labelMenuItem){
            if (a3==null)
                return;
            a3.setLabel("ラベル");
        } else if (ae.getSource() == labelDelMenuItem){
            if (a3==null)
                return;
            a3.setLabel(null);
        } 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() == htmlMenuItem){
            openHtml();
        } else if (ae.getSource() == metadataMenuItem){
            openMetadata();
        } else if (ae.getSource() == helpMenuItem){
            openHelp();
        } else if (ae.getSource() == aboutMenuItem){
            openAbout();
        } else if (ae.getSource() == pauseCheckBox) {
            if (a3 == null)
                return;
            if (pauseCheckBox.isSelected()) {
                a3.changeImmediately(actionName);
                a3.setMode(Motion.Mode.PAUSE);
                double t = motionLength*((double)slider.getValue()/(double)sliderLength);
                a3.setPauseTime(t);
            } else {
                a3.changeImmediately(actionName);
                a3.setMode(Motion.Mode.PLAY);
                double t = motionLength*((double)slider.getValue()/(double)sliderLength);
                a3.setPauseTime(t);
            }
        } else {
            System.out.append("gaha???");
        }
    }

    void openHtml() {
        if (a3==null)
            return;
        URL url = a3.getHTML();
        if (url==null) {
            JOptionPane.showMessageDialog(this,
                    "No html file.", 
                    "ERROR",JOptionPane.ERROR_MESSAGE);
            return;
        }
        htmlViewer.open(url);
    }
    void openMetadata() {
        if (a3==null)
            return;
        String s = "";
        s=s+"<html><body><h1>Not implemented yet!</h1><p><a href=\"http://www.google.co.jp/\">test</a></p>";
        s=s+"<pre>"+a3.getComment()+"</pre>";
        s=s+"</body></html>";
        htmlViewer.open(s);
    }
    void openHelp() {
        URL url = null;
        try {
            url = new URL("x-res:///jp/sourceforge/acerola3d/a3viewer/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/a3viewer/resources/ja/about.html");
        } catch(Exception e) {
            e.printStackTrace();
        }
        htmlViewer.open(url);
    }

    public void stateChanged(ChangeEvent ce) {
        if (ce.getSource()==volumeSlider) {
            double g = 2.0*((double)volumeSlider.getValue()/(double)sliderLength);
            a3canvas.setSoundGain(g);
        } else if (ce.getSource()==slider) {
            if (a3==null)
                return;
            double t = motionLength*((double)slider.getValue()/(double)sliderLength);
            a3.setPauseTime(t);
        }
    }
    
    void loadAction3D() {
        Action3D.clearCash();
        statusBarLabel.setText("Status: Loading ... ");
        statusBarLabel.repaint();
        if (a3!=null)
            a3canvas.del(a3);
        String urlString = urlTextField.getText();
        try {
            A23.initA23();
            a3 = Action3D.load(urlString);
        } catch(Exception e) {
            e.printStackTrace();
        }

        actionNameCB.removeAllItems();
        String sss[] = a3.getActionNames();
        for (int i=0;i<sss.length;i++)
            actionNameCB.addItem(sss[i]);
        a3canvas.add(a3);

        commentTextArea.setText(a3.getComment());
        if (pauseCheckBox.isSelected())
            a3.setMode(Motion.Mode.PAUSE);
        else
            a3.setMode(Motion.Mode.PLAY);
        motionLength = a3.getMotionLength(actionName);
        slider.setValue(0);

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

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

        statusBarLabel.setText("Status: Loading ... done.");
    }
    void loadAction3D(File f) throws Exception {
        urlTextField.setText(f.toURI().toURL().toString());
        Runnable r = new Runnable() {
            public void run() {
                loadAction3D();
            }
        };
        executor.schedule(r,100,TimeUnit.MILLISECONDS);
    }

    public void repaint() {
        if (a3 == null)
            return;
        if (immediatelyCheckBox.isSelected())
            a3.changeImmediately(actionName);
        else
            a3.change(actionName);
        motionLength = a3.getMotionLength(actionName);
        slider.setValue(0);
    }

    void ref() {
        if (fileChooser == null) {
            fileChooser = new JFileChooser(".");
            fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
        }
        int returnVal = fileChooser.showOpenDialog(this);
        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() {
                loadAction3D();
            }
        };
        executor.schedule(r,100,TimeUnit.MILLISECONDS);
    }
    void setBackground() {
        if (fileChooser == null) {
            fileChooser = new JFileChooser(".");
        }
        int returnVal = fileChooser.showOpenDialog(this);
        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;
        Action3D a = null;
        try{
            a = Action3D.load(url);
        } catch(Exception e) {
            e.printStackTrace();
        }
        if (a!=null)
            a3canvas.setBackground(a);
        Box.createVerticalBox();
    }
    class MyHorizontalBox extends JPanel {
        private static final long serialVersionUID = 1L;
        GridBagConstraints c;
        int index;
        MyHorizontalBox() {
            this.setLayout(new GridBagLayout());
            c = new GridBagConstraints();
            c.fill = GridBagConstraints.BOTH;
            c.weighty = 1.0;
        }
        void myAdd(Component component,double weight) {
            c.gridx = index;
            c.weightx = weight;
            this.add(component,c);
            index++;
        }
    }
    class MyVerticalBox extends JPanel {
        private static final long serialVersionUID = 1L;
        GridBagConstraints c;
        int index;
        MyVerticalBox() {
            this.setLayout(new GridBagLayout());
            c = new GridBagConstraints();
            c.fill = GridBagConstraints.BOTH;
            c.weightx = 1.0;
        }
        public void myAdd(Component component,double weight) {
            c.gridy = index;
            c.weighty = weight;
            this.add(component,c);
            index++;
        }
    }
}
