/*
 * $Id: Restricted.java 4507 2008-11-19 04:42:48Z rodneykinney $
 *
 * 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.
 */
/*
 * Created by IntelliJ IDEA.
 * User: rkinney
 * Date: Jun 13, 2002
 * Time: 9:52:40 PM
 * To change template for new class use
 * Code Style | Class Templates options (Tools | IDE Options).
 */
package VASSAL.counters;

import java.awt.Component;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Shape;
import java.util.Iterator;

import javax.swing.Box;
import javax.swing.JComponent;
import javax.swing.KeyStroke;

import VASSAL.build.GameModule;
import VASSAL.build.module.Map;
import VASSAL.build.module.PlayerRoster;
import VASSAL.build.module.documentation.HelpFile;
import VASSAL.command.ChangeTracker;
import VASSAL.command.Command;
import VASSAL.command.NullCommand;
import VASSAL.configure.BooleanConfigurer;
import VASSAL.configure.StringArrayConfigurer;
import VASSAL.tools.SequenceEncoder;

/**
 * A GamePiece with the Restricted trait can only be manipulated by the player playing a specific side
 */
public class Restricted extends Decorator implements EditablePiece {
  public static final String ID = "restrict;";
  private String[] side;
  private boolean restrictByPlayer;
  private String owningPlayer="";
  private static PlayerRoster.SideChangeListener handleRetirement;

  public Restricted() {
    this(ID, null);
  }

  public Restricted(String type, GamePiece p) {
    setInner(p);
    mySetType(type);
    if (handleRetirement == null) {
      handleRetirement = new RetirementHandler();
      PlayerRoster.addSideChangeListener(handleRetirement);
    }
  }

  public String getDescription() {
    return "Restricted Access";
  }

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

  public void mySetType(String type) {
    type = type.substring(ID.length());
    SequenceEncoder.Decoder st = new SequenceEncoder.Decoder(type,';');
    side = st.nextStringArray(0);
    restrictByPlayer = st.nextBoolean(false);
  }

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

  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();
  }

  protected KeyCommand[] myGetKeyCommands() {
    return new KeyCommand[0];
  }

  public boolean isRestricted() {
    boolean restricted = false;
    if (restrictByPlayer) {
      restricted = owningPlayer.length() > 0 && !GameModule.getUserId().equals(owningPlayer);
    }
    if ((restricted || !restrictByPlayer) 
        && PlayerRoster.isActive()
        && GameModule.getGameModule().getGameState().isGameStarted()) {
      restricted = true;
      for (int i = 0; i < side.length; ++i) {
        if (side[i].equals(PlayerRoster.getMySide())) {
          restricted = false;
          break;
        }
      }
    }
    return restricted;
  }
  
/*  @Override
  public void setMap(Map m) {
    if (m != null && restrictByPlayer && owningPlayer.length() == 0) {
      owningPlayer = GameModule.getUserId();
    }
    super.setMap(m);
  }
*/  
  @Override
  public void setProperty(Object key, Object val) {
    if (Properties.SELECTED.equals(key) && Boolean.TRUE.equals(val) && restrictByPlayer && owningPlayer.length() == 0) {
      if (getMap() != null) {
        owningPlayer = GameModule.getUserId();
      }
      else {
        System.err.println("Selected, but map == null");
      }
    }
    super.setProperty(key, val);
  }

  protected KeyCommand[] getKeyCommands() {
    if (!isRestricted()) {
      return super.getKeyCommands();
    }
    else {
      return new KeyCommand[0];
    }
  }

  @Override
  public Object getLocalizedProperty(Object key) {
    if (Properties.RESTRICTED.equals(key)) {
      return Boolean.valueOf(isRestricted());
    }
    else {
      return super.getLocalizedProperty(key);
    }    
  }
  
  public Object getProperty(Object key) {
    if (Properties.RESTRICTED.equals(key)) {
      return Boolean.valueOf(isRestricted());
    }
    else {
      return super.getProperty(key);
    }
  }

  public String myGetState() {
    return owningPlayer;
  }

  public String myGetType() {
    return ID + new SequenceEncoder(';').append(side).append(restrictByPlayer).getValue();
  }

  public Command myKeyEvent(KeyStroke stroke) {
    return null;
  }

  public Command keyEvent(KeyStroke stroke) {
    if (!isRestricted()) {
      return super.keyEvent(stroke);
    }
    else {
      return null;
    }
  }

  public void mySetState(String newState) {
    owningPlayer = newState;
  }

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

  public static class Ed implements PieceEditor {
    private BooleanConfigurer byPlayer;
    private StringArrayConfigurer config;
    private Box box;

    public Ed(Restricted r) {
      byPlayer = new BooleanConfigurer(null,"Also belongs to initially-placing player",r.restrictByPlayer);
      config = new StringArrayConfigurer(null, "Belongs to side", r.side);
      box = Box.createVerticalBox();
      ((JComponent)byPlayer.getControls()).setAlignmentX(Box.RIGHT_ALIGNMENT);
      box.add(config.getControls());
      box.add(byPlayer.getControls());
    }

    public Component getControls() {
      return box;
    }

    public String getState() {
      return "";
    }

    public String getType() {
      return ID + new SequenceEncoder(';').append(config.getValueString()).append(byPlayer.booleanValue()).getValue();
    }
  }
  /**
   * When a player changes sides to become an observer, relinquish ownership of all pieces
   * @author rodneykinney
   *
   */
  private static class RetirementHandler implements PlayerRoster.SideChangeListener, PieceVisitor {

    public void sideChanged(String oldSide, String newSide) {
      if (newSide == null) {
        PieceVisitorDispatcher d = new PieceVisitorDispatcher(this);
        Command c = new NullCommand();
        for(Map m : GameModule.getGameModule().getComponentsOf(Map.class)) {
          for (GamePiece piece : m.getPieces()) {
            c = c.append((Command)d.accept(piece));
          }
        }
        GameModule.getGameModule().sendAndLog(c);
      }
    }

    public Object visitDefault(GamePiece p) {
      Restricted r = (Restricted)Decorator.getDecorator(p, Restricted.class);
      if (r != null
          && r.restrictByPlayer
          && GameModule.getUserId().equals(r.owningPlayer)) {
        
        ChangeTracker t = new ChangeTracker(p);
        r.owningPlayer = "";
        return t.getChangeCommand();
      }
      return null;
    }

    public Object visitStack(Stack s) {
      Command c = new NullCommand();
      for (Iterator<GamePiece> it = s.getPiecesIterator(); it.hasNext();) {
        c = c.append((Command)visitDefault(it.next()));
      }
      return c;
    }
    
  }
}
