import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;

import javax.swing.*;

import chooser.BindingChooser;
import chooser.Persistence;

/**
 * Small demo for the keybindings util library. This presents a frame whose
 * background changes via customized keystrokes.
 */
public class KeybindingDemo extends JPanel {
  private static final long serialVersionUID = 0;
  
  // string constants associated with actions (displayed by chooser)
  private static final String SET_BG_RED = "Set Red";
  private static final String SET_BG_BLUE = "Set Blue";
  private static final String SET_BG_YELLOW = "Set Yellow";
  private static final String SET_BG_GREEN = "Set Green";
  private static final String SET_BG_WHITE = "Set White";
  
  // information for persistence
  private static final File BINDINGS_FILE = new File("savedBindings.xml");
  private static final Persistence BINDINGS_FORMAT = Persistence.PROPERTIES_XML;
  
  public static void main(String[] args) {
    JFrame frame = new JFrame("Keybinding Demo");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(new KeybindingDemo());
    frame.pack();
    frame.setVisible(true);
  }
  
  private KeybindingDemo() {
    setPreferredSize(new Dimension(200, 100));
    setBackground(Color.WHITE);
    
    // loads initial bindings
    Map <KeyStroke, String> initialBindings;
    try {
      // parses the bindings using the xml format of java.util.Properties
      initialBindings = BINDINGS_FORMAT.load(new FileInputStream(BINDINGS_FILE));
    } catch (IOException exc) {
      // couldn't find the initial bindings
      System.out.println("Initial bindings not found: " + BINDINGS_FILE.getAbsolutePath());
      initialBindings = fabricateDefaults();
      
      // writes bindings so they can be loaded properly next time
      saveBindings(initialBindings);
    } catch (ParseException exc) {
      // couldn't read the initial bindings (corrupt file?)
      System.out.println(exc.getMessage());
      initialBindings = fabricateDefaults();
    }
    
    // loads bindings into this panel's input map
    setBindings(initialBindings);
    
    // generates action portion of the bindings (ie, what they do)
    ActionMap am = getActionMap();
    am.put(SET_BG_RED, new DemoAction(Color.RED));
    am.put(SET_BG_BLUE, new DemoAction(Color.BLUE));
    am.put(SET_BG_YELLOW, new DemoAction(Color.YELLOW));
    am.put(SET_BG_GREEN, new DemoAction(Color.GREEN));
    am.put(SET_BG_WHITE, new DemoAction(Color.WHITE));
    
    // adds a button to present a chooser for changing the keybindings
    JButton chooserButton = new JButton("Show Chooser");
    chooserButton.setFocusable(false); // prevents stealing focus from panel
    chooserButton.addActionListener(new ChooserPopup(initialBindings));
    add(chooserButton);
  }
  
  // resets the panel's input map to a given set of bindings
  private void setBindings(Map <KeyStroke, String> bindings) {
    InputMap im = getInputMap();
    im.clear();
    
    for (KeyStroke shortcut : bindings.keySet()) {
      im.put(shortcut, bindings.get(shortcut));
    }
  }
  
  // saves set of keybindings
  private void saveBindings(Map <KeyStroke, String> bindings) {
    try {
      BINDINGS_FORMAT.save(new FileOutputStream(BINDINGS_FILE), bindings);
    } catch (IOException exc) {
      System.out.println("Unable to save bindings to: " + BINDINGS_FILE.getAbsolutePath());
    }
  }
  
  /*
   * Usually you'll want the defaults to be loaded in a fail-safe fashion but
   * for this demo we'll just fabricate some bindings.
   */
  private static Map <KeyStroke, String> fabricateDefaults() {
    Map <KeyStroke, String> bindings = new HashMap <KeyStroke, String>();
    bindings.put(KeyStroke.getKeyStroke(KeyEvent.VK_R, 0), SET_BG_RED);
    bindings.put(KeyStroke.getKeyStroke(KeyEvent.VK_B, 0), SET_BG_BLUE);
    bindings.put(KeyStroke.getKeyStroke(KeyEvent.VK_Y, 0), SET_BG_YELLOW);
    bindings.put(KeyStroke.getKeyStroke(KeyEvent.VK_G, 0), SET_BG_GREEN);
    bindings.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), SET_BG_WHITE);
    return bindings;
  }
  
  // Simply sets the panel's background to a given color
  private class DemoAction extends AbstractAction {
    private static final long serialVersionUID = 0;
    private final Color bgColor;
    
    DemoAction(Color bgColor) {
      this.bgColor = bgColor;
    }
    
    public void actionPerformed(ActionEvent event) {
      KeybindingDemo.this.setBackground(this.bgColor);
    }
  }
  
  // Displays a dialog allowing user to customize keybindings
  private class ChooserPopup implements ActionListener {
    private Map <KeyStroke, String> previousBindings;
    
    ChooserPopup(Map <KeyStroke, String> initialBindings) {
      this.previousBindings = initialBindings;
    }
    
    public void actionPerformed(ActionEvent event) {
      Map <KeyStroke, String> output =
          BindingChooser.showDialog(KeybindingDemo.this, this.previousBindings);
      
      // output is null if canceled
      if (output != null) {
        setBindings(output);
        saveBindings(output);
        this.previousBindings = output;
      }
    }
  }
}

