package net.sf.saxon.event;

import net.sf.saxon.Controller;
import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.om.Item;
import net.sf.saxon.om.SequenceIterator;
import net.sf.saxon.om.ValueRepresentation;
import net.sf.saxon.tree.iter.EmptyIterator;
import net.sf.saxon.tree.iter.ListIterator;
import net.sf.saxon.value.EmptySequence;
import net.sf.saxon.value.SequenceExtent;

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


/**
 * This outputter is used when writing a sequence of atomic values and nodes, that
 * is, when xsl:variable is used with content and an "as" attribute. The outputter
 * builds the sequence and provides access to it. (It isn't really an outputter at all,
 * it doesn't pass the events to anyone, it merely constructs the sequence in memory
 * and provides access to it). Note that the event sequence can include calls such as
 * startElement and endElement that require trees to be built. If nodes such as attributes
 * and text nodes are received while an element is being constructed, the nodes are added
 * to the tree. Otherwise, "orphan" nodes (nodes with no parent) are created and added
 * directly to the sequence.
 *
 * <p>This class is not used to build temporary trees. For that, the ComplexContentOutputter
 * is used.</p>
 *
 *
 * @author Michael H. Kay
 */

public final class SequenceOutputter extends SequenceWriter {

	private List<Item> list;
	private Controller controller;  // enables the SequenceOutputter to be reused


	/**
	 * Create a new SequenceOutputter
     * @param pipe the pipeline configuration
     */

	public SequenceOutputter(/*@NotNull*/ PipelineConfiguration pipe) {
        super(pipe);
		this.list = new ArrayList<Item>(50);
	}

	public SequenceOutputter(PipelineConfiguration pipe, Controller controller, int estimatedSize) {
        super(pipe);
		this.list = new ArrayList<Item>(estimatedSize);
		this.controller = controller;
	}

	public SequenceOutputter(PipelineConfiguration pipe, Controller controller) {
        super(pipe);
		this.list = new ArrayList<Item>(50);
		this.controller = controller;
	}

    /**
     * Allocate a SequenceOutputter. Used from generated bytecode.
     * @param context dynamic XPath context
     * @param hostLang host language (XSLT/XQuery)
     * @return the allocated SequenceOutputter
     * @see com.saxonica.bytecode.util.CompilerService
     */

	/*@Nullable*/ public static SequenceOutputter allocateSequenceOutputter(XPathContext context, int hostLang){

		Controller controller = context.getController();
		SequenceOutputter seq = controller.allocateSequenceOutputter(20);
		seq.getPipelineConfiguration().setHostLanguage(hostLang);
		return seq;
	}
	/**
	 * Clear the contents of the SequenceOutputter and make it available for reuse
	 */

	public void reset() {
		list = new ArrayList<Item>(Math.min(list.size()+10, 50));
		if (controller != null && adviseReuse()) {
			controller.reuseSequenceOutputter(this);
		}
	}

	/**
	 * Method to be supplied by subclasses: output one item in the sequence.
	 */

	public void write(Item item) {
		list.add(item);
	}

    /**
	 * Get the sequence that has been built
     * @return the value (sequence of items) that have been written to this SequenceOutputter
	 */

	public ValueRepresentation<Item> getSequence() {
		switch (list.size()) {
		case 0:
			return EmptySequence.getInstance();
		case 1:
			return list.get(0);
		default:
			return new SequenceExtent<Item>(list);
		}
	}

	/**
	 * Get an iterator over the sequence of items that has been constructed
     * @return an iterator over the items that have been written to this SequenceOutputter
     */

	public SequenceIterator<? extends Item> iterate() {
		if (list.isEmpty()) {
			return EmptyIterator.emptyIterator();
		} else {
			return new ListIterator<Item>(list);
		}
	}

	/**
	 * Get the list containing the sequence of items
     * @return the list of items that have been written to this SequenceOutputter
     */

	public List<Item> getList() {
		return list;
	}

	/**
	 * Get the first item in the sequence that has been built
     * @return the first item in the list of items that have been written to this SequenceOutputter;
     * or null if the list is empty.
     */

	public Item getFirstItem() {
		if (list.isEmpty()) {
			return null;
		} else {
			return list.get(0);
		}
	}

	/**
	 * Get the last item in the sequence that has been built, and remove it
     * @return the last item written
     */

	public Item popLastItem() {
		if (list.isEmpty()) {
			return null;
		} else {
			return list.remove(list.size()-1);
		}
	}


}

//
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
// you may not use this file except in compliance with the License. You may obtain a copy of the
// License at http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
// See the License for the specific language governing rights and limitations under the License.
//
// The Original Code is: all this file
//
// The Initial Developer of the Original Code is Saxonica Limited.
// Portions created by ___ are Copyright (C) ___. All rights reserved.
//
// Contributor(s):
//