/*
 * $Id: NotesWindow.java 4561 2008-11-28 15:57:51Z uckelman $
 *
 * Copyright (c) 2000-2003 by Rodney Kinney
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License (LGPL) as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, copies are available
 * at http://www.opensource.org.
 */
package VASSAL.build.module;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.KeyStroke;

import VASSAL.build.AbstractConfigurable;
import VASSAL.build.AutoConfigurable;
import VASSAL.build.Buildable;
import VASSAL.build.Configurable;
import VASSAL.build.GameModule;
import VASSAL.build.module.documentation.HelpFile;
import VASSAL.build.module.noteswindow.PrivateNotesController;
import VASSAL.build.module.noteswindow.SecretNotesController;
import VASSAL.command.Command;
import VASSAL.command.CommandEncoder;
import VASSAL.command.NullCommand;
import VASSAL.configure.Configurer;
import VASSAL.configure.ConfigurerFactory;
import VASSAL.configure.IconConfigurer;
import VASSAL.configure.TextConfigurer;
import VASSAL.i18n.Resources;
import VASSAL.tools.LaunchButton;

/**
 * This is a {@link GameComponent} that allows players to type and
 * save text notes during a game.  There is one set of shared public
 * notes, and each player has a set of private notes visible only to
 * him
 */
public class NotesWindow extends AbstractConfigurable
    implements GameComponent, CommandEncoder {

  protected JDialog frame;
  protected LaunchButton launch;
  protected TextConfigurer scenarioNotes;
  protected TextConfigurer publicNotes;
  protected PrivateNotesController privateNotes;
  protected SecretNotesController secretNotes;
  protected static final String SCENARIO_NOTE_COMMAND_PREFIX = "NOTES\t"; //$NON-NLS-1$
  protected static final String PUBLIC_NOTE_COMMAND_PREFIX = "PNOTES\t"; //$NON-NLS-1$

  public static final String HOT_KEY = "hotkey"; //$NON-NLS-1$
  public static final String ICON = "icon"; //$NON-NLS-1$
  public static final String BUTTON_TEXT = "buttonText"; //$NON-NLS-1$
  public static final String TOOLTIP = "tooltip"; //$NON-NLS-1$

  protected String lastSavedScenarioNotes;
  protected String lastSavedPublicNotes;

  public NotesWindow() {
    privateNotes = new PrivateNotesController();
    secretNotes = new SecretNotesController();
    frame = new NotesDialog();
    frame.setTitle(Resources.getString("Notes.notes")); //$NON-NLS-1$
    ActionListener al = new ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent e) {
        captureState();
        frame.setVisible(!frame.isShowing());
      }
    };
    launch = new LaunchButton(Resources.getString("Notes.notes"), TOOLTIP, BUTTON_TEXT, HOT_KEY, ICON, al); //$NON-NLS-1$
    launch.setAttribute(ICON, "/images/notes.gif"); //$NON-NLS-1$
    launch.setToolTipText(Resources.getString("Notes.notes")); //$NON-NLS-1$
    frame.pack();
    setup(false);
  }

  /**
   * Capture this object's state, to be restored if the user hits "Cancel"
   */
  protected void captureState() {
    lastSavedScenarioNotes = (String) scenarioNotes.getValue();
    lastSavedPublicNotes = (String) publicNotes.getValue();
    privateNotes.captureState();
    secretNotes.captureState();
  }

  public void cancel() {
    restoreState();
    privateNotes.restoreState();
    secretNotes.restoreState();
  }

  protected void restoreState() {
    scenarioNotes.setValue(lastSavedScenarioNotes);
    publicNotes.setValue(lastSavedPublicNotes);
  }

  protected void save() {
    Command c = new NullCommand();
    if (!lastSavedScenarioNotes.equals(scenarioNotes.getValue())) {
      c.append(new SetScenarioNote(scenarioNotes.getValueString()));
    }
    if (!lastSavedPublicNotes.equals(publicNotes.getValue())) {
      c.append(new SetPublicNote(publicNotes.getValueString()));
    }
    c.append(privateNotes.save());
    c.append(secretNotes.save());
    GameModule.getGameModule().sendAndLog(c);
  }

  protected class NotesDialog extends JDialog {

    private static final long serialVersionUID = 1L;
    
    protected NotesDialog() {
      super(GameModule.getGameModule().getFrame());
      initComponents();
      setLocationRelativeTo(getOwner());
    }

    protected void initComponents() {
      setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
      setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
      addWindowListener(new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
          cancel();
          setVisible(false);
        }
      });

      scenarioNotes = new TextConfigurer(null, null);
      publicNotes = new TextConfigurer(null, null);
      JTabbedPane tab = new JTabbedPane();
      add(tab);

      Box b = Box.createVerticalBox();
      b.add(new JLabel(Resources.getString("Notes.visible_to_all"))); //$NON-NLS-1$
      b.add(scenarioNotes.getControls());
      tab.addTab(Resources.getString("Notes.scenario"), b); //$NON-NLS-1$

      b = Box.createVerticalBox();
      b.add(new JLabel(Resources.getString("Notes.visible_to_all"))); //$NON-NLS-1$
      b.add(publicNotes.getControls());
      tab.addTab(Resources.getString("Notes.public"), b); //$NON-NLS-1$

      tab.addTab(Resources.getString("Notes.private"), privateNotes.getControls()); //$NON-NLS-1$

      tab.addTab(Resources.getString("Notes.delayed"), secretNotes.getControls()); //$NON-NLS-1$

      JPanel p = new JPanel();
      JButton saveButton = new JButton(Resources.getString(Resources.SAVE));
      p.add(saveButton);
      saveButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          save();
          setVisible(false);
        }
      });
      JButton cancelButton = new JButton(Resources.getString(Resources.CANCEL));
      cancelButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          cancel();
          setVisible(false);
        }
      });
      p.add(cancelButton);
      add(p);
    }
  }


  public HelpFile getHelpFile() {
    return HelpFile.getReferenceManualPage("GameModule.htm", "NotesWindow"); //$NON-NLS-1$ //$NON-NLS-2$
  }

  public String[] getAttributeNames() {
    return new String[] {BUTTON_TEXT, TOOLTIP, ICON, HOT_KEY};
  }

  public void setAttribute(String name, Object value) {
    launch.setAttribute(name, value);
  }

  public String getAttributeValueString(String name) {
    return launch.getAttributeValueString(name);
  }

  public String encode(Command c) {
    String s = null;
    if (c instanceof SetScenarioNote) {
      s = SCENARIO_NOTE_COMMAND_PREFIX + ((SetScenarioNote) c).msg;
    }
    else if (c instanceof SetPublicNote) {
      s = PUBLIC_NOTE_COMMAND_PREFIX + ((SetPublicNote) c).msg;
    }
    else {
      s = privateNotes.encode(c);
      if (s == null) {
        s = secretNotes.encode(c);
      }
    }
    return s;
  }

  public Command decode(String command) {
    Command comm;
    if (command.startsWith(SCENARIO_NOTE_COMMAND_PREFIX)) {
      comm = new SetScenarioNote(command.substring(SCENARIO_NOTE_COMMAND_PREFIX.length()));
    }
    else if (command.startsWith(PUBLIC_NOTE_COMMAND_PREFIX)) {
      comm = new SetPublicNote(command.substring(PUBLIC_NOTE_COMMAND_PREFIX.length()));
    }
    else {
      comm = privateNotes.decode(command);
      if (comm == null) {
        comm = secretNotes.decode(command);
      }
    }
    return comm;
  }

  public String[] getAttributeDescriptions() {
    return new String[] {
      "Button text:  ",
      "Tooltip text:  ",
      "Button Icon:  ", 
      "Hotkey:  "
    };
  }

  public Class<?>[] getAttributeTypes() {
    return new Class<?>[] {
      String.class,
      String.class,
      IconConfig.class,
      KeyStroke.class
    };
  }

  public static class IconConfig implements ConfigurerFactory {
    public Configurer getConfigurer(AutoConfigurable c, String key, String name) {
      return new IconConfigurer(key, name, ((NotesWindow) c).launch.getAttributeValueString(ICON));
    }
  }

  public Configurable[] getConfigureComponents() {
    return new Configurable[0];
  }

  public Class<?>[] getAllowableConfigureComponents() {
    return new Class<?>[0];
  }

  public static String getConfigureTypeName() {
    return "Notes Window";
  }

  /**
   * Expects to be added to a {@link VASSAL.build.GameModule}.  Adds a button to
   * the controls window toolbar to show the window containing the
   * notes */
  public void addTo(Buildable b) {
    GameModule.getGameModule().getToolBar().add(launch);
    launch.setAlignmentY(0.0F);
    GameModule.getGameModule().addCommandEncoder(this);
    GameModule.getGameModule().getGameState().addGameComponent(this);
    GameModule.getGameModule().addCommandEncoder(privateNotes);
    GameModule.getGameModule().getGameState().addGameComponent(privateNotes);
    GameModule.getGameModule().addCommandEncoder(secretNotes);
    GameModule.getGameModule().getGameState().addGameComponent(secretNotes);
  }

  public void removeFrom(Buildable b) {
    GameModule.getGameModule().getToolBar().remove(launch);
    GameModule.getGameModule().removeCommandEncoder(this);
    GameModule.getGameModule().getGameState().removeGameComponent(this);
    GameModule.getGameModule().removeCommandEncoder(privateNotes);
    GameModule.getGameModule().getGameState().removeGameComponent(privateNotes);
    GameModule.getGameModule().removeCommandEncoder(secretNotes);
    GameModule.getGameModule().getGameState().removeGameComponent(secretNotes);
  }

  public void setup(boolean show) {
    launch.setEnabled(show);
    if (!show) {
      scenarioNotes.setValue(""); //$NON-NLS-1$
      publicNotes.setValue(""); //$NON-NLS-1$
    }
  }

  public Command getRestoreCommand() {
    Command c = new SetScenarioNote(scenarioNotes.getValueString());
    c.append(new SetPublicNote(publicNotes.getValueString()));
    c.append(privateNotes.getRestoreCommand());
    c.append(secretNotes.getRestoreCommand());
    return c;
  }

  protected class SetScenarioNote extends Command {
    protected String msg;

    protected SetScenarioNote(String s) {
      msg = s;
    }

    protected void executeCommand() {
      scenarioNotes.setValue(msg);
    }

    protected Command myUndoCommand() {
      return null;
    }
  }
  
  protected class SetPublicNote extends Command {
    protected String msg;

    protected SetPublicNote(String s) {
      msg = s;
    }

    protected void executeCommand() {
      publicNotes.setValue(msg);
    }

    protected Command myUndoCommand() {
      return null;
    }
  }
}
