/*******************************************************************************
 * Copyright (c) 2001, 2009 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Jens Lukowski/Innoopract - initial renaming/restructuring
 *     
 *******************************************************************************/
package org.eclipse.wst.xml.core.internal.parser;



import org.eclipse.jface.text.BadLocationException;
import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentEvent;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionCollection;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionContainer;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
import org.eclipse.wst.sse.core.internal.text.TextRegionListImpl;
import org.eclipse.wst.xml.core.internal.Logger;


public class ContextRegionContainer implements ITextRegionContainer {
	protected int length;
	protected ITextRegionCollection parent;
	protected ITextRegionList regions;
	protected int start;
	protected int textLength;
	protected String type;

	public ContextRegionContainer() {
		super();
		regions = new TextRegionListImpl();

	}

	/**
	 * these "deep" parenting is not normal, but just in case.
	 */
	private IStructuredDocument _getParentDocument() {
		// go up enough parents to get to document
		ITextRegionCollection parent = getParent();
		while (!(parent instanceof IStructuredDocumentRegion)) {
			// would be an error not to be container, but
			// won't check for it now
			parent = ((ITextRegionContainer) parent).getParent();
		}
		return ((IStructuredDocumentRegion) parent).getParentDocument();
	}


	public void adjust(int i) {

		start += i;
		// I erroneously added length and textLength
		// TODO: may want to rename this method to adjustStart
		//length += i;
		//textLength += i;

	}

	public void adjustLength(int i) {
		length += i;
	}

	public void adjustStart(int i) {
		start += i;
	}


	public void adjustTextLength(int i) {
		textLength += i;

	}

	public boolean containsOffset(int i) {

		return getStartOffset() <= i && i < getEndOffset();
	}

	public boolean containsOffset(ITextRegion containedRegion, int offset) {
		return getStartOffset(containedRegion) <= offset && offset < getEndOffset(containedRegion);
	}

	/**
	 * This method is just to equate positions. clients may (will probably)
	 * still need to make calls to equate regions, parent, etc.
	 */
	public void equatePositions(ITextRegion region) {
		start = region.getStart();
		length = region.getLength();
		textLength = region.getTextLength();
	}

	public int getEnd() {
		return start + length;
	}

	public int getEndOffset() {
		// our startOffset take into account our parent, and our start
		return getStartOffset() + getLength();
	}

	public int getEndOffset(ITextRegion containedRegion) {
		return getStartOffset(containedRegion) + containedRegion.getLength();
	}

	public ITextRegion getFirstRegion() {
		return getRegions().get(0);
	}

	public String getFullText() {
		return getParent().getFullText(this);
	}

	public String getFullText(org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion aRegion) {
		// Must be proxied here since aRegion should always be a child of
		// *this* container and indexed from
		// this container's offset
		return parent.getFullText().substring(start + aRegion.getStart(), start + aRegion.getEnd());
	}

	public ITextRegion getLastRegion() {
		return getRegions().get(getRegions().size() - 1);
	}

	public int getLength() {
		return length;
	}


	public int getNumberOfRegions() {
		return getRegions().size();
	}

	public ITextRegionCollection getParent() {
		return parent;
	}

	/**
	 * The parameter offset refers to the overall offset in the document.
	 */
	public ITextRegion getRegionAtCharacterOffset(int offset) {
		ITextRegion result = null;
		if (regions != null) {
			int thisStartOffset = getStartOffset();
			if (offset < thisStartOffset)
				return null;
			int thisEndOffset = getStartOffset() + getLength();
			if (offset > thisEndOffset)
				return null;
			// transform the requested offset to the "scale" that
			// regions are stored in, which are all relative to the
			// start point.
			//int transformedOffset = offset - getStartOffset();
			//
			ITextRegionList regions = getRegions();
			int length = regions.size();
			int low = 0;
			int high = length;
			int mid = 0;
			// Binary search for the region
			while (low < high) {
				mid = low + ((high - low) >> 1);
				ITextRegion region = regions.get(mid);
				if (org.eclipse.wst.sse.core.internal.util.Debug.debugStructuredDocument) {
					System.out.println("region(s) in IStructuredDocumentRegion::getRegionAtCharacterOffset: " + region); //$NON-NLS-1$
					System.out.println("       midpoint of search:" + mid); //$NON-NLS-1$
					System.out.println("       requested offset: " + offset); //$NON-NLS-1$
					//System.out.println(" transformedOffset: " +
					// transformedOffset); //$NON-NLS-1$
					System.out.println("       region start: " + region.getStart()); //$NON-NLS-1$
					System.out.println("       region end: " + region.getEnd()); //$NON-NLS-1$
					System.out.println("       region type: " + region.getType()); //$NON-NLS-1$
					System.out.println("       region class: " + region.getClass()); //$NON-NLS-1$

				}
				// Region is before this one
				if (offset < region.getStart() + thisStartOffset)
					high = mid;
				else if (offset > (region.getEnd() + thisStartOffset - 1))
					low = mid + 1;
				else
					return region;
			}
			return null;
		}
		return result;
	}

	public ITextRegionList getRegions() {
		return regions;
	}

	public int getStart() {
		return start;
	}

	public int getStartOffset() {
		return getParent().getStartOffset() + getStart();
	}

	public int getStartOffset(ITextRegion containedRegion) {
		// it is an error to pass null to this method
		// ISSUE: need better "spec" on error behavior: 
		// for now will return zero as this will roughly 
		// work for some cases (and avoid NPE).
		if (containedRegion == null) {
			return getStartOffset();
		}
		return getStartOffset() + containedRegion.getStart();
	}

	/**
	 * same as getFullText for this region type ... do we need to take white
	 * space off?
	 */

	public String getText() {
		String result = null;
		try {
			IStructuredDocument parentDocument = _getParentDocument();
			result = parentDocument.get(start, length);
		} catch (BadLocationException e) {
			Logger.logException("program error: unreachable exception", e); //$NON-NLS-1$
		}
		return result;
	}

	public String getText(org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion aRegion) {
		// Must be proxied here since aRegion should always be a child of
		// *this* container and indexed from
		// this container's offset
		return parent.getText().substring(start + aRegion.getStart(), start + aRegion.getTextEnd());
	}

	public int getTextEnd() {
		return start + textLength;
	}

	public int getTextEndOffset() {
		ITextRegion region = regions.get(regions.size() - 1);
		// our startOffset take into account our parent, and our start
		// (pa) 10/4 changed to be based on text end
		//           it used to return incorrect value for embedded region containers
		//

		// TODO CRITICAL -- need to re-work this work around, so doesn't
		// depend on XMLRegionContext
		//		// this is a workaround for 226823///////////
		//		for (int i = regions.size() - 1; i >= 0 && region.getType() ==
		// XMLRegionContext.WHITE_SPACE; i--)
		//			region = (ITextRegion) regions.get(i);
		//		/////////////////////////////////////////////

		return getStartOffset() + region.getTextEnd();
	}

	public int getTextEndOffset(ITextRegion containedRegion) {
		int result = 0;
		if (regions != null) {
			int length = getRegions().size();
			for (int i = 0; i < length; i++) {
				ITextRegion region = getRegions().get(i);
				if (region == containedRegion) {
					result = getStartOffset(region) + region.getTextEnd();
					break;
				}
			}
		}
		return result;
	}

	public int getTextLength() {
		return textLength;
	}

	public String getType() {
		return type;
	}

	public void setLength(int i) {
		length = i;
	}

	public void setParent(ITextRegionCollection parentRegion) {
		parent = parentRegion;
	}

	public void setRegions(ITextRegionList containedRegions) {
		regions = containedRegions;
	}

	public void setStart(int i) {
		start = i;
	}

	public void setTextLength(int i) {
		textLength = i;
	}

	public void setType(String string) {
		type = string;
	}

	public String toString() {
		String className = getClass().getName();
		String shortClassName = className.substring(className.lastIndexOf(".") + 1); //$NON-NLS-1$
		String result = "Container!!! " + shortClassName + "--> " + getType() + ": " + getStart() + "-" + getTextEnd() + (getTextEnd() != getEnd() ? ("/" + getEnd()) : ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
		return result;
	}

	public StructuredDocumentEvent updateRegion(Object requester, IStructuredDocumentRegion parent, String changes, int requestStart, int lengthToReplace) {
		org.eclipse.wst.sse.core.internal.provisional.events.RegionChangedEvent result = null;
		// FUTURE_TO_DO: need to implement region level parsing in
		// ITextRegionContainer::updateModel
		// never being called?
		return result;
	}

}
