/*  
 * Copyright 2004-2009 unitarou <boss@unitarou.org>. 
 * All rights reserved.
 * 
 * This program and the accompanying materials are made available under the terms of 
 * the Common Public License v1.0 which accompanies this distribution, 
 * and is available at http://opensource.org/licenses/cpl.php
 * 
 * Contributors:
 *     unitarou - initial API and implementation
 */
package org.unitarou.sgf.cmd;

import java.util.Iterator;

import org.unitarou.cmd.AbstractCommand;
import org.unitarou.lang.ArgumentChecker;
import org.unitarou.sgf.GameTree;
import org.unitarou.sgf.Node;
import org.unitarou.sgf.RootGameTree;
import org.unitarou.sgf.Sequence;

/**
 * RXgN^Åwèµ½{@link org.unitarou.sgf.Node}ð
 * {@link org.unitarou.sgf.Sequence}©çí·éR}hÅ·B<br>
 * ±±Å{@link org.unitarou.sgf.Sequence}Ì·³ª0ÉÈÁ½êÍ
 * q{@link org.unitarou.sgf.GameTree}ð©gÌeÉo^µÜ·B<br>
 * 
 * {@link org.unitarou.sgf.Node}ª{@link org.unitarou.sgf.Sequence}É¶ÝµÈ¢êâA
 * [gm[hÅSequenceªPÅqGameTreeª éê(í·éÆRootÌãÉªòªKv)É
 * {@link java.lang.IllegalStateException}ªo³êÜ·B 
 * 
 * @author unitarou &lt;boss@unitarou.org&gt; 
 */
public class RemoveNode extends AbstractCommand {

	private final GameTree parent_;
	private final GameTree gameTree_;
	private final Node target_;

	/**
	 * {@link #undo()}ÌÌ}ü|CgÅ·B
	 * öðíµ½êÍ<code>null</code>ªüèÜ·B
	 */
	private final Node insertPoint_;
	
	/**
	 * sequenceÌTCYª1ÌÉ{@link #gameTree_}ÌqªüèÜ·B
	 * »êÈOÌêÍnullÉÈèÜ·B
	 */
	private final GameTree[] children_;
	
	/**
	 * sequenceÌTCYª1ÌÉ{@link #parent_}ÌqªüèÜ·B
	 * »êÈOÌêÍnullÉÈèÜ·B
	 */
	private final GameTree[] siblings_;
	
	/**
	 * target_ªV[PXÌöÅqð½È¢êA
	 * ±¿çÌR}hªÀs³êÜ·B
	 * »êÈOÌÆ«ÍnullðÛµÜ·B
	 */
	private final RemoveLastNode removeLastNode_;
	
	/**
	 * gameTreeàÌ{@link Sequence}É étargetðí·éR}hðì¬µÜ·B
	 * @param gameTree 
	 * @param target 
	 * 
	 * @throws org.unitarou.lang.NullArgumentException øªnullÌê 
	 * @throws IllegalArgumentException 
	 *          {@link org.unitarou.sgf.Node}ª{@link org.unitarou.sgf.Sequence}É¶ÝµÈ¢êâ
	 *          [gm[hÅSequenceªPÅqGameTreeª éê(í·éÆRootÌãÉªòªKv)
	 */
	public RemoveNode(GameTree gameTree, Node target) {
		super();
		ArgumentChecker.throwIfNull(gameTree, target);
		
		gameTree_ = gameTree;
		parent_ = gameTree_.getParent();
		Sequence sequence = gameTree_.getSequence();
		if ( (gameTree_ instanceof RootGameTree) 
				&& (sequence.size() == 1)
				&& (2 <= gameTree_.getChildrenSize())) 
		{
			throw new IllegalArgumentException("Bad argument, can't remove root sequence."); //$NON-NLS-1$
		}
		target_ = target;
		
		boolean found = false;
		Node point = null;
		for(Iterator<Node> ip = sequence.iterator(); ip.hasNext(); ) {
			Node node = ip.next();
			if (node == target_) {
				point = ip.hasNext() ? ip.next() : null;
				found = true;
				break;
			}
		}
		insertPoint_ = point;
		
		if (!found) {
			throw new IllegalArgumentException(
					"Can't find target node(" + target_  //$NON-NLS-1$
					+ ") in the sequence (" + sequence + ')'); //$NON-NLS-1$
		}
		
		if (sequence.size() == 1) {
			children_ = gameTree.getChildren();
			siblings_ = parent_.getChildren();
		} else {
			children_ = null;
			siblings_ = null;
		}
		
		if ((gameTree_.getSequence().getLast() == target_)
				&& (gameTree_.getChildrenSize() == 0)) 
		{
			removeLastNode_ = new RemoveLastNode(gameTree_);
		} else {
			removeLastNode_ = null;
		}
	}

	@Override
	protected void executeCommand() {
		if (removeLastNode_ != null) {
			removeLastNode_.execute();
			return;
		}
		Sequence sequence = gameTree_.getSequence();
		sequence.remove(target_);
		if (sequence.size() != 0) {
			return;
		}
		
		//V[PXª0ÉÈéêÍAeÌGameTreeÉ·éKvª éB
		parent_.removeGameTree(gameTree_);
		for (GameTree child : children_) {
			gameTree_.removeGameTree(child);
			parent_.addGameTree(child);
		}
	}

	@Override
	protected void undoCommand() {
		if (removeLastNode_ != null) {
			removeLastNode_.undo();
			return;
		}
		
		Sequence sequence = gameTree_.getSequence();
		if (insertPoint_ != null) {
			sequence.insert(target_, insertPoint_);
		} else {
			sequence.addLast(target_);
		}
		
		if (sequence.size() != 1) {
			return;
		}
		
		// V[PXª0¾Á½êÍAe©çGameTreeðæèã°Ä
		// ©ªÌqÉ·éKvª éB
		for (GameTree sibling : siblings_) {
			parent_.removeGameTree(sibling);
		}
		for (GameTree child : children_) {
			parent_.removeGameTree(child);
		}

		for (GameTree sibling : siblings_) {
			parent_.addGameTree(sibling);
		}
		for (GameTree child : children_) {
			gameTree_.addGameTree(child);
		}
	}
}
