/*
 * $Id: ReturnToDeck.java 6001 2009-09-04 23:15:06Z 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.counters;

import java.awt.Component;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;

import VASSAL.build.GameModule;
import VASSAL.build.module.Map;
import VASSAL.build.module.documentation.HelpFile;
import VASSAL.build.module.map.DrawPile;
import VASSAL.command.Command;
import VASSAL.configure.HotKeyConfigurer;
import VASSAL.configure.StringConfigurer;
import VASSAL.i18n.PieceI18nData;
import VASSAL.i18n.Resources;
import VASSAL.i18n.TranslatablePiece;
import VASSAL.tools.ScrollPane;
import VASSAL.tools.SequenceEncoder;
import VASSAL.tools.UniqueIdManager;

/**
 * GamePiece trait that returns a piece to a {@link DrawPile}
 */
public class ReturnToDeck extends Decorator implements TranslatablePiece {
  public static final String ID = "return;";
  protected String deckId;
  protected String returnCommand;
  protected String selectDeckPrompt="Select destination";
  protected KeyStroke returnKey;
  protected DrawPile deck;

  protected KeyCommand[] commands;
  protected KeyCommand myCommand;

  public ReturnToDeck() {
    this(ID + "Return to Deck;R;", null);
  }

  public ReturnToDeck(String type, GamePiece inner) {
    mySetType(type);
    setInner(inner);
  }

  protected KeyCommand[] myGetKeyCommands() {
    if (commands == null) {
      myCommand = new KeyCommand(returnCommand, returnKey, Decorator.getOutermost(this), this);
      if (returnCommand.length() > 0 && returnKey != null) {
        commands =
            new KeyCommand[]{myCommand};
      }
      else {
        commands = new KeyCommand[0];
      }
    }
    return commands;
  }

  public String myGetState() {
    return "";
  }

  public void mySetType(String s) {
    s = s.substring(ID.length());
    SequenceEncoder.Decoder st = new SequenceEncoder.Decoder(s, ';');
    returnCommand = st.nextToken();
    returnKey = st.nextKeyStroke(null);
    deckId = st.nextToken("");
    selectDeckPrompt = st.nextToken(selectDeckPrompt);
  }

  public String myGetType() {
    SequenceEncoder se = new SequenceEncoder(';');
    return ID + se.append(returnCommand).append(returnKey).append(deckId).append(selectDeckPrompt).getValue();
  }

  public Command myKeyEvent(KeyStroke stroke) {
    myGetKeyCommands();
    Command comm = null;
    if (myCommand.matches(stroke)) {
      DrawPile pile = deck;
      if (pile == null || deckId.length() == 0)
        pile = findDeck();
      if (pile == null)
        return null;
      final Map preMap = getMap();
      final Point prePos = getPosition();
      comm = pile.addToContents(Decorator.getOutermost(this));
      // Apply Auto-move key if the piece has moved
      Map m = pile.getMap();
      if (m != null && m.getMoveKey() != null && (m != preMap || !getPosition().equals(prePos))) {
        comm.append(Decorator.getOutermost(this).keyEvent(m.getMoveKey()));
      }
      pile.getMap().repaint();
    }
    return comm;
  }

  public Rectangle boundingBox() {
    return piece.boundingBox();
  }

  public void draw(Graphics g, int x, int y, Component obs, double zoom) {
    piece.draw(g, x, y, obs, zoom);
  }

  public String getName() {
    return piece.getName();
  }

  public Shape getShape() {
    return piece.getShape();
  }

  private DrawPile findDeck() {
    DrawPile pile = null;
    if (deckId.length() > 0)
      pile = DrawPile.findDrawPile(deckId);
    if (pile == null)
      return promptForDrawPile();
    // cache
    deck = pile;
    return pile;
  }
  
  private DrawPile promptForDrawPile() {
    final JDialog d = new JDialog(GameModule.getGameModule().getFrame(), true);
    d.setTitle(Decorator.getInnermost(this).getName()); //$NON-NLS-1$
    d.setLayout(new BoxLayout(d.getContentPane(), BoxLayout.Y_AXIS));
    
    class AvailableDeck {
      private DrawPile pile;

      public AvailableDeck(DrawPile pile) {
        this.pile = pile;
      }

      public String toString() {
        return pile.getConfigureName();
      }
    }
    
    final List<DrawPile> piles =
      GameModule.getGameModule().getAllDescendantComponentsOf(DrawPile.class);

    if (piles.size() == 0) {
      throw new IllegalStateException("No decks in module.");
    }
    
    final AvailableDeck[] decks = new AvailableDeck[piles.size()];
    int i = 0;
    for (DrawPile p : piles)
      decks[i++] = new AvailableDeck(p);

    final JList list = new JList(decks);
    list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    JLabel prompt = new JLabel(selectDeckPrompt);
    prompt.setAlignmentX(0.5f);
    d.add(prompt); //$NON-NLS-1$
    d.add(new ScrollPane(list));
    Box box = Box.createHorizontalBox();
    box.setAlignmentX(0.5F);
    JButton b = new JButton(Resources.getString(Resources.OK));
    b.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        AvailableDeck selection = (AvailableDeck) list.getSelectedValue();
        if (selection != null)
          deck = selection.pile;
        d.dispose();
      }
    });
    box.add(b);
    b = new JButton(Resources.getString(Resources.CANCEL));
    b.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        d.dispose();
      }
    });
    box.add(b);
    d.add(box);
    d.pack();
    d.setLocationRelativeTo(d.getOwner());
    d.setVisible(true);
    // don't cache -- ask again next time
    DrawPile pile = deck;
    deck = null;
    return pile;
  }

  public void mySetState(String newState) {
  }

  public PieceEditor getEditor() {
    return new Ed(this);
  }

  public String getDescription() {
    String d = "Return to Deck";
    if (deck != null) {
      findDeck();
      if (deck != null) {
        d += " - " + deck.getConfigureName();
      }
    }
    return d;
  }

  public HelpFile getHelpFile() {
    return HelpFile.getReferenceManualPage("ReturnToDeck.htm");
  }

  public PieceI18nData getI18nData() {
    return getI18nData(returnCommand, "Return to Deck command");
  }
  
  private static class Ed implements PieceEditor {
    private StringConfigurer menuName;
    private HotKeyConfigurer menuKey;
    private JPanel controls;
    private String deckId;
    private final JTextField tf = new JTextField(12);
    private StringConfigurer promptText;
    private JCheckBox prompt;

    public Ed(ReturnToDeck p) {
      controls = new JPanel();
      controls.setLayout(new BoxLayout(controls, BoxLayout.Y_AXIS));
      menuName = new StringConfigurer(null, "Menu Text:  ", p.returnCommand);
      controls.add(menuName.getControls());
      menuKey = new HotKeyConfigurer(null,"Keyboard Command:  ",p.returnKey);
      deckId = p.deckId;
      controls.add(menuKey.getControls());
      JButton select = new JButton("Select Deck");
      tf.setEditable(false);
      updateDeckName();
      select.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          VASSAL.configure.ChooseComponentDialog d = new VASSAL.configure.ChooseComponentDialog((Frame) SwingUtilities.getAncestorOfClass(Frame.class, controls), DrawPile.class);
          d.setTitle("Select Deck");
          d.setVisible(true);
          if (d.getTarget() != null) {
            deckId = UniqueIdManager.getIdentifier((UniqueIdManager.Identifyable)d.getTarget());
            updateDeckName();
          }
        }
      });
      final Box box = Box.createHorizontalBox();
      box.add(select);
      box.add(tf);
      controls.add(box);
      promptText = new StringConfigurer(null,"Prompt for destination deck:  ",p.selectDeckPrompt);
      prompt = new JCheckBox("Choose destination deck at game time");
      controls.add(prompt);
      controls.add(promptText.getControls());
      promptText.getControls().setVisible(p.deckId == null || p.deckId.length() == 0);
      box.setVisible(p.deckId != null && p.deckId.length() > 0);
      prompt.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          box.setVisible(!prompt.isSelected());
          promptText.getControls().setVisible(prompt.isSelected());
        }
      });
      prompt.setSelected(p.deckId == null || p.deckId.length() == 0);
    }

    private void updateDeckName() {
      DrawPile p = DrawPile.findDrawPile(deckId);
      tf.setText(p != null ? p.getConfigureName() : "<none>");
    }

    public Component getControls() {
      return controls;
    }

    public String getState() {
      return "";
    }

    public String getType() {
      SequenceEncoder se = new SequenceEncoder(';');
      return ID + se.append(menuName.getValueString()).append((KeyStroke)menuKey.getValue()).append(prompt.isSelected() ? "" : deckId).append(promptText.getValueString()).getValue();
    }
  }
}
