//[Update:[Fri Dec 11 IST 2020]]
/*History:
**Thu Oct 24 2019
   Started.
**Wed Oct 30 2019
 First working version.
**Wed Jul 22 2020
  Adopting new camera policy
**Fri Dec 11 2020
   1) Now camera is controlled by mouse drag and wheel.
   2) GUI layout changed.
*/
import java.util.*;
import java.awt.*;
import java.io.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

public class Screen  extends JPanel
    implements ActionListener, ListSelectionListener, MouseListener, MouseMotionListener, MouseWheelListener {

    DefaultListModel<Object3D> lst;
    JList<Object3D> jlst;
    Camera cam;

    public Screen(String fname) {
        this();
        load(fname);
    }
    public Screen() {
        lst = new DefaultListModel<Object3D>();
        jlst = new JList<Object3D>(lst);
        jlst.addListSelectionListener(this);
        cam = new Camera(this);
        //lst.addElement(cam);

        JFrame father = new JFrame("New 3D");
        father.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        father.add(this);
        JPanel south = new JPanel();
        father.add(BorderLayout.SOUTH,south);
        JPanel east = new JPanel();
        father.add(BorderLayout.EAST,east);
        
        JButton dmp = new JButton("Export");
        dmp.addActionListener(this);
        south.add(dmp);
        JButton addCone = new JButton("Cone");
        addCone.addActionListener(this);
        south.add(addCone);
        JButton addCyl = new JButton("Cylinder");
        addCyl.addActionListener(this);
        south.add(addCyl);
        JButton addSkl = new JButton("Skeleton");
        addSkl.addActionListener(this);
        south.add(addSkl);
        JButton addRotX = new JButton("RotX");
        addRotX.addActionListener(this);
        south.add(addRotX);
        JButton addPos = new JButton("Pos");
        addPos.addActionListener(this);
        south.add(addPos);

        east.setLayout(new GridLayout(2,1));
        JScrollPane jsrcl = new JScrollPane(jlst);
        jsrcl.setMaximumSize(new Dimension(100,getHeight()));
        east.add(jsrcl);
        layout = new JPanel();
        layout.setLayout(new GridLayout(2,1));
        east.add(layout);
        addMouseListener(this);
        addMouseMotionListener(this);
        addMouseWheelListener(this);


        father.pack();
        father.setSize(1000,800);
        father.setVisible(true);


    }
    JPanel layout;
    Object3D currObj;
    int anchorX, anchorY;
    double startLat, startLon, startR;
    
    public void mouseMoved(MouseEvent me) {}
    public void mouseDragged(MouseEvent me) {
        int delX = me.getX()-anchorX;
        int delY = me.getY()-anchorY;
        cam.setLatLon(startLat+delY/1000.0, startLon+delX/1000.0);
    }
    public void mouseEntered(MouseEvent me) {}
    public void mouseExited(MouseEvent me) {}
    public void mousePressed(MouseEvent me) {
        anchorX = me.getX();
        anchorY = me.getY();
        startLat = cam.getLat();
        startLon = cam.getLon();
        startR = cam.getR();
        //System.err.format("Snapping at lat=%.2f, lon=%.2f, r=%f.\n",startLat,startLon,startR);
    }
    public void mouseReleased(MouseEvent me) {}
    public void mouseClicked(MouseEvent me) {}
    public void mouseWheelMoved(MouseWheelEvent mwe) {
        int val = mwe.getWheelRotation();
        //System.err.println("val = "+val);
        cam.changeR(val);
    }
    public void valueChanged(ListSelectionEvent le) {
        int i = jlst.getSelectedIndex();
        currObj = lst.getElementAt(i);
        showDetails(currObj);
    }
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        GraphicsPad gp = new GraphicsPad(g2,getWidth()/2, getHeight()/2);

        int n = lst.getSize();
        
        for(int i=0;i<n;i++) lst.getElementAt(i).dump(cam,gp);
    }
    PrintWriter pw;
    public void actionPerformed(ActionEvent ae) {
        String comm = ae.getActionCommand();
        if(comm.equals("Export")) {
            try {
                Pad pad = new SVG(new PrintWriter(new File("3draw.svg")));
                pad.startPic();
                int n = lst.getSize();
        
                for(int i=0;i<n;i++) lst.getElementAt(i).dump(cam,pad);
                pad.endPic();
            }
            catch(Exception ex) {
                System.err.println("Can't write in file 3draw.svg!");
                ex.printStackTrace(System.err);
            }
        }
        else if(comm.equals("Cone")) {
            createObj(new Cone(this));
        }
        else if(comm.equals("Cylinder")) {
            createObj(new Cylinder(this));
        }
        else if(comm.equals("Skeleton")) {
            createObj(new Skeleton(this));
        }
        else if(comm.equals("RotX")) {
            createObj(new RotX(this));
        }
        else if(comm.equals("Pos")) {
            createObj(new Pos(this));
        }
    }

    public void load(String fname) {
        try {
            Scanner scnr = new Scanner(new FileInputStream(fname));
            while(scnr.hasNext()) {
                String type = scnr.next();
                if(type.equals("cone")) {
                    Cone tmp = new Cone(this);
                    tmp.load(scnr);
                    createObj(tmp);
                }
                else if(type.equals("cylinder")) {
                    Cylinder tmp = new Cylinder(this);
                    tmp.load(scnr);
                    createObj(tmp);
                }
                else if(type.equals("skeleton")) {
                    Skeleton tmp = new Skeleton(this);
                    tmp.load(scnr);
                    createObj(tmp);
                }
            }
        }
        catch(Exception ex) {
            System.err.println("Can't load from file ["+fname+"]");
            ex.printStackTrace(System.err);
        }
    }
    private void createObj(Object3D o) {
        currObj = o;
        lst.addElement(currObj);
        repaint();
        jlst.repaint();
        showDetails(currObj);
    }
    
    private void showDetails(Object3D o) {
        if(!first) layout.removeAll();
        layout.add(o.getPanel());
        layout.add(o.getPose());
        if(first) {
            first = false;
        }
        else {
            layout.invalidate();
            layout.revalidate();
        }
    }
        
    boolean first = true;
    public static void main(String args[]) {
        if(args.length==0)
            new Screen();
        else
            new Screen(args[0]);
    }
}
