/*
 * EditorWindow.java
 * $Date$
 * $Author$
 * $Revision$
 */

package cz.tkubin.editor;

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

/**
 *
 * @author cathack
 */
public class EditorWindow extends JFrame {
    
    protected static final String DEFAULT_WINDOW_TITLE = "Text editor";
    protected static final String NO_FILENAME = "(new document)";
    protected static final String DOC_CHANGED_TITLE_SUFFIX = " *";
    protected static final Font DEFAULT_FONT = new Font(Font.MONOSPACED, Font.PLAIN, 12);
    protected static final int DEFAULT_ROWS_COUNT = 0;
    protected static final int DEFAULT_COLUMNS_COUNT = 0;
    protected static final int BORDER = 10;
    protected static final boolean DEFAULT_WRAP_TEXT = false;
    
    private JTextArea textArea;
    private JCheckBoxMenuItem cWrap;
    
    private JFileChooser chooser;
    private File currentFile;
    private boolean documentOpen;
    private boolean documentChanged;
    
    public EditorWindow() {
        super(DEFAULT_WINDOW_TITLE);
        
        // GUI
        this.addWindowListener(new WindowAdapter() {
            @Override public void windowClosing(WindowEvent e) {
                actionQuit(true);
            }
        });
        this.setPreferredSize(new Dimension(800, 600));
        
        //contents
        Container pane = this.getContentPane();
        pane.setLayout(new BorderLayout(BORDER, BORDER));
        textArea = new JTextArea(DEFAULT_ROWS_COUNT, DEFAULT_COLUMNS_COUNT);
        textArea.setFont(DEFAULT_FONT);
        textArea.addKeyListener(new KeyAdapter() {
            @Override public void keyTyped(KeyEvent e) {
                setDocumentChanged(true);
            }
        });
        pane.add(new JScrollPane(textArea));
        setJMenuBar(createMenuBar());
        pack();
        
        // filechooser
        chooser = new JFileChooser();
        chooser.setFileFilter(new FileFilter() {
            private static final String EXTENSION = ".txt";
            @Override public boolean accept(File pathname) {
                return (pathname.isDirectory() || pathname.getName().endsWith(EXTENSION));
            }
            @Override public String getDescription() {
                return "Text file (*" + EXTENSION + ")";
            }
        });
        
        // init
        setCurrentFile(null);
        setDocumentOpen(false);
        documentChanged = false;
    }
    
    private JMenuBar createMenuBar() {
        JMenuBar mb = new JMenuBar();
        
        // File
        JMenu menuFile = new JMenu("File");
        menuFile.setMnemonic('F');
        // File -> New
        JMenuItem mNew = new JMenuItem("New");
        mNew.setMnemonic('N');
        mNew.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK));
        mNew.addActionListener(new ActionListener() {
            @Override public void actionPerformed(ActionEvent e) {
                actionNew();
            }
        });
        menuFile.add(mNew);
        // File -> Open
        JMenuItem mOpen = new JMenuItem("Open ...");
        mOpen.setMnemonic('O');
        mOpen.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.CTRL_MASK));
        mOpen.addActionListener(new ActionListener() {
            @Override public void actionPerformed(ActionEvent e) {
                actionOpen();
            }
        });
        menuFile.add(mOpen);
        // File -> Save
        JMenuItem mSave = new JMenuItem("Save");
        mSave.setMnemonic('S');
        mSave.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK));
        mSave.addActionListener(new ActionListener() {
            @Override public void actionPerformed(ActionEvent e) {
                actionSave();
            }
        });
        menuFile.add(mSave);
        // File -> Save as
        JMenuItem mSaveAs = new JMenuItem("Save as ...");
        mSaveAs.setMnemonic('a');
        mSaveAs.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK | ActionEvent.SHIFT_MASK));
        mSaveAs.addActionListener(new ActionListener() {
            @Override public void actionPerformed(ActionEvent e) {
                actionSaveAs();
            }
        });
        menuFile.add(mSaveAs);
        // File -> Close
        JMenuItem mClose = new JMenuItem("Close");
        mClose.setMnemonic('C');
        mClose.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, ActionEvent.CTRL_MASK));
        mClose.addActionListener(new ActionListener() {
            @Override public void actionPerformed(ActionEvent e) {
                actionClose();
            }
        });
        menuFile.add(mClose);
        
        menuFile.addSeparator();
        // File -> Quit
        JMenuItem mQuit = new JMenuItem("Quit");
        mQuit.setMnemonic('Q');
        mQuit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, ActionEvent.CTRL_MASK));
        mQuit.addActionListener(new ActionListener() {
            @Override public void actionPerformed(ActionEvent e) {
                actionQuit();
            }
        });
        menuFile.add(mQuit);
        
        // View
        JMenu menuView = new JMenu("View");
        menuView.setMnemonic('V');
        // View -> Wrap text
        cWrap = new JCheckBoxMenuItem("Wrap text", DEFAULT_WRAP_TEXT);
        cWrap.setMnemonic('W');
        cWrap.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, ActionEvent.CTRL_MASK));
        cWrap.addActionListener(new ActionListener() {
            @Override public void actionPerformed(ActionEvent e) {
                actionToggleWrap();
            }
        });
        menuView.add(cWrap);
        
        
        mb.add(menuFile);
        mb.add(menuView);
        
        return mb;
    }
    
    protected void setCurrentFile(File file) {
        this.currentFile = file;
        resetWindowTitle();
    }
    
    protected String getCurrentFileName() {
        if (currentFile == null)
            return NO_FILENAME;
        else
            return currentFile.getName();
    }
    
    protected void setDocumentChanged(boolean changed) {
        if (this.documentChanged != changed) {
            this.documentChanged = changed;
            resetWindowTitle();
        }
        else
            this.documentChanged = changed;
    }
    
    protected void setDocumentOpen(boolean open) {
        this.documentOpen = open;
        this.textArea.setVisible(open);
        this.textArea.setEditable(open);
        resetWindowTitle();
    }
    
    protected void resetWindowTitle() {
        if (documentOpen)
            this.setTitle(DEFAULT_WINDOW_TITLE + " - " + getCurrentFileName() + (documentChanged ? DOC_CHANGED_TITLE_SUFFIX : ""));
        else
            this.setTitle(DEFAULT_WINDOW_TITLE);
    }
    
    protected void showErrorDialog(Exception e) {
        JOptionPane.showMessageDialog(this, e.getMessage(), e.getClass().getName(), JOptionPane.ERROR_MESSAGE);
    }

    private int showYesNoCancelDialog(String message, String title) {
        Object[] options = {"Yes", "No", "Cancel"};
        return JOptionPane.showOptionDialog(this, message, title,
            JOptionPane.YES_NO_CANCEL_OPTION,
            JOptionPane.WARNING_MESSAGE,
            null, options, options[2]);
    }
    
    private int showYesNoDialog(String message, String title) {
        Object[] options = {"Yes", "No"};
        return JOptionPane.showOptionDialog(this, message, title,
            JOptionPane.YES_NO_OPTION,
            JOptionPane.WARNING_MESSAGE,
            null, options, options[0]);
    }

    private boolean testChangedDocumentClosing() {
        return testChangedDocumentClosing(false);
    }
    
    private boolean testChangedDocumentClosing(boolean mustResponse) {
        if (documentOpen && documentChanged) {
            int response;
            if (mustResponse)
                response = showYesNoDialog(getCurrentFileName() + " isn't saved. Do you want to save it?", "File isn't saved");
            else
                response = showYesNoCancelDialog(getCurrentFileName() + " isn't saved. Do you want to save it?", "File isn't saved");
            if (response == 0)
                actionSave();
            if (response == 2)
                return true;
        }
        return false;
    }
    
    protected File openFileDialog() {
        if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION)
            return chooser.getSelectedFile();
        else
            return null;
    }
    
    protected File saveFileDialog() {
        if (chooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION)
            return chooser.getSelectedFile();
        else
            return null;
    }
    
    private void openDocument(File file) throws FileNotFoundException, IOException {
        BufferedReader reader = new BufferedReader(new FileReader(file));
        StringBuffer sb = new StringBuffer();
        String line;
        while ((line = reader.readLine()) != null) {
            sb.append(line);
            sb.append('\n');
        }
        if (sb.length() > 0)
            sb.deleteCharAt(sb.length() - 1);
        textArea.setText(sb.toString());
        
        setDocumentOpen(true);
        setCurrentFile(file);
        setDocumentChanged(false);
    }
    
    private void saveDocument(File file) throws IOException {
        BufferedWriter writer = new BufferedWriter(new FileWriter(file));
        String[] lines = textArea.getText().split("\\n");
        boolean firstLine = true;
        for (String line : lines) {
            if (firstLine)
                firstLine = false;
            else
                writer.newLine();
            writer.write(line);
        }
        writer.close();
        
        setDocumentChanged(false);
    }
    
    protected void actionNew() {
        if (testChangedDocumentClosing())
            return;
        
        textArea.setText("");
        setCurrentFile(null);
        setDocumentOpen(true);
        setDocumentChanged(false);
    }
    
    protected void actionOpen() {
        if (testChangedDocumentClosing())
            return;

        File file = openFileDialog();
        if (file == null)
            return;
            
        try {
            openDocument(file);
        } catch (FileNotFoundException ex) {
            showErrorDialog(ex);
        } catch (IOException ex) {
            showErrorDialog(ex);
        }
    }
    
    protected void actionSave() {
        if (documentOpen) {
            if (currentFile == null) {
                actionSaveAs();
                return;
            }
            if (documentChanged) {
                try {
                    saveDocument(currentFile);
                } catch (IOException ex) {
                    showErrorDialog(ex);
                }
            }
        }
    }
    
    protected void actionSaveAs() {
        if (documentOpen) {
            File file = saveFileDialog();
            if (file == null)
                return;
            try {
                saveDocument(file);
                setCurrentFile(file);
            } catch (IOException ex) {
                showErrorDialog(ex);
            }
        }
    }
    
    protected void actionClose() {
        if (documentOpen) {
            if (testChangedDocumentClosing())
                return;

            setCurrentFile(null);
            setDocumentOpen(false);
            setDocumentChanged(false);
        }
    }
    
    protected void actionQuit() {
        actionQuit(false);
    }
    
    protected void actionQuit(boolean mustResponse) {
        if (testChangedDocumentClosing(mustResponse))
            return;
        System.exit(0);
    }
    
    protected void actionToggleWrap() {
        textArea.setLineWrap(cWrap.getState());
    }
    
    public static void main(String... args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override public void run() {
                new EditorWindow().setVisible(true);
            }
        });
    }
}