/**
 * License Agreement.
 *
 *  JBoss RichFaces - Ajax4jsf Component Library
 *
 * Copyright (C) 2007  Exadel, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */

package org.richfaces.model;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * Default {@link TreeRowKey} implementation based on {@link ArrayList}
 * @author Nick Belaevski - nbelaevski@exadel.com
 * created 17.11.2006
 */
public class ListRowKey<T> extends TreeRowKey<T> {

	private ArrayList<T> path;

	/**
	 * 
	 */
	private static final long serialVersionUID = 7718335783201397177L;

	public String toString() {
		return getPath();
	}

	/**
	 * Default constructor
	 */
	public ListRowKey() {
		super();
		this.path = new ArrayList<T>();
	}

	/**
	 * Copy constructor
	 * @param parentRowKey row key to clone
	 */
	
	@SuppressWarnings("unchecked")
	public ListRowKey(ListRowKey<T> parentRowKey) {
		super();
		if (parentRowKey != null) {
			this.path = (ArrayList<T>) parentRowKey.path.clone();
		} else {
			this.path = new ArrayList<T>();
		}
	}

	/**
	 * Appending constructor
	 * @param parentRowKey base row key
	 * @param pathElement path segment to append to base row key
	 */
	public ListRowKey(ListRowKey<T> parentRowKey, T pathElement) {
		this(parentRowKey);
		this.path.add(pathElement);
	}


	/**
	 * List constructor
	 * @param list List of strings to create corresponding row key from
	 */
	public ListRowKey(List<T> list) {
		super();

		this.path = new ArrayList<T>(list);
	}
	
	/**
	 * Path object constructor
	 * @param path first path segment
	 */
	public ListRowKey(T path) {
		super();
		this.path = new ArrayList<T>(1);
		this.path.add(path);
	}

	public int depth() {
		return path.size();
	}

	public Iterator<T> iterator() {
		return path.iterator();
	}

	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((path == null) ? 0 : path.hashCode());
		return result;
	}

	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		final ListRowKey<?> other = (ListRowKey<?>) obj;
		if (path == null) {
			if (other.path != null)
				return false;
		} else if (!path.equals(other.path))
			return false;
		return true;
	}

	public Iterator<T> getSubPathIterator(int fromIndex) {
		return path.listIterator(fromIndex);
	}

	public ListRowKey<T> getSubKey(int fromIndex) {
		return new ListRowKey<T>(path.subList(fromIndex, path.size()));
	}
	
	
	@SuppressWarnings("unchecked")
	public boolean isSubKey(TreeRowKey<T> rowKey) {
		if (rowKey instanceof ListRowKey) {
			ListRowKey<T> listRowKey = (ListRowKey<T>) rowKey;

			return depth() == getCommonPathLength(listRowKey);
		} else {
			return super.isSubKey(rowKey);
		}
	}

	public String getPath() {
		StringBuffer result = new StringBuffer();
		Iterator<T> iterator = path.iterator();
		boolean hasNext = iterator.hasNext();

		while (hasNext) {
			String pathSegment = iterator.next().toString();

			StringBuffer escapedSubPath = new StringBuffer();
			for (int i = 0; i < pathSegment.length(); i++) {
				char ch = pathSegment.charAt(i);

				//escape
				if (AbstractTreeDataModel.SEPARATOR == ch || ListRowKey.SEPARATOR_ESCAPE_CHAR == ch) {
					escapedSubPath.append(ListRowKey.SEPARATOR_ESCAPE_CHAR);
				}

				escapedSubPath.append(ch);
			}
			
			result.append(escapedSubPath.toString());
			
			hasNext = iterator.hasNext();

			if (hasNext) {
				result.append(AbstractTreeDataModel.SEPARATOR);
			}
		}

		return result.toString();
	}

	public int getCommonPathLength(TreeRowKey<T> otherRowKey) {
		if (otherRowKey == null)
			return 0;
		Iterator<T> iterator = this.iterator();
		Iterator<T> otherIterator = otherRowKey.iterator();
		int length = 0;
		while (iterator.hasNext() && otherIterator.hasNext()
				&& iterator.next().equals(otherIterator.next()))
			length++;
		return length;
	}
	
	public T get(int i) {
		return path.get(i);
	}
}
