/* RowTracker.java

	Purpose:
		
	Description:
		
	History:
		Oct 24, 2014 2:17:01 PM, Created by henrichen

	Copyright (C) 2014 Potix Corporation. All Rights Reserved.
*/

package org.zkoss.zssex.model.sys;

import java.io.Serializable;
import java.util.Collection;
import java.util.LinkedHashSet;

import org.zkoss.zss.model.sys.dependency.Ref;
import org.zkoss.zssex.util.Interval;
import org.zkoss.zssex.util.Interval.InfiniteInterval;
import org.zkoss.zssex.util.IntervalTree;

/**
 * Tracking row references.
 * @author henri
 *
 */
class RowTracker implements Serializable {
	private static final long serialVersionUID = 1624759978398776608L;
	final private IntervalTree<ColTracker> _rowIntervalTree;
	final private int _maxRowLimit;
	final private int _maxColLimit; 
	
	RowTracker(int maxRowLimit, int maxColLimit) {
		_rowIntervalTree = new IntervalTree<ColTracker>(maxRowLimit);
		_maxRowLimit = maxRowLimit;
		_maxColLimit = maxColLimit;
	}

	Interval<Ref> insertRef(Ref ref) {
		final Interval<ColTracker> interval = prepareKeyInterval(ref, new ColTracker(_maxColLimit));
		final Interval<ColTracker> intv = _rowIntervalTree.insertInterval(interval, null);
		final ColTracker colTracker = intv.getPayload();
		return colTracker.insertRef(ref);
	}
	
	Interval<Ref> deleteRef(Ref ref) {
		final Interval<ColTracker> intv = searchColTracker(ref);
		if (intv == null) return null;
		
		final ColTracker colTracker = intv.getPayload();
		final Interval<Ref> refIntv = colTracker.deleteRef(ref);
		
		if (colTracker.isEmpty()) {
			_rowIntervalTree.deleteInterval(intv, null);
		}
		
		return refIntv;
	}
	
	Interval<Ref> searchRef(Ref ref) {
		final Interval<ColTracker> intv = searchColTracker(ref);
		if (intv == null) return null;
		
		final ColTracker colTracker = intv.getPayload();
		return colTracker.searchRef(ref);
	}
	
	Collection<Interval<Ref>> overlaps(Ref ref) {
		final Interval<ColTracker> interval = prepareKeyInterval(ref, null);
		final Collection<Interval<ColTracker>> intervals = _rowIntervalTree.overlaps(interval);
		final LinkedHashSet<Interval<Ref>> results = new LinkedHashSet<Interval<Ref>>();
		for (Interval<ColTracker> intv : intervals) {
			final ColTracker colTracker = intv.getPayload();
			results.addAll(colTracker.overlaps(ref));
		}
		return results;
	}

	Collection<Interval<Ref>> contained(Ref ref) {
		final Interval<ColTracker> interval = prepareKeyInterval(ref, null);
		final Collection<Interval<ColTracker>> intervals = _rowIntervalTree.contained(interval);
		final LinkedHashSet<Interval<Ref>> results = new LinkedHashSet<Interval<Ref>>();
		for (Interval<ColTracker> intv : intervals) {
			final ColTracker colTracker = intv.getPayload();
			results.addAll(colTracker.overlaps(ref));
		}
		return results;
	}

	boolean isEmpty() {
		return _rowIntervalTree.isEmpty();
	}
	
	private Interval<ColTracker> searchColTracker(Ref ref) {
		final Interval<ColTracker> interval = prepareKeyInterval(ref, null);
		return _rowIntervalTree.searchInterval(interval);
	}
	
	private Interval<ColTracker> prepareKeyInterval(Ref ref, ColTracker payload) {
		final int row1 = ref.getRow();
		final int row2 = ref.getLastRow();
		return row1 <= 0 && row2 < 0 ?
				new InfiniteInterval<ColTracker>(_maxRowLimit, payload) :
				new Interval<ColTracker>(row1, row2, payload);
	}
}
