/* 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();
            }});
}
}