/*
 * $Id: PieceCloner.java 4586 2008-11-30 18:12:31Z uckelman $
 *
 * 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 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();
        ((Decorator)clone).setInner(clonePiece(((Decorator)piece).getInner()));
        ((EditablePiece)clone).mySetType(((Decorator)piece).myGetType());
        ((Decorator)clone).mySetState(((Decorator)piece).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;
  }
}
