/*
 * $Id: DeckGlobalKeyCommand.java 5158 2009-02-24 15:06:25Z uckelman $
 *
 * Copyright (c) 2005 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.map;

import java.awt.event.ActionEvent;

import javax.swing.KeyStroke;

import VASSAL.build.Buildable;
import VASSAL.build.GameModule;
import VASSAL.build.module.Chatter;
import VASSAL.build.module.Map;
import VASSAL.build.module.properties.PropertySource;
import VASSAL.command.Command;
import VASSAL.command.NullCommand;
import VASSAL.configure.PropertyExpression;
import VASSAL.counters.Deck;
import VASSAL.counters.DeckVisitorDispatcher;
import VASSAL.counters.GlobalCommand;
import VASSAL.counters.KeyCommand;
import VASSAL.counters.PieceFilter;
import VASSAL.tools.RecursionLimiter.Loopable;
import VASSAL.tools.SequenceEncoder;

/**
 * This version of {@link MassKeyCommand} is added directly to a
 * {@link VASSAL.build.GameModule} and applies to all maps
 */
public class DeckGlobalKeyCommand extends MassKeyCommand {

  public DeckGlobalKeyCommand() {
    globalCommand = new DeckGlobalCommand(this);
    setConfigureName("");
  }
  
  public DeckGlobalKeyCommand(String code) {
    this();
    decode(code);
  }
  
  public static String getConfigureTypeName() {
    return "Deck Global Key Command";
  }
  
  public void addTo(Buildable parent) {
    if (parent instanceof Map) {
      map = (Map) parent;
    }
    if (parent instanceof PropertySource) {
      propertySource = (PropertySource) parent;
    }
    ((DrawPile) parent).addGlobalKeyCommand(this);
  }
  
  public void removeFrom(Buildable parent) {
    ((DrawPile) parent).removeGlobalKeyCommand(this);
  }
  
  public KeyCommand getKeyCommand(Deck deck) {
    return new DeckKeyCommand(getLocalizedConfigureName(), null, deck);
  }
  
  class DeckKeyCommand extends KeyCommand {
    private static final long serialVersionUID = 1L;
    protected Deck deck;
    public DeckKeyCommand(String name, KeyStroke key, Deck deck) {      
      super(name, key, deck);
      this.deck = deck;
    }
    public void actionPerformed(ActionEvent e) {
      apply(deck);
    }
  }
  
  /**
   * Since we also limit application of a Deck Global Key command to a specified number of pieces in the
   * Deck, a null match expression should match all pieces, not reject them all. 
   */
  public PieceFilter getFilter() {
    if (propertiesFilter == null || propertiesFilter.getExpression() == null || propertiesFilter.getExpression().length() == 0) {
      return null;
    }
    return super.getFilter();
  }
  
  public void apply(Deck deck) {
    GameModule.getGameModule().sendAndLog(((DeckGlobalCommand) globalCommand).apply(deck, getFilter()));
  }
  
  public String encode() {
    SequenceEncoder se = new SequenceEncoder('|');
    se.append(getConfigureName())
      .append(getAttributeValueString(KEY_COMMAND))
      .append(getAttributeValueString(PROPERTIES_FILTER))
      .append(getAttributeValueString(DECK_COUNT))
      .append(getAttributeValueString(REPORT_FORMAT))
      .append(getLocalizedConfigureName());
    return se.getValue();
  }
  
  public void decode(String s) {
    SequenceEncoder.Decoder sd = new SequenceEncoder.Decoder(s, '|');
    setConfigureName(sd.nextToken(""));
    setAttribute(KEY_COMMAND, sd.nextKeyStroke('A'));
    setAttribute(PROPERTIES_FILTER, sd.nextToken(null));
    setAttribute(DECK_COUNT, sd.nextInt(0));
    setAttribute(REPORT_FORMAT, sd.nextToken(""));
    localizedName = sd.nextToken(getConfigureName());
  }
  
  public String[] getAttributeDescriptions() {
    return new String[]{
      "Menu Command:  ",
      "Global Command:  ",
      "Matching properties:  ",
      "Affects:  ",
      "Report Format:  "
    };
  }

  public String[] getAttributeNames() {
    return new String[]{
      NAME,
      KEY_COMMAND,
      PROPERTIES_FILTER,
      DECK_COUNT,
      REPORT_FORMAT
    };
  }


  public Class<?>[] getAttributeTypes() {
    return new Class<?>[]{
      String.class,
      KeyStroke.class,
      PropertyExpression.class,
      DeckPolicyConfig2.class,
      ReportFormatConfig.class
    };
  }
  
  public static class DeckPolicyConfig2 extends DeckPolicyConfig {
    public DeckPolicyConfig2() {
      super();
      typeConfig.setValidValues(new String[]{ALL, FIXED});
      prompt.setText("Affects:  ");
    }
  }
  
  public static class DeckGlobalCommand extends GlobalCommand {
    
    public DeckGlobalCommand(Loopable l) {
      super(l);
    }

    public Command apply(Deck d, PieceFilter filter) {
      String reportText = reportFormat.getText();
      Command c;
      if (reportText.length() > 0) {
        c = new Chatter.DisplayText(GameModule.getGameModule().getChatter(), "*" + reportText);
        c.execute();
      }
      else {
        c = new NullCommand();
      }
      Visitor visitor = new Visitor(c, filter, keyStroke);
      DeckVisitorDispatcher dispatcher = new DeckVisitorDispatcher(visitor);
     
      dispatcher.accept(d);
      visitor.getTracker().repaint();

      c = visitor.getCommand();
      return c;
    }
  }
    
}
