/*
 * The JTS Topology Suite is a collection of Java classes that
 * implement the fundamental operations required to validate a given
 * geo-spatial data set to a known topological specification.
 *
 * Copyright (C) 2001 Vivid Solutions
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * For more information, contact:
 *
 *     Vivid Solutions
 *     Suite #1A
 *     2328 Government Street
 *     Victoria BC  V8T 5G5
 *     Canada
 *
 *     (250)385-6040
 *     www.vividsolutions.com
 */
package com.vividsolutions.jts.index.intervalrtree;

import java.util.*;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.index.*;
import com.vividsolutions.jts.io.WKTWriter;

/**
 * A static index on a set of 1-dimensional intervals,
 * using an R-Tree packed based on the order of the interval midpoints.
 * It supports range searching,
 * where the range is an interval of the real line (which may be a single point).
 * A common use is to index 1-dimensional intervals which 
 * are the projection of 2-D objects onto an axis of the coordinate system.
 * <p>
 * This index structure is <i>static</i> 
 * - items cannot be added or removed once the first query has been made.
 * The advantage of this characteristic is that the index performance 
 * can be optimized based on a fixed set of items.
 * 
 * @author Martin Davis
 */
public class SortedPackedIntervalRTree 
{
  private List leaves = new ArrayList();
	private IntervalRTreeNode root = null;
	
	public SortedPackedIntervalRTree()
	{
		
	}
	
  /**
   * Adds an item to the index which is associated with the given interval
   * 
   * @param min the lower bound of the item interval
   * @param max the upper bound of the item interval
   * @param item the item to insert
   * 
   * @throws IllegalStateException if the index has already been queried
   */
	public void insert(double min, double max, Object item)
	{
    if (root != null)
      throw new IllegalStateException("Index cannot be added to once it has been queried");
    leaves.add(new IntervalRTreeLeafNode(min, max, item));
	}
	
  private void init()
  {
    if (root != null) return;
    buildRoot();
  }
  
  private synchronized void buildRoot() 
  {
    if (root != null) return;
    root = buildTree();
  }
  
	private  IntervalRTreeNode buildTree()
	{
	  
    // sort the leaf nodes
    Collections.sort(leaves, new IntervalRTreeNode.NodeComparator());
    
    // now group nodes into blocks of two and build tree up recursively
		List src = leaves;
		List temp = null;
		List dest = new ArrayList();
		
		while (true) {
			buildLevel(src, dest);
			if (dest.size() == 1)
				return (IntervalRTreeNode) dest.get(0);
      
			temp = src;
			src = dest;
			dest = temp;
		}
	}
	
  private int level = 0;
  
	private void buildLevel(List src, List dest) 
  {
    level++;
		dest.clear();
		for (int i = 0; i < src.size(); i += 2) {
			IntervalRTreeNode n1 = (IntervalRTreeNode) src.get(i);
			IntervalRTreeNode n2 = (i + 1 < src.size()) 
						? (IntervalRTreeNode) src.get(i) : null;
			if (n2 == null) {
				dest.add(n1);
			} else {
				IntervalRTreeNode node = new IntervalRTreeBranchNode(
						(IntervalRTreeNode) src.get(i),
						(IntervalRTreeNode) src.get(i + 1));
//        printNode(node);
//				System.out.println(node);
				dest.add(node);
			}
		}
	}
	
  private void printNode(IntervalRTreeNode node)
  {
    System.out.println(WKTWriter.toLineString(new Coordinate(node.min, level), new Coordinate(node.max, level)));
  }
  
  /**
   * Search for intervals in the index which intersect the given closed interval
   * and apply the visitor to them.
   * 
   * @param min the lower bound of the query interval
   * @param max the upper bound of the query interval
   * @param visitor the visitor to pass any matched items to
   */
	public void query(double min, double max, ItemVisitor visitor)
	{
    init();

		root.query(min, max, visitor);
	}
  
}
