/*
 * $Id: PrivateMap.java 4560 2008-11-28 15:40:49Z 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.
 */
/*
 * Created by IntelliJ IDEA.
 * User: rkinney
 * Date: Jun 11, 2002
 * Time: 9:30:01 PM
 * To change template for new class use
 * Code Style | Class Templates options (Tools | IDE Options).
 */
package VASSAL.build.module;

import java.awt.Window;
import java.awt.dnd.DropTarget;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JScrollPane;
import javax.swing.KeyStroke;
import javax.swing.WindowConstants;
import VASSAL.build.Buildable;
import VASSAL.build.GameModule;
import VASSAL.build.module.documentation.HelpFile;
import VASSAL.build.module.map.boardPicker.Board;
import VASSAL.configure.ConfigureTree;
import VASSAL.configure.StringArrayConfigurer;
import VASSAL.configure.ValidationReport;
import VASSAL.configure.ValidityChecker;
import VASSAL.tools.AdjustableSpeedScrollPane;
import VASSAL.tools.menu.MenuManager;

/**
 * A Map that may be configured to be visible only a particular side.
 * If visible to all, the map will respond to key/mouse events
 * only from the player playing the assigned side
 */
public class PrivateMap extends Map {
  protected String[] owners = new String[0];
  protected boolean visibleToAll;
  protected Map surrogate;

  public static final String VISIBLE = "visible"; //$NON-NLS-1$
  public static final String SIDE = "side"; //$NON-NLS-1$
  public static final String USE_BOARDS = "useBoards"; //$NON-NLS-1$

  public String[] getAttributeNames() {
    String[] s1 = new String[]{SIDE, VISIBLE, USE_BOARDS};
    String[] s2 = super.getAttributeNames();
    String[] s = new String[s1.length + s2.length];
    System.arraycopy(s1, 0, s, 0, s1.length);
    System.arraycopy(s2, 0, s, s1.length, s2.length);
    return s;
  }

  public String[] getAttributeDescriptions() {
    String[] s1 = new String[]{"Belongs to side", "Visible to all players?", "Use same boards as this map:  "}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
    String[] s2 = super.getAttributeDescriptions();
    String[] s = new String[s1.length + s2.length];
    System.arraycopy(s1, 0, s, 0, s1.length);
    System.arraycopy(s2, 0, s, s1.length, s2.length);
    return s;
  }

  public Class<?>[] getAttributeTypes() {
    final Class<?>[] c1 = new Class<?>[]{
      String[].class,
      Boolean.class,
      String.class
    };
    final Class<?>[] c2 = super.getAttributeTypes();
    final Class<?>[] c = new Class<?>[c1.length + c2.length];
    System.arraycopy(c1, 0, c, 0, c1.length);
    System.arraycopy(c2, 0, c, c1.length, c2.length);
    return c;
  }

  public void setAttribute(String key, Object value) {
    if (VISIBLE.equals(key)) {
      if (value instanceof String) {
        value = Boolean.valueOf((String) value);
      }
      visibleToAll = ((Boolean) value).booleanValue();
    }
    else if (SIDE.equals(key)) {
      if (value instanceof String) {
        value = StringArrayConfigurer.stringToArray((String) value);
      }
      owners = (String[]) value;
    }
    else if (USE_BOARDS.equals(key)) {
      for (Map m : Map.getMapList()) {
        if (m.getMapName().equals(value)) {
          surrogate = m;
          break;
        }
      }
    }
    else {
      super.setAttribute(key, value);
    }
  }

  public String getAttributeValueString(String key) {
    if (VISIBLE.equals(key)) {
      return String.valueOf(visibleToAll);
    }
    else if (SIDE.equals(key)) {
      return StringArrayConfigurer.arrayToString(owners);
    }
    else if (USE_BOARDS.equals(key)) {
      return surrogate == null ? null :
          surrogate.getMapName();
    }
    else {
      return super.getAttributeValueString(key);
    }
  }
  
  public JComponent getView() {
    if (theMap == null) {
      theMap = new View(this);
      scroll = new AdjustableSpeedScrollPane(
            theMap,
            JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
            JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
      scroll.unregisterKeyboardAction(KeyStroke.getKeyStroke(
            KeyEvent.VK_PAGE_DOWN, 0));
      scroll.unregisterKeyboardAction(KeyStroke.getKeyStroke(
            KeyEvent.VK_PAGE_UP, 0));

      layeredPane.setLayout(new InsetLayout(layeredPane, scroll));
      layeredPane.add(scroll, JLayeredPane.DEFAULT_LAYER);
    }
    return theMap;
  }

  protected Window createParentFrame() {
    if (GlobalOptions.getInstance().isUseSingleWindow()) {
      final JDialog d = new JDialog(GameModule.getGameModule().getFrame()) {
        private static final long serialVersionUID = 1L;

        @Override
        public void setVisible(boolean show) {
          super.setVisible(show && 
            (visibleToAll || isAccessibleTo(PlayerRoster.getMySide())));
        }
      };

      d.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
      d.setTitle(getDefaultWindowTitle());
      return d;
    }
    else {
      final JFrame d = new JFrame() {
        private static final long serialVersionUID = 1L;

        @Override
        public void setVisible(boolean show) {
          super.setVisible(show &&
            (visibleToAll || isAccessibleTo(PlayerRoster.getMySide())));
        }
      };

      d.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
      d.setTitle(getDefaultWindowTitle());
      d.setJMenuBar(MenuManager.getInstance().getMenuBarFor(d));
      return d;
    }
  }

  public void sideChanged(String oldSide, String newSide) {
    super.sideChanged(oldSide, newSide);
    ((View)getView()).disableListeners();
    if (isAccessibleTo(newSide)) {
      ((View)getView()).enableListeners();
    }
    launchButton.setEnabled(isVisibleTo(PlayerRoster.getMySide()));
  }

  public boolean shouldDockIntoMainWindow() {
    return false;
  }

  /** Return true if the player playing the given side can access this map
   * @see PlayerRoster
   */
  public boolean isAccessibleTo(String playerSide) {
    for (int i = 0; i < owners.length; ++i) {
      if (owners[i].equals(playerSide)) {
        return true;
      }
    }
    return false;
  }

  public boolean isVisibleTo(String playerSide) {
    return (visibleToAll || isAccessibleTo(playerSide));
  }
  
  public void setup(boolean show) {
    super.setup(show);
    if (!show) {
      ((View) theMap).disableListeners();
    }
    else if (isAccessibleTo(PlayerRoster.getMySide())) {
      ((View) theMap).enableListeners();
    }
    launchButton.setEnabled(isVisibleTo(PlayerRoster.getMySide()));
  }

  @Override
  public void setBoards(Collection<Board> c) {
    if (surrogate != null) {
      c = surrogate.getBoards();
      edgeBuffer = surrogate.getEdgeBuffer();
    }
    super.setBoards(c);
  }

  /** @deprecated Use {@link #setBoards(Collection<Board>)} instead. */
  @SuppressWarnings("unchecked")
  @Override
  @Deprecated
  public void setBoards(Enumeration boardList) {
    if (surrogate != null) {
      boardList = surrogate.getAllBoards();
      edgeBuffer = surrogate.getEdgeBuffer();
    }
    super.setBoards(boardList);
  }

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

  public HelpFile getHelpFile() {
    return HelpFile.getReferenceManualPage("PrivateWindow.htm"); //$NON-NLS-1$
  }

  public void build(org.w3c.dom.Element el) {
    validator = new ValidityChecker() {
      public void validate(Buildable target, ValidationReport report) {
        if (!PlayerRoster.isActive()) {
          report.addWarning("Must add "+ConfigureTree.getConfigureName(PlayerRoster.class)
                            +" in order to use "+ConfigureTree.getConfigureName(getClass()));
        }
      }
    };
    surrogate = null;
    super.build(el);
  }

  public static class View extends Map.View {
    private static final long serialVersionUID = 1L;

    private boolean listenersActive;
    private List<KeyListener> keyListeners = new ArrayList<KeyListener>();
    private List<MouseListener> mouseListeners =
      new ArrayList<MouseListener>();
    private List<MouseMotionListener> mouseMotionListeners =
      new ArrayList<MouseMotionListener>();
    private DropTarget dropTarget;

    public View(PrivateMap m) {
      super(m);
    }

    public synchronized void setDropTarget(DropTarget dt) {
      if (dt != null) {
        dropTarget = dt;
      }
    }

    public synchronized void addKeyListener(KeyListener l) {
      if (listenersActive) {
        super.addKeyListener(l);
      }
      else {
        keyListeners.add(l);
      }
    }

    public synchronized void addMouseListener(MouseListener l) {
      if (listenersActive) {
        super.addMouseListener(l);
      }
      else {
        mouseListeners.add(l);
      }
    }

    public synchronized void addMouseMotionListener(MouseMotionListener l) {
      if (listenersActive) {
        super.addMouseMotionListener(l);
      }
      else {
        mouseMotionListeners.add(l);
      }
    }

    /**
     * Disable all keyboard and mouse listeners on this component
     */
    protected void disableListeners() {
      for (KeyListener l : keyListeners) {
        removeKeyListener(l);
      }
      for (MouseListener l : mouseListeners) {
        removeMouseListener(l);
      }
      for (MouseMotionListener l : mouseMotionListeners) {
        removeMouseMotionListener(l);
      }
      super.setDropTarget(null);
      listenersActive = false;
    }

    /**
     * Enable all keyboard and mouse listeners on this component
     */
    protected void enableListeners() {
      for (KeyListener l : keyListeners) {
        super.addKeyListener(l);
      }
      for (MouseListener l : mouseListeners) {
        super.addMouseListener(l);
      }
      for (MouseMotionListener l : mouseMotionListeners) {
        super.addMouseMotionListener(l);
      }
      super.setDropTarget(dropTarget);
      listenersActive = true;
    }
  }
}
