/*
 * $Id: CompoundPieceCollection.java 2893 2008-01-27 20:15:23Z uckelman $
 *
 * Copyright (c) 2004 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.util.ArrayList;
import java.util.Arrays;
import VASSAL.counters.Deck;
import VASSAL.counters.GamePiece;
import VASSAL.counters.Properties;
import VASSAL.counters.Stack;

/**
 * Base class for PieceCollection implementation that organize
 * pieces into distinct layers.  The layers are drawn in order of their index, i.e.
 * layer 0 is on the bottom.
 */
public abstract class CompoundPieceCollection implements PieceCollection {
  protected SimplePieceCollection[] layers;
  protected int bottomLayer = 0;
  protected boolean[] enabled;

  protected CompoundPieceCollection(int layerCount) {
    initLayers(layerCount);
  }

  protected void initLayers(int layerCount) {
    layers = new SimplePieceCollection[layerCount];
    enabled = new boolean[layerCount];
    for (int i=0;i<layers.length;++i) {
      layers[i] = new SimplePieceCollection();
      enabled[i] = true;
    }
  }

  public int getLayerForPiece(GamePiece p) {
    return 0;
  }
  
  public String getLayerNameForPiece(GamePiece p) {
    return "";
  }

  public int getLayerForName(String layerName) {
    return -1;
  }
  
  protected PieceCollection getCollectionForPiece(GamePiece p) {
    return layers[getLayerForPiece(p)];
  }

  public void add(GamePiece p) {
    getCollectionForPiece(p).add(p);
  }

  public void clear() {
    for (int i=0;i<layers.length;++i) {
      layers[i].clear();
    }
  }

  /*
   * Return pieces in layer order from the bottom up. Take into account
   * layer rotation and enabled state.
   */
  public GamePiece[] getPieces() {
    return getPieces(false);
  }
  
  protected GamePiece[] getPieces(boolean includeDisabled) {
    ArrayList<GamePiece> l = new ArrayList<GamePiece>();
    int layer = bottomLayer;
    for (int i = 0; i < layers.length; ++i) {
      if (includeDisabled || (!includeDisabled && enabled[layer])) {
        l.addAll(Arrays.asList(layers[layer].getPieces()));
      }
      layer++;
      if (layer >= layers.length) {
        layer = 0;
      }
    }
    return l.toArray(new GamePiece[l.size()]);
  }

  public GamePiece[] getAllPieces() {
      return getPieces(true);
  
  }
  
  public int indexOf(GamePiece p) {
    int layer = getLayerForPiece(p);
    int index = layers[layer].indexOf(p);
    if (index >= 0) {
      for (int i=0;i<layer-1;++i) {
        index += layers[i].getPieces().length;
      }
    }
    return index;
  }

  public void remove(GamePiece p) {
    getCollectionForPiece(p).remove(p);
  }

  public void moveToBack(GamePiece p) {
    getCollectionForPiece(p).moveToBack(p);
  }

  public void moveToFront(GamePiece p) {
    getCollectionForPiece(p).moveToFront(p);
  }

  public boolean canMerge(GamePiece p1, GamePiece p2) {
    boolean canMerge = false;
    if (p1 instanceof Deck
        || p2 instanceof Deck) {
      canMerge = true;
    }
    else if (p1 instanceof Stack) {
      if (p2 instanceof Stack) {
        canMerge = canStacksMerge((Stack) p1, (Stack) p2);
      }
      else {
        canMerge = canStackAndPieceMerge((Stack) p1, p2);
      }
    }
    else if (p2 instanceof Stack) {
      canMerge = canStackAndPieceMerge((Stack) p2, p1);
    }
    else {
      canMerge = canPiecesMerge(p1, p2);
    }
    return canMerge;
  }

  protected boolean canStacksMerge(Stack s1, Stack s2) {
    return canPiecesMerge(s1.topPiece(), s2.topPiece());
  }

  protected boolean canStackAndPieceMerge(Stack s, GamePiece p) {
    boolean canMerge = false;
    GamePiece top = s.topPiece();
    if (top != null) {
      canMerge = canPiecesMerge(top, p);
    }
    return canMerge;
  }

  protected boolean canPiecesMerge(GamePiece p1, GamePiece p2) {
    boolean canMerge = false;
    if (p1 != null
        && p2 != null) {
      canMerge = !Boolean.TRUE.equals(p1.getProperty(Properties.NO_STACK))
          && !Boolean.TRUE.equals(p2.getProperty(Properties.NO_STACK))
          && !Boolean.TRUE.equals(p1.getProperty(Properties.INVISIBLE_TO_ME))
          && !Boolean.TRUE.equals(p2.getProperty(Properties.INVISIBLE_TO_ME));
    }
    return canMerge;
  }

  public int getLayerCount() {
    return layers.length;
  }
  
  /*
   * Set a new bottom layer. Take care of wrapping around ends of layer list.
   */
  public void setBottomLayer(int layer) {
    bottomLayer = layer;
    if (bottomLayer < 0) bottomLayer = getLayerCount() - 1;
    if (bottomLayer >= getLayerCount()) bottomLayer = 0;
  }
  
  public int getBottomLayer() {
    return bottomLayer;
  }
  
  public int getTopLayer() {
    int layer = bottomLayer - 1;
    if (layer < 0) {
      layer = getLayerCount() - 1;
    }
    return layer;
  }
  
  /*
   * Rotate layers up or down, optionally skipping top layers not containing
   * counters.
   */
  public void rotate(boolean rotateUp, boolean skipNullLayers) {
    if (skipNullLayers) {
      for (int i = 0; i < layers.length; i++) {
        rotate(rotateUp);
        if (layers[getTopLayer()].getPieces().length > 0) {
          return;
        }
      }
    }
    else {
      rotate(rotateUp);
    }
  }
  
  /*
   * Rotate Layers up or down by 1 layer
   */
  public void rotate(boolean rotateUp) {
    if (rotateUp) {
      setBottomLayer(bottomLayer-1);
    }
    else {
      setBottomLayer(bottomLayer + 1);
    }
  }
  
  /*
   * Enable/Disable layers
   */
  public void setLayerEnabled(int layer, boolean b) {
    if (layer >= 0 && layer < layers.length) {
      enabled[layer] = b;
    }
  }
  
  public void toggleLayerEnabled(int layer) {
    if (layer >= 0 && layer < layers.length) {
      enabled[layer] = !enabled[layer];
    }
  }
  
  public void setLayerEnabled(String layer, boolean b) {
    setLayerEnabled(getLayerForName(layer), b);
  }
  
  public void toggleLayerEnabled(String layer) {
    toggleLayerEnabled(getLayerForName(layer));
  }
  
  /* 
   * Reset Layers to original state
   */
  public void reset() {
    setBottomLayer(0);
    for (int i = 0; i < layers.length; i++) {
      enabled[i] = true;
    }
  }
}
