package org.jcon.test;

import org.jcon.ui.VisualLib;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JToolBar;
import javax.swing.text.JTextComponent;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;

/**
 * Test sophisticated text editor, using Swing Notepad 
 * example as starting point. Became org.jcon.ui.TextEditor.
 * THIS CLASS MAY BE REMOVED ***
 * 
 * @author Jack Harich - java org.jcon.test.TestEditor
 */
public class TestEditor implements ActionListener, 
    UndoableEditListener, KeyListener {

//---------- Private Fields ------------------------------
private JFrame         frame;
private JTextComponent editor;

private UndoManager    undoManager;
private UndoAction     undoAction; // Inner class
private RedoAction     redoAction; // Inner class

private JButton        undoBtn;
private JButton        redoBtn;

//---------- Initialization ------------------------------
public static void main(String args[]) {
    new TestEditor().start();
}
public void start() {
    print("Starting test...");
    VisualLib.installLookAndFeel();
    
    // Prepare frame
    frame = new JFrame("This is a Test");
    frame.setBackground(Color.lightGray);
        
    // Undo feature
    undoManager = new UndoManager();
    undoAction = new UndoAction();
    redoAction = new RedoAction();
    
    undoBtn = createButton("Undo", undoAction,
        "Undoes the last edit - Control Z");
    redoBtn = createButton("Redo", redoAction,
        "Redoes the last edit that was undone - Control R");
    
    // Prepare toolBar *** more later
    // Undo, Redo, (gap) Apply, Save, Revert
    JToolBar toolbar = new JToolBar();
    toolbar.add(undoBtn);
    toolbar.add(redoBtn);
    frame.getContentPane().add("North", toolbar);
    
    // Put textArea in scroller, add to frame
    editor = new JTextArea();
    editor.setFont(new Font("Courier", Font.PLAIN, 12));
    editor.getDocument().addUndoableEditListener(this);
    editor.addKeyListener(this); // For hot keys
    
    JScrollPane scroller = new JScrollPane();
    scroller.getViewport().add(editor);
    scroller.getViewport().setBackingStoreEnabled(true); // Doesn't seem to spped up yet
        
    frame.getContentPane().add("Center", scroller);
    
    // done
    frame.setSize(500, 600);
    frame.setVisible(true);
    
    print("Test started");
}
//---------- ActionListener Implementation ---------------
public void actionPerformed(ActionEvent evt) {
    // *** Drop??? Not used
}
//---------- UndoableEditListener Implementation ---------
public void undoableEditHappened(UndoableEditEvent evt) {
    //print(".undoableEditHappened() - " + evt.getEdit().getPresentationName());
    undoManager.addEdit(evt.getEdit());
    undoAction.update();
    redoAction.update();
}
//---------- KeyListener Implementation ------------------
public void keyPressed(KeyEvent evt) {
    //print("pressed evt.getKeyCode() = " + evt.getKeyCode() );    
    
    // Hot keys
    if (! evt.isControlDown()) return;
    
    int keyCode = evt.getKeyCode();
    
    if (keyCode == KeyEvent.VK_Z) {
        undoAction.perform();
        
    } else if (keyCode == KeyEvent.VK_R) {
        redoAction.perform();
        
    } else if (keyCode == KeyEvent.VK_HOME) {
        editor.setCaretPosition(0);

    } else if (keyCode == KeyEvent.VK_END) {
        int length = editor.getText().length();
        editor.setCaretPosition(length);
    }    
}
public void keyReleased(KeyEvent evt) { }
public void keyTyped(KeyEvent evt) { }

//---------- Private Methods -----------------------------
private JButton createButton(String text, Action action,
                                String toolTip) {
            
    JButton button = new JButton(text);
    button.setActionCommand((String)action.getValue(Action.NAME));
    button.setToolTipText(toolTip);
    button.addActionListener(action);
    action.addPropertyChangeListener(new ActionManager(button));    
    button.setEnabled(action.isEnabled()); 
    button.setForeground(Color.blue);
    
    // Remove excess left and right space
    //print("button margin - " + button.getMargin());
    Insets margin = button.getMargin();
    margin.left = margin.top; // Was 14, now 2   
    margin.right = margin.top;
    button.setMargin(margin);
    //print("button margin - " + button.getMargin());    
    return button;
}    
//--- Std
private static void print(String text) {
    System.out.println(text);
}
//========== Inner Classes ===============================
// Handles button enablement, maybe more later such as
// menu item string maintenance like in Notepad example
private class ActionManager implements PropertyChangeListener {
    private Component comp;
    
    ActionManager(Component component) {
        comp = component;
    }
    //---------- PropertyChangeListener Implementation
    public void propertyChange(PropertyChangeEvent evt) {
        if (evt.getPropertyName().equals("enabled")) {
            boolean enabled = ((Boolean)evt.getNewValue()).booleanValue();
            comp.setEnabled(enabled);
        }
    }
}    
private class UndoAction extends AbstractAction {
    public UndoAction() {
        super("Undo");
        setEnabled(false);
    }
    public void actionPerformed(ActionEvent evt) {
        try {
            if(undoManager.canUndo()) undoManager.undo();
        } catch (CannotUndoException ex) {
            print(" - UndoAction - Unable to undo: " + ex);
            ex.printStackTrace();
        }
        update();
        redoAction.update();
    }
    protected void update() {
        if(undoManager.canUndo()) {
            setEnabled(true);
            putValue(Action.NAME, undoManager.getUndoPresentationName());
        }
        else {
            setEnabled(false);
            putValue(Action.NAME, "Undo");
        }
    }
    protected void perform() {
        if (isEnabled()) actionPerformed(null);
    }
} // End inner class
    
private class RedoAction extends AbstractAction {
    public RedoAction() {
        super("Redo");
        setEnabled(false);
    }
    public void actionPerformed(ActionEvent evt) {
        try {
            if(undoManager.canRedo()) undoManager.redo();
        } catch (CannotRedoException ex) {
            System.out.println("Unable to redo: " + ex);
            ex.printStackTrace();
        }
        update();
        undoAction.update();
    }
    protected void update() {
        if(undoManager.canRedo()) {
            setEnabled(true);
            putValue(Action.NAME, undoManager.getRedoPresentationName());
        }
        else {
            setEnabled(false);
            putValue(Action.NAME, "Redo");
        }
    }
    protected void perform() {
        if (isEnabled()) actionPerformed(null);
    }    
} // End inner class
        
} // End outer class
