/***********************************************************************
  (C) 1998 Giovanni Trevisti

  You may distribute under the terms of the GNU General Public License.
***********************************************************************/

import java.applet.*;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.filechooser.*;
import java.io.*;

/*import javax.swing.event.*;
import javax.swing.JToolBar;
import javax.swing.AbstractButton;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JFrame;
import javax.swing.ImageIcon;
import javax.swing.JTextArea;
import javax.swing.JScrollPane;*/

import java.util.*;


class swingVirtualProc extends VirtualProcAbs
{
    JavaTurtle apl;     // memorizza il puntatore alla classe principale (applet)
    boolean isApplet = false;

    void resetButtons() {
        apl.playAction.setEnabled(true);
        apl.stepAction.setEnabled(true);
        apl.slowAction.setEnabled(true);
    }
    
    String getProgram() {
        return apl.textArea.getText() + System.getProperty("line.separator");
    }
    
    void outMessage(String s, int line) {
      String str;

      if (line >= 0) str = "Linea " + (line+1) + ": " + s;
      else str = s;
      System.err.println(str);
      apl.statusLine.setText(str);
    }
    
    boolean programHasChanged() {
        if (apl.toBeComp) {
            apl.toBeComp = false;
            return true;
        } else return false;
    }

    public void initTurtle(Object a) {
        MediaTracker tracker = new MediaTracker(this); // per caricare subito le immagini
    
        apl = (JavaTurtle) a;
        if (isApplet) {
            /*apl = (Applet)a;
            for (int i = 0; i < 8; i++) {
                img[i] = apl.getImage(apl.getCodeBase(), 
                                      "images/tarta" + i*45 + ".gif");
                tracker.addImage(img[i], i);
            }*/
        } else {
            for (int i = 0; i < 8; i++) {
                img[i] = Toolkit.getDefaultToolkit().getImage("images/tarta" + i*45 + ".gif");
                tracker.addImage(img[i], i);
            }
        }
        try
            {
                tracker.waitForAll();
            }
        catch(InterruptedException e) {}
        
    }
} // fine classe swingVirtualProc



class jtFilter extends javax.swing.filechooser.FileFilter {
    final static String jtExt = "jt";
    
    public boolean accept(File f) {

        if (f.isDirectory()) {
            return true;
        }

        String s = f.getName();
        int i = s.lastIndexOf('.');

        if (i > 0 &&  i < s.length() - 1) {
            String extension = s.substring(i+1).toLowerCase();
            if (jtExt.equals(extension)) {
                    return true;
            } else {
                return false;
            }
        }

        return false;
    }
    
    // The description of this filter
    public String getDescription() {
        return "JavaTurtle source files (*.jt)";
    }
}


/******************************************************************************
Classe: turtleApplication
Uso:    serve solo quando il programma e' usato come stand-alone: in questo
        caso crea il menu e gestisce l'evento di chiusura della finestra
******************************************************************************/
class JavaTurtleApplication extends JFrame
{
    public JavaTurtle jTurtle = null;

    public JavaTurtleApplication() {
        super();

        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                if (jTurtle != null) jTurtle.exitFromJavaTurtle();
            }
        });
    }

    public void setApplet(JavaTurtle jl) {
        jTurtle = jl;
    }
}



/*public class JavaTurtle extends JFrame implements KeyListener, MouseListener {*/
public class JavaTurtle extends JApplet implements KeyListener, MouseListener {
    //final static String LOOKANDFEEL = "Motif";
    //final static String LOOKANDFEEL = "Windows";
    final static String LOOKANDFEEL = null;
    
    static final String PROP_FILE = "JavaTurtle.ini";
    static final String VERSION = "JavaTurtle ver 0.63";
    static final String NONAME = "noname.jt";

    public JavaTurtleApplication jta = null;
    protected JTextArea textArea;
    protected JSplitPane splitPane;
    protected JScrollPane areaScrollPane;
    protected String newline = System.getProperty("line.separator");
    protected JMenu fileMenu;
    protected JMenu actionMenu;
    protected JPanel allPane, contentPane;
    
    protected swingVirtualProc VP;
    protected JTextField statusLine;
    protected JTextField textAreaStatusLine;
    
    protected JFileChooser fc = null;
    
    Properties defaultProps;
    String fileName;
    boolean newFile;
    boolean textModified;
    boolean toBeComp;
    
    // "azioni"
    protected Action newAction;
    protected Action openAction;
    protected Action saveAction;
    protected Action saveAsAction;
    protected Action exitAction;

    protected Action playAction;
    protected Action pauseAction;
    protected Action stopAction;
    protected Action stepAction;
    protected Action slowAction;



    private int saveTurtleProgram(String name) {
        BufferedOutputStream st = null;
        File file;

        if ((name.length() == 0) || (name.equals(NONAME))) {
            int returnVal = fc.showSaveDialog(this);

            if (returnVal == JFileChooser.APPROVE_OPTION) {
                file = fc.getSelectedFile();
                name = file.getPath();
                System.out.println("Nome: " + name);
            } else return 0;
        }

        try {
            st = new BufferedOutputStream(new FileOutputStream(name));
            int l = textArea.getText().length();
            byte buf[] = new byte[l];
            buf = textArea.getText().getBytes();
            st.write(buf, 0, l);

        } catch(Exception e) {
            statusLine.setText("Errore nella scrittura: " + e.toString());
            return -1;
        } finally
            {
                try {
                    if (st != null) st.close();
                } catch(Exception e) {
                }
            }
        // mette il nome del file nelle preferenze
        defaultProps.put("FileName", name);
        fileName = name;
        writeTitle();

        textModified = false;
        return 0;
    }

    private int loadTurtleProgram(String name) {
        final int bufSize = 1024;
        
        if (name.length() == 0) {
            File file;
            // chiede il nome del file da caricare
            int returnVal = fc.showOpenDialog(this);

            if (returnVal == JFileChooser.APPROVE_OPTION) {
                file = fc.getSelectedFile();
                name = file.getPath();
                System.out.println("Nome: " + name);
            } else return 0;
        }

        try {
            //InputStream is = new URL(getDocumentBase(), nome).openStream();
            BufferedInputStream st = new BufferedInputStream(new FileInputStream(name));
            byte buf[] = new byte[bufSize];
            int n_read, num;

            // cancella il contenuto attuale della textArea
            textArea.replaceRange("", 0, textArea.getText().length());

            // carica il contenuto del file nella textArea
            while ((num = st.available()) > 0) {
                n_read = st.read(buf, 0, Math.min(num, bufSize));
                textArea.append(new String(buf, 0, n_read));
            }
            // mette il caret all'inizio del programma
            textArea.select(0, 0);

            // mette il nome del file nelle preferenze
            defaultProps.put("FileName", name);
            statusLine.setText(" file: " + name);
            fileName = name;
            writeTitle();
        } catch(Exception e) {
            statusLine.setText("Errore nella lettura: " + e.toString());
            return -1;
        }
        newFile = true;
        toBeComp = true;
        return 0;
    }


    public void init() {
        boolean loaded = false;


        // se funziona come applicazione legge le dimensioni dalle preferenze
        if (jta != null) {
            String xs, ys;
            int x, y;

            // setta le dimensioni
            xs = defaultProps.getProperty("Xsize");
            ys = defaultProps.getProperty("Ysize");

            if ((xs != null) && (ys != null)) {
                x = Integer.valueOf(xs).intValue();
                y = Integer.valueOf(ys).intValue();

                if ((x >= 100) && (y >= 50)) {
                    if (jta != null) jta.setSize(x, y);
                } else {
                    jta.setSize(700, 400);
                }
            } else {
                jta.setSize(700, 400);
            }
        }

        // inizializza il VirtualProc
        VP.initTurtle(this);

        if (defaultProps.getProperty("FileName") != null)
            if (defaultProps.getProperty("FileName").length() > 0)
                if (loadTurtleProgram(defaultProps.getProperty("FileName")) == 0)
                    loaded = true;

        if (loaded == false) {
            defaultProps.put("FileName", NONAME);
            statusLine.setText(" file: " + NONAME);
            fileName = NONAME;
            writeTitle();
        }

        textArea.requestFocus();  // da' il focus alla TextArea
        statusLine.setText(VERSION);
        textModified  = false;
        toBeComp = true;
        newFile   = true;

        // File chooser per open e save
        jtFilter jtf = new jtFilter();
        try {
            String userDir = System.getProperty("user.dir");
            fc = new JFileChooser(userDir);
        } catch(Exception e) {
            fc = new JFileChooser();
        }                
        fc.addChoosableFileFilter(jtf);
        fc.setFileFilter(jtf);

        showLineNumber();
    }

    public void stop() {
        // salva le preferenze
        try
            {
                FileOutputStream defaultsOut = new FileOutputStream(PROP_FILE);
                defaultProps.save(defaultsOut, "File di preferenze di JavaTurtle");
                defaultsOut.close();
            }
        catch(Exception e) {
            System.out.println("Errore nella scrittura: " + e.toString());
        }
    }


    protected void exitFromJavaTurtle() {
        // se funziona come applicazione
        if (jta != null) {
            Dimension d1 = jta.getSize();
            defaultProps.put("Xsize", String.valueOf(d1.width));
            defaultProps.put("Ysize", String.valueOf(d1.height));
        }
        defaultProps.put("Xdiv", String.valueOf(splitPane.getDividerLocation()));

        stop();
        System.exit(0);
    }
    

    protected void createActionComponents(JToolBar toolBar) {
        JButton button = null;
        JMenuItem menuItem = null;


        // ***** PLAY *****
        playAction = new AbstractAction("Play", new ImageIcon("images/play.gif")) {
            public void actionPerformed(ActionEvent e) {
                playAction.setEnabled(false);
                stepAction.setEnabled(false);
                slowAction.setEnabled(false);
                int out = VP.run_program(0);
                if (out != 0) { // se c'e' stato un errore
                    playAction.setEnabled(true);
                    stepAction.setEnabled(true);
                    slowAction.setEnabled(true);
                }
            }
        };
        button = toolBar.add(playAction);
        button.setText(""); //an icon-only button
        button.setToolTipText("Esegue il programma");
        button.setMargin(new Insets(1,1,1,1));
        
        menuItem = actionMenu.add(playAction);
        menuItem.setIcon(null); //arbitrarily chose not to use icon in menu

        // ***** PAUSE *****
        pauseAction = new AbstractAction("Pausa", new ImageIcon("images/pause.gif")) {
            public void actionPerformed(ActionEvent e) {
                playAction.setEnabled(true);
                stepAction.setEnabled(true);
                slowAction.setEnabled(true);
                VP.pause();
            }
        };
        button = toolBar.add(pauseAction);
        button.setText(""); //an icon-only button
        button.setToolTipText("Congela l'esecuzione");
        button.setMargin(new Insets(1,1,1,1));
        menuItem = actionMenu.add(pauseAction);
        menuItem.setIcon(null); //arbitrarily chose not to use icon in menu
        
        // ***** STOP *****
        stopAction = new AbstractAction("Stop", new ImageIcon("images/stop.gif")) {
            public void actionPerformed(ActionEvent e) {
                VP.stop();
            }
        };
        button = toolBar.add(stopAction);
        button.setText(""); //an icon-only button
        button.setToolTipText("Termina l'esecuzione del programma");
        button.setMargin(new Insets(1,1,1,1));
        menuItem = actionMenu.add(stopAction);
        menuItem.setIcon(null); //arbitrarily chose not to use icon in menu
        
        //toolBar.add(Box.createHorizontalStrut(5));
        
        // ***** STEP *****
        stepAction = new AbstractAction("Un passo", new ImageIcon("images/step.gif")) {
            public void actionPerformed(ActionEvent e) {
                VP.step_by_step();                
            }
        };
        button = toolBar.add(stepAction);
        button.setText(""); //an icon-only button
        button.setToolTipText("Esegue un solo passo del programma");
        button.setMargin(new Insets(1,1,1,1));
        menuItem = actionMenu.add(stepAction);
        menuItem.setIcon(null); //arbitrarily chose not to use icon in menu
        
        // ***** SLOW *****
        slowAction = new AbstractAction("Rallentatore", new ImageIcon("images/slow.gif")) {
            public void actionPerformed(ActionEvent e) {
                VP.run_program(1);
            }
        };
        button = toolBar.add(slowAction);
        button.setText(""); //an icon-only button
        button.setToolTipText("Esegue il programma lentamente");
        button.setMargin(new Insets(1,1,1,1));
        menuItem = actionMenu.add(slowAction);
        menuItem.setIcon(null); //arbitrarily chose not to use icon in menu



        //****************************************************************************
        //toolBar.addSeparator();
        //toolBar.add(Box.createVerticalGlue());
        toolBar.add(Box.createHorizontalStrut(5));


        // New document
        newAction = new AbstractAction("Nuovo programma", new ImageIcon("images/new.gif")) {
            public void actionPerformed(ActionEvent e) {
                fileName = NONAME;
                textModified  = false;
                toBeComp = true;
                newFile   = true;
                textArea.setText("\n\nblocco_principale\n");
                writeTitle();
            }
        };
        button = toolBar.add(newAction);
        button.setText(""); //an icon-only button
        button.setToolTipText("Prepara l'inserimento di un nuovo programma");
        button.setMargin(new Insets(1,1,1,1));
        menuItem = fileMenu.add(newAction);
        menuItem.setIcon(null); //arbitrarily chose not to use icon in menu

        // Open
        openAction = new AbstractAction("Apri", new ImageIcon("images/open.gif")) {
            public void actionPerformed(ActionEvent e) {
                //displayResult("Action for first button/menu item", e);
                loadTurtleProgram("");
            }
        };
        button = toolBar.add(openAction);
        button.setText(""); //an icon-only button
        button.setToolTipText("Apre un programma salvato precedentemente");
        button.setMargin(new Insets(1,1,1,1));
        menuItem = fileMenu.add(openAction);
        menuItem.setIcon(null); //arbitrarily chose not to use icon in menu

        // Save
        saveAction = new AbstractAction("Salva", new ImageIcon("images/save.gif")) {
            public void actionPerformed(ActionEvent e) {
                saveTurtleProgram(fileName);
            }
        };
        button = toolBar.add(saveAction);
        button.setText("");
        button.setToolTipText("Salva il programma corrente sul disco");
        button.setMargin(new Insets(1,1,1,1));
        menuItem = fileMenu.add(saveAction);
        menuItem.setIcon(null); //arbitrarily chose not to use icon in menu

        
        
        // Save As
        saveAsAction = new AbstractAction("Salva come", new ImageIcon("images/save.gif")) {
            public void actionPerformed(ActionEvent e) {
                saveTurtleProgram("");
            }
        };
        menuItem = fileMenu.add(saveAsAction);
        menuItem.setIcon(null); //arbitrarily chose not to use icon in menu

        fileMenu.addSeparator();

        // Exit
        exitAction = new AbstractAction("Esci", new ImageIcon("images/save.gif")) {
            public void actionPerformed(ActionEvent e) {
                exitFromJavaTurtle();
            }
        };
        menuItem = fileMenu.add(exitAction);
        menuItem.setIcon(null); //arbitrarily chose not to use icon in menu


        //toolBar.add(Box.createHorizontalGlue());

    }

    private static void initLookAndFeel() { 
        String lookAndFeel = null;

        if (LOOKANDFEEL != null) {
            if (LOOKANDFEEL.equals("Metal")) {
                lookAndFeel = UIManager.getCrossPlatformLookAndFeelClassName();
            } else if (LOOKANDFEEL.equals("System")) {
                lookAndFeel = UIManager.getSystemLookAndFeelClassName();
            } else if (LOOKANDFEEL.equals("Mac")) {
                lookAndFeel = "com.sun.java.swing.plaf.mac.MacLookAndFeel";
                //PENDING: check!
            } else if (LOOKANDFEEL.equals("Windows")) {
                lookAndFeel = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
            } else if (LOOKANDFEEL.equals("Motif")) {
                lookAndFeel = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
            }

            if (true) {
                System.out.println("About to request look and feel: " 
                                   + lookAndFeel);
            }

            try {
                UIManager.setLookAndFeel(lookAndFeel);
            } catch (ClassNotFoundException e) {
                System.err.println("Couldn't find class for specified look and feel:"
                                   + lookAndFeel);
                System.err.println("Did you include the L&F library in the class path?");
                System.err.println("Using the default look and feel.");
            } catch (UnsupportedLookAndFeelException e) {
                System.err.println("Can't use the specified look and feel ("
                                   + lookAndFeel
                                   + ") on this platform.");
                System.err.println("Using the default look and feel.");
            } catch (Exception e) { 
                System.err.println("Couldn't get specified look and feel ("
                                   + lookAndFeel
                                   + "), for some reason.");
                System.err.println("Using the default look and feel.");
                e.printStackTrace();
            } 
        }
    }

    public JavaTurtle() {

        //Do frame stuff.
        super();
        
        String xs, ys;

        // setta il look&feel
        initLookAndFeel();

        // legge le preferenze
        try
            {
                defaultProps = new Properties();
                FileInputStream defaultStream = new FileInputStream(PROP_FILE);
                defaultProps.load(defaultStream);
                defaultStream.close();
            }
        catch(Exception e) {
            System.err.println("Errore nella lettura delle preferenze: " + e.toString());
        }


        //Create the toolbar.
        JToolBar toolBar = new JToolBar();
        fileMenu = new JMenu("File");
        actionMenu = new JMenu("Esecuzione");
        //addButtons(toolBar);
        
        createActionComponents(toolBar);
        
        //Set up the menu bar.
        JMenuBar mb = new JMenuBar();
        mb.add(fileMenu);
        mb.add(actionMenu);
        //mb.add(createAbleMenu());
        setJMenuBar(mb);
        
        
        //Create the text area used for output.
        textArea = new JTextArea(10, 40);
        textArea.addMouseListener(this);
        textArea.setFont(new Font("Courier", Font.PLAIN, 12));
        textArea.setText("");
        textArea.setLineWrap(false);
        textArea.setWrapStyleWord(false);
        textArea.addKeyListener(this);
        textArea.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 0));


        areaScrollPane = new JScrollPane(textArea);
        /*areaScrollPane.setVerticalScrollBarPolicy(
                        JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);*/
        //areaScrollPane.setPreferredSize(new Dimension(250, 250));
        //areaScrollPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));

        // linea di stato associata alla text area
        textAreaStatusLine = new JTextField(20);
        textAreaStatusLine.setEditable(false);
        textAreaStatusLine.setText("");

        JPanel leftPane = new JPanel();
        leftPane.setLayout(new BorderLayout());
        leftPane.add(areaScrollPane, BorderLayout.CENTER);
        leftPane.add(textAreaStatusLine, BorderLayout.SOUTH);


        // VirtualProc
        VP = new swingVirtualProc();
        // Informa che il componente e' opaco, cosi' da non perdere tempo a cercare
        // di disegnare quello che c'e' dietro
        VP.setOpaque(true);
        //VP.setBorder(BorderFactory.createEmptyBorder(50, 50, 50, 50));


        // split pane
        splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
        splitPane.setLeftComponent(leftPane);
        //splitPane.setRightComponent(VP);
        splitPane.setRightComponent(VP);
        splitPane.setOneTouchExpandable(true);
        splitPane.setDividerSize(8);
        
        xs = defaultProps.getProperty("Xdiv");
        if (xs != null) {
            splitPane.setDividerLocation(Integer.valueOf(xs).intValue());
        } else {
            splitPane.setDividerLocation(300);
        }

        //Provide a preferred size for the split pane
        //splitPane.setPreferredSize(new Dimension(700, 350));


        //Lay out the content pane.
        contentPane = new JPanel();
        contentPane.setLayout(new BorderLayout());

        /*xs = defaultProps.getProperty("Xsize");
        ys = defaultProps.getProperty("Ysize");
        if ((xs != null) && (ys != null)) {
            contentPane.setPreferredSize(new Dimension(Integer.valueOf(xs).intValue(), 
                                           Integer.valueOf(ys).intValue()));
        } else {
            contentPane.setPreferredSize(new Dimension(700, 400));
        }

        if (jta != null) jta.setSize(Integer.valueOf(xs).intValue(), 
                                     Integer.valueOf(ys).intValue());*/

        contentPane.add(splitPane, BorderLayout.CENTER);
        contentPane.add(toolBar, BorderLayout.SOUTH);


        statusLine = new JTextField(20);
        statusLine.setEditable(false);
        statusLine.setText("");

        /*JPanel allPane = new JPanel();*/
        allPane = new JPanel();
        allPane.setLayout(new BorderLayout());
        allPane.add(contentPane, BorderLayout.CENTER);
        allPane.add(statusLine, BorderLayout.SOUTH);

        setContentPane(allPane);
        //getContentPane().add("Center", allPane);

        //VP.initTurtle(this);
        //setSize(500, 300);
    }

    protected void writeTitle() {
        if (jta != null)
            jta.setTitle(VERSION+" ("+fileName+")");
    }

    protected void showLineNumber() {
        int cp = textArea.getCaretPosition();
        try {
            int al = textArea.getLineOfOffset(cp)+1;
            int tl = textArea.getLineCount()+1;
            textAreaStatusLine.setText("linea "+al+" di "+tl);
            
        } catch(Exception error) {
        }
    }

    protected void displayResult(String actionDescription) {
        textArea.append(actionDescription + newline);
    }

    public void keyTyped(KeyEvent e) {
    }
    public void keyPressed(KeyEvent e) {
    }
    public void keyReleased(KeyEvent e) {
        textModified = true;
        toBeComp = true;
        showLineNumber();
    }

    public void mousePressed(MouseEvent e) {
    }
    public void mouseClicked(MouseEvent e) {
    }
    public void mouseReleased(MouseEvent e) {
        showLineNumber();    
    }
    public void mouseEntered(MouseEvent e) {
    }
    public void mouseExited(MouseEvent e) {
    }


    public static void main(String[] args) {

        JavaTurtleApplication jta = new JavaTurtleApplication();
        JavaTurtle jt = new JavaTurtle(); // JApplet
        jt.jta = jta;
        jta.setApplet(jt);

        jt.init();
        jt.start();

        jta.getContentPane().add("Center", jt);
        //jta.setSize(700, 480);
        jta.setVisible(true);
    }
}

