/* MEPer.java
* This file is part of source files of MEPer.
* It create the main window and starts the program.
*
* @author Shengjun Pan
*/

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

import javax.swing.*;
import javax.swing.text.*;
import java.io.*;


public class MEPer extends JFrame {

     static int initWidth=1000;
     static int initHeight=618;

     boolean ispc;
     JToolBar toolbar;
     JTextPane editor;
     JLabel graph;
     JLabel statusLabel;

     JButton saveButton;
     JButton previewButton;
     JButton helpButton;

     Action saveAction;
     Action previewAction;
     Action helpAction;
     JDialog helpWindow=null;

     EditorFilter editorFilter;

     String workingDir;

     static String helpTitle = "Metapost Editor and Previewer v1.0";
     static String dirName= "MetaPostPreview";
     static String metapost = "mpost -interaction=nonstopmode preview.mp end";
     static String convert = "convert preview.eps preview.png";
     static String ghostscript = " -dBATCH -dNOPAUSE -dQUIET"+
         " -dEPSCrop -dEPSFitPage -dGraphicsAlphaBits=4 -dTextAlphaBits=4"+
         " -sDEVICE=pngalpha -sOutputFile=preview.png preview.eps";

     private void saveFile() {
       try {
           FileWriter outputStream = new FileWriter(workingDir+File.separator+"preview.mp");
           outputStream.write(editor.getText());
           outputStream.close();
           saveButton.setEnabled(false);
       } catch (Throwable t){}
     }

    private void consumeRuntime(Process proc) {
        final BufferedReader errorConsumer = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
        final BufferedReader outputConsumer = new BufferedReader(new InputStreamReader(proc.getInputStream()));
        Thread errorThread = new Thread() {
                public void run() {
                    String nextline;
                    try {
                        while ( (nextline = errorConsumer.readLine()) != null);
                    } catch (Throwable t){}
                }};
        Thread outputThread = new Thread(){
                public void run() {
                    String nextline;
                    try {
                        while ( (nextline = outputConsumer.readLine()) != null);
                    } catch (Throwable t){}
                }};
        errorThread.start();
        outputThread.start();
    }

     private void createActions(){
         saveAction = new AbstractAction () {
             public void actionPerformed(ActionEvent e) {
               saveFile();
               statusLabel.setText("preview.mp saved in directory: "+ workingDir);
               editor.requestFocus();
             }};
         previewAction = new AbstractAction(){
             public void actionPerformed(ActionEvent e) {
                   saveFile();

                   SwingUtilities.invokeLater(new Runnable () {
                   public void run() {
                     try {
                       File previewmp= new File(workingDir+File.separator+"preview.mp");
                       File preview1= new File(workingDir+File.separator+"preview.1");
                       File previeweps= new File(workingDir+File.separator+"preview.eps");
                       File previewpng= new File(workingDir+File.separator+"preview.png");

                       (new File(workingDir+File.separator+"preview.1")).delete();
                       (new File(workingDir+File.separator+"preview.eps")).delete();
                       (new File(workingDir+File.separator+"preview.png")).delete();

                       File dir = new File(workingDir);
                       Process proc=null;

                       if(previewmp.exists()) {
                           statusLabel.setText("Generating preview image...");
                           proc = Runtime.getRuntime().exec(metapost,null,dir);
                           if(proc != null) {
                               consumeRuntime(proc);
                               proc.waitFor();
                               proc = null;
                           } else {
                               statusLabel.setText("Error on running metapost.");
                               return;
                           }
                       } else {
                           statusLabel.setText("Can't find file: preview.mp");
                           return;
                       }

                       if(preview1.exists()) {
                           preview1.renameTo(previeweps);
                           statusLabel.setText("preview.1 renamed to preview.eps.");
                       } else {
                           statusLabel.setText("Cant' find file: preview.1");
                           return;
                       }

                       if(previeweps.exists()) {
                           // try convert
                           proc = Runtime.getRuntime().exec(convert, null,dir);
                           if(proc != null) {
                               consumeRuntime(proc);
                               proc.waitFor();
                               proc=null;
                           } else
                               statusLabel.setText("Error on running ImageMagick." +
                                                   "Try ghostscript...");
                           // try ghostscript
                           if(!previewpng.exists()) {
                               proc = Runtime.getRuntime().exec(ghostscript, null,dir);
                               if(proc != null) {
                                   consumeRuntime(proc);
                                   proc.waitFor();
                                   proc=null;
                               } else {
                                   statusLabel.setText("Error on running ghostscript.");
                                   return;
                               }
                           }
                       } else {
                           statusLabel.setText("Can't find file: preview.eps");
                           return;
                       }

                       if(previewpng.exists()) {
                           statusLabel.setText("Reloading preview image...");
                           ((ImageIcon)graph.getIcon()).getImage().flush();
                           ImageIcon img = new ImageIcon(workingDir+File.separator+"preview.png");
                           graph.setIcon(img);
                           graph.repaint();
                           statusLabel.setText(" preview.[mp|eps|png] saved in directory: "+ workingDir);
                       } else {
                           statusLabel.setText("Can't find file: preview.png");
                           return;
                       }

                       } catch (Throwable t) {}
                    editor.requestFocus();
                   }});
             }};
        helpAction = new AbstractAction () {
            public void actionPerformed(ActionEvent e) {
                if(helpWindow==null){
                   helpWindow = new JDialog();
                   JTextArea infoPane = new JTextArea();
                   infoPane.setEditable(false);
                   infoPane.setAlignmentX(Component.CENTER_ALIGNMENT);
                   //URL readmeURL = this.getClass().getResource("README.txt");

                   try {
                       String nextline;
                       InputStream is = this.getClass().getResourceAsStream("README.txt");
                       InputStreamReader isr = new InputStreamReader(is);
                       BufferedReader inputStream = new BufferedReader(isr);
                       nextline = inputStream.readLine();
                       infoPane.getDocument().insertString(infoPane.getDocument().getLength(), nextline, null);
                       while(!(nextline = inputStream.readLine()).equals(null))
                           infoPane.getDocument().insertString(infoPane.getDocument().getLength(), "\n"+nextline, null);
                       inputStream.close();
                   } catch(Throwable t) {}

                   Container helpPane = helpWindow.getContentPane();

                   helpPane.setLayout(new BoxLayout(helpPane,BoxLayout.Y_AXIS));
                   JLabel titleLabel = new JLabel(helpTitle);
                   titleLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
                   helpPane.add(titleLabel);
                   infoPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10));
                   helpPane.add(infoPane);
                   JButton closeButton = new JButton(new AbstractAction() {
                        public void actionPerformed(ActionEvent e) {
                            helpWindow.setVisible(false);
                            editor.requestFocus();
                        }});
                   closeButton.setText("Close");
                   closeButton.setAlignmentX(Component.CENTER_ALIGNMENT);
                   helpPane.add(closeButton);
                   helpWindow.addWindowListener( new WindowAdapter() {
                       @Override
                       public void windowClosing(WindowEvent e) {
                           helpWindow.setVisible(false);
                           editor.requestFocus();
                           }});
                   helpWindow.pack();
                }

                int topX, topY;
                topX = (MEPer.this.getWidth()-helpWindow.getWidth())/2;
                topY = (MEPer.this.getHeight()-helpWindow.getHeight())/3;
                Point helpLocation =MEPer.this.getLocation();
                helpLocation.translate(topX, topY);
                helpWindow.setLocation(helpLocation);
                helpWindow.setVisible(true);
            }
        };
     }

   public MEPer() {

           String osName=System.getProperty("os.name").toLowerCase();
           ispc = osName.indexOf("win")!=-1;
           ghostscript = (ispc?"gsWin32":"gs") + ghostscript;

           workingDir = System.getProperty("user.dir");
           File newdir = new File(System.getProperty("user.home") + File.separator+ dirName);

           if(newdir.exists() || newdir.mkdir()) {
               workingDir = newdir.getAbsolutePath();
               System.setProperty("user.dir", workingDir);
           }

           // Create a toolbar
           toolbar = new JToolBar();
           createActions();

           saveButton = new JButton(saveAction);
           saveButton.setText("Save");
           saveButton.setToolTipText("Ctr+S");
           toolbar.add(saveButton);

           previewButton = new JButton(previewAction);
           previewButton.setText("Preview");
           previewButton.setToolTipText("Ctr+P");
           toolbar.add(previewButton);

           toolbar.add(Box.createHorizontalGlue());

           helpButton = new JButton(helpAction);
           helpButton.setText("Help");
           toolbar.add(helpButton);

           statusLabel = new JLabel();
           statusLabel.setText("Working directory: "+System.getProperty("user.dir"));
           statusLabel.setFont(new Font(Font.SERIF,Font.PLAIN,12));

           //Create a text pane for the editing area
           editor = new JTextPane();

           editorFilter = new EditorFilter(editor, saveButton);
           ((AbstractDocument)editor.getDocument()).setDocumentFilter(editorFilter);
           editor.getInputMap().put(KeyStroke.getKeyStroke("ctrl S"), "save");
           editor.getInputMap().put(KeyStroke.getKeyStroke("ctrl P"), "preview");
           editor.getActionMap().put("save", saveAction);
           editor.getActionMap().put("preview", previewAction);
/*
 */
           // load file "preview.mp" into editor
           BufferedReader inputStream;
           String mpName = workingDir+File.separator+"preview.mp";
           try {
               boolean loadTemplate = false;
               if ((new File(mpName)).exists())
                   inputStream = new BufferedReader(new FileReader(mpName));
               else{
                   statusLabel.setText("Can't load preview.mp; using template instead.");
                   loadTemplate=true;
                   InputStream is = this.getClass().getResourceAsStream("template.mp");
                   InputStreamReader isr = new InputStreamReader(is);
                   inputStream = new BufferedReader(isr);
               }
               String nextline;
               nextline = inputStream.readLine();
               editor.getDocument().insertString(editor.getDocument().getLength(), nextline, null);
               while(!((nextline = inputStream.readLine())==null))
                   editor.getDocument().insertString(editor.getDocument().getLength(), "\n"+nextline, null);
               inputStream.close();
               if(loadTemplate) editor.setCaretPosition(editor.getDocument().getLength()-12);
           } catch(Throwable t) {}

           saveButton.setEnabled(false);

           JScrollPane editorScrollPane = new JScrollPane(editor);

           graph =  new JLabel();
           graph.setOpaque(true);
           graph.setBackground(Color.WHITE);
           ImageIcon img = new ImageIcon(workingDir+File.separator+"preview.png");
           graph.setIcon(img);
           graph.setHorizontalAlignment(SwingConstants.CENTER);
           graph.setVerticalAlignment(SwingConstants.CENTER);

           JScrollPane imagePane = new JScrollPane(graph);

           editorScrollPane.setPreferredSize(new Dimension(initWidth/2,initHeight));

           JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
                                      editorScrollPane, imagePane);

           this.getContentPane().add(toolbar,BorderLayout.PAGE_START);
           this.getContentPane().add(splitPane, BorderLayout.CENTER);
           this.getContentPane().add(statusLabel,BorderLayout.PAGE_END);

           addWindowListener( new WindowAdapter() {
               @Override
               public void windowOpened( WindowEvent e ){ editor.requestFocus();}
               @Override
               public void windowClosing(WindowEvent e) {
                   if( saveButton.isEnabled()) {
                       int response = JOptionPane.showConfirmDialog(
                                       MEPer.this,
                                      "Save file before exit?",
                                      "Closing...",
                                      JOptionPane.YES_NO_OPTION);
                   if (response == JOptionPane.YES_OPTION)  saveFile();
                   }}
           });
   }

   private static void initialize() {
           MEPer mainFrame = new MEPer();
           mainFrame.setTitle("MetaPost Editor and Previewer");

           mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

           Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
           int topX = (int)((screenSize.getWidth()-initWidth)/2);
           if(topX<0) topX=0;
           int topY =(int)((screenSize.getHeight()-initHeight)/3);
           if(topY<0) topY=0;
           mainFrame.setPreferredSize(new Dimension(initWidth,initHeight));
           mainFrame.setLocation(topX, topY);
           mainFrame.pack();
           mainFrame.setVisible(true);
   }

   public static void main(String args[]) {
       SwingUtilities.invokeLater(new Runnable() {
           public void run() {
               initialize();
           }});
}
}