/*
 *
 * Copyright (c) 2004-2008 by Rodney Kinney, Joel Uckelman
 *
 * 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 VASSAL.build.GameModule;
import VASSAL.build.module.Map;
import VASSAL.command.AddPiece;
import VASSAL.tools.ReflectionUtils;

/**
 * Utility class for cloning {@link GamePiece}s
 */
public class PieceCloner {
  private static final PieceCloner instance = new PieceCloner();

  // For use by subclasses
  protected PieceCloner() {

  }

  public static PieceCloner getInstance() {
    return instance;
  }

  /**
   * Create a new instance that is a clone of the given piece.
   *
   * @return the new instance
   */
  public GamePiece clonePiece(GamePiece piece) {
    GamePiece clone = null;
    if (piece instanceof BasicPiece) {
      clone = GameModule.getGameModule().createPiece(piece.getType());
      final Map m = piece.getMap();

      // Temporarily set map to null so that clone won't be added to map
      piece.setMap(null);

      clone.setState(piece.getState());
      piece.setMap(m);
    }
    else if (piece instanceof UsePrototype) {
      clone = clonePiece(((UsePrototype)piece).getExpandedInner());
    }
    else if (piece instanceof EditablePiece && piece instanceof Decorator) {
      try {
        clone = piece.getClass().getConstructor().newInstance();
        final Decorator dclone = (Decorator) clone;
        final Decorator dpiece = (Decorator) piece;

        dclone.setInner(clonePiece(dpiece.getInner()));
        ((EditablePiece)clone).mySetType(dpiece.myGetType());
        dclone.mySetState(dpiece.myGetState());
      }
      catch (Throwable t) {
        ReflectionUtils.handleNewInstanceFailure(t, piece.getClass());
      }
    }
    else {
      clone = ((AddPiece) GameModule.getGameModule().decode(
        GameModule.getGameModule().encode(new AddPiece(piece)))).getTarget();
      final Map m = piece.getMap();

      // Temporarily set map to null so that clone won't be added to map
      piece.setMap(null);

      clone.setState(piece.getState());
      piece.setMap(m);
    }
    return clone;
  }

  public GamePiece clonePieceUnexpanded(GamePiece piece) {
    GamePiece clone = null;
    if (piece instanceof BasicPiece) {
      clone = GameModule.getGameModule().createPiece(piece.getType());
      final Map m = piece.getMap();

      // Temporarily set map to null so that clone won't be added to map
      piece.setMap(null);

      clone.setState(piece.getState());
      piece.setMap(m);
    }
    else if (piece instanceof EditablePiece && piece instanceof Decorator) {
      try {
        clone = piece.getClass().getConstructor().newInstance();
        final Decorator dclone = (Decorator) clone;
        final Decorator dpiece = (Decorator) piece;

        dclone.setInner(clonePieceUnexpanded(dpiece.getInner()));
        ((EditablePiece)clone).mySetType(dpiece.myGetType());
        dclone.mySetState(dpiece.myGetState());
      }
      catch (Throwable t) {
        ReflectionUtils.handleNewInstanceFailure(t, piece.getClass());
      }
    }
    else {
      clone = ((AddPiece) GameModule.getGameModule().decode(
        GameModule.getGameModule().encode(new AddPiece(piece)))).getTarget();
      final Map m = piece.getMap();

      // Temporarily set map to null so that clone won't be added to map
      piece.setMap(null);

      clone.setState(piece.getState());
      piece.setMap(m);
    }
    return clone;
  }
}


@@ -1194,16 +1194,76 @@
  piece.%0A
+   * Expand any prototypes to create a fully function piece%0A
    *%0A   
@@ -1336,32 +1336,390 @@
 ePiece piece) %7B%0A
+    return clonePiece(piece, true);%0A  %7D%0A%0A  /**%0A   * Create a new instance that is a clone of the given piece.%0A   * Do not expand any prototypes.%0A   *%0A   * @return the new instance%0A   */%0A  public GamePiece clonePieceUnexpanded(GamePiece piece) %7B%0A    return clonePiece(piece, false);%0A  %7D%0A%0A%0A  public GamePiece clonePiece(GamePiece piece, boolean expandPiece) %7B%0A
     GamePiece cl
@@ -2088,16 +2088,31 @@
 rototype
+ && expandPiece
 ) %7B%0A    
@@ -3110,1344 +3110,7 @@
  %7D%0A%0A
-  public GamePiece clonePieceUnexpanded(GamePiece piece) %7B%0A    GamePiece clone = null;%0A    if (piece instanceof BasicPiece) %7B%0A      clone = GameModule.getGameModule().createPiece(piece.getType());%0A      final Map m = piece.getMap();%0A%0A      // Temporarily set map to null so that clone won't be added to map%0A      piece.setMap(null);%0A%0A      clone.setState(piece.getState());%0A      piece.setMap(m);%0A    %7D%0A    else if (piece instanceof EditablePiece && piece instanceof Decorator) %7B%0A      try %7B%0A        clone = piece.getClass().getConstructor().newInstance();%0A        final Decorator dclone = (Decorator) clone;%0A        final Decorator dpiece = (Decorator) piece;%0A%0A        dclone.setInner(clonePieceUnexpanded(dpiece.getInner()));%0A        ((EditablePiece)clone).mySetType(dpiece.myGetType());%0A        dclone.mySetState(dpiece.myGetState());%0A      %7D%0A      catch (Throwable t) %7B%0A        ReflectionUtils.handleNewInstanceFailure(t, piece.getClass());%0A      %7D%0A    %7D%0A    else %7B%0A      clone = ((AddPiece) GameModule.getGameModule().decode(%0A        GameModule.getGameModule().encode(new AddPiece(piece)))).getTarget();%0A      final Map m = piece.getMap();%0A%0A      // Temporarily set map to null so that clone won't be added to map%0A      piece.setMap(null);%0A%0A      clone.setState(piece.getState());%0A      piece.setMap(m);%0A    %7D%0A    return clone;%0A  %7D%0A
 %7D%0A%0A

Remove code duplication