/*******************************************************************************
 * Copyright (c) 2005, 2009 QNX Software Systems and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    QNX - Initial API and implementation
 *    Markus Schorn (Wind River Systems)
 *    Sergey Prigogin (Google)
 *******************************************************************************/

package org.eclipse.cdt.internal.core.pdom.db;

import org.eclipse.cdt.core.dom.IPDOMVisitor;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMLinkage;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMNode;
import org.eclipse.core.runtime.CoreException;

/**
 * Represents a linked list of PDOMNode records
 * @author Doug Schaefer
 *
 */
public class PDOMNodeLinkedList {
	private long offset;
	private PDOMLinkage linkage;
	private boolean allowsNull;
	
	private static final int FIRST_MEMBER = 0;
	protected static final int RECORD_SIZE = 4;

	public PDOMNodeLinkedList(PDOMLinkage linkage, long offset, boolean allowsNulls) {
		this.offset = offset;
		this.linkage = linkage;
		this.allowsNull = allowsNulls;
	}
	
	/**
	 * Creates an object representing a linked list at the specified offset of the specified pdom.
	 * The linked list created may not hold null items
	 * @param linkage
	 * @param offset
	 */
	public PDOMNodeLinkedList(PDOMLinkage linkage, long offset) {
		this(linkage, offset, false);
	}

	protected int getRecordSize() {
		return RECORD_SIZE;
	}
	
	public void accept(IPDOMVisitor visitor) throws CoreException {
		Database db = linkage.getDB();
		long firstItem = db.getRecPtr(offset + FIRST_MEMBER);
		if (firstItem == 0)
			return;
		
		long item = firstItem;
		do {
			PDOMNode node;
			final long record= db.getRecPtr(item + ListItem.ITEM);
			if (record == 0) {
				if (!allowsNull) {
					throw new NullPointerException();
				}
				node= null;
			} else {
				node= linkage.getNode(record);
			}
			if (visitor.visit(node) && node != null) {
				node.accept(visitor);
			}
			visitor.leave(node);
		} while ((item = db.getRecPtr(item + ListItem.NEXT)) != firstItem);
	}
	
	private ListItem getFirstMemberItem() throws CoreException {
		Database db = linkage.getDB();
		long item = db.getRecPtr(offset + FIRST_MEMBER);
		return item != 0 ? new ListItem(db, item) : null;
	}

	/**
	 * Returns node at position {@code pos}. Not recommended to be used in a loop since
	 * such a loop would be more expensive that a single {@code accept(IPDOMVisitor)} call. 
	 * @param pos A zero-based position in the list.
	 * @return The node at position {@code pos}, or {@code null} if no such node exists.
	 */
	public PDOMNode getNodeAt(int pos) throws CoreException {
		Database db = linkage.getDB();
		long firstItem = db.getRecPtr(offset + FIRST_MEMBER);
		if (firstItem == 0) {
			return null;
		}
		long item = firstItem;
		do {
			if (--pos < 0) {
				long record = db.getRecPtr(item + ListItem.ITEM);
				if (record == 0) {
					if (!allowsNull) {
						throw new NullPointerException();
					}
					return null;
				} else {
					return linkage.getNode(record);
				}
			}
		} while ((item = db.getRecPtr(item + ListItem.NEXT)) != firstItem);
		return null;
	}

	public void addMember(PDOMNode member) throws CoreException {
		addMember(allowsNull && member==null ? 0 : member.getRecord());
	}
	
	protected void addMember(long record) throws CoreException {
		Database db = linkage.getDB();
		ListItem firstMember = getFirstMemberItem();
		if (firstMember == null) {
			firstMember = new ListItem(db);
			firstMember.setItem(record);
			firstMember.setNext(firstMember);
			firstMember.setPrev(firstMember);
			db.putRecPtr(offset + FIRST_MEMBER, firstMember.getRecord());
		} else {
			ListItem newMember = new ListItem(db);
			newMember.setItem(record);
			ListItem prevMember = firstMember.getPrev();
			prevMember.setNext(newMember);
			firstMember.setPrev(newMember);
			newMember.setPrev(prevMember);
			newMember.setNext(firstMember);
		}
	}

	public void deleteListItems() throws CoreException {
		ListItem item = getFirstMemberItem();
		if (item != null) {
			long firstRec= item.record;

			do {
				ListItem nextItem= item.getNext();
				item.delete();
				item= nextItem;
			}
			while (item.record != firstRec && item.record != 0);
		}
	}
}
