/*******************************************************************************
 * Copyright (c) 2000, 2004 IBM Corporation 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.ui.internal.texteditor.quickdiff.compare.rangedifferencer;

/**
 * Memory-monitoring factory for <code>LinkedRangeDifference</code>.
 *
 * @since 3.0
 */
public class LinkedRangeFactory {

	/**
	 * Exception that is thrown after the minimal allowed free memory is reached.
	 * <p>
	 * This class is not intended to be serialized.
	 * </p>
	 *
	 */
	public static class LowMemoryException extends Exception {

		/**
		 * Serial version UID for this class.
		 * <p>
		 * Note: This class is not intended to be serialized.
		 * </p>
		 * @since 3.1
		 */
		private static final long serialVersionUID= 3977582493823939894L;

		/**
		 * Initialize without detail message.
		 */
		public LowMemoryException() {
			super();
		}

		/**
		 * Initialize with the given detail message.
		 *
		 * @param message the detail message
		 */
		public LowMemoryException(String message) {
			super(message);
		}
	}

	/**
	 * Relative amount of memory that must be free in order to allow the creation of additional instances
	 */
	private static final double THRESHOLD= 0.1;
	/**
	 * Number of instantiations after which the amount of free memory is checked
	 */
	private static final long CHECK_INTERVAL= 5000;
	/**
	 * Considered maximal size of a difference object in bytes.
	 */
	private static final long OBJECT_SIZE= 100;
	/**
	 * The maximal memory requirement for the next round in bytes.
	 */
	private static final long MAXIMAL_INTERVAL_REQUIREMENT= CHECK_INTERVAL * OBJECT_SIZE;
	/**
	 * Allowed memory consumption in bytes.
	 */
	private static final long MAX_MEMORY_CONSUMPTION= 10 * 1024 * 1024;
	/**
	 * The maximal number of instances.
	 */
	private static final long MAX_INSTANCES= MAX_MEMORY_CONSUMPTION /  OBJECT_SIZE;


	/**
	 * Preallocated low memory exception
	 */
	private LowMemoryException fLowMemoryException= new LowMemoryException();

	/**
	 * Number of instantiations
	 */
	private long fCount= 0;

	/**
	 * Create a new linked range difference with the given next range and operation.
	 *
	 * @param next the next linked range difference
	 * @param operation the operation
	 * @return the new linked range difference
	 * @throws LowMemoryException
	 */
	public LinkedRangeDifference newRange(LinkedRangeDifference next, int operation) throws LowMemoryException {
		check();
		return new LinkedRangeDifference(next, operation);
	}

	/**
	 * After <code>CHECK_INTERVAL</code> calls check whether at least a fraction of <code>THRESHOLD</code>
	 * of the maximal available memory is free, otherwise throw an {@link LowMemoryException}.
	 *
	 * @throws LowMemoryException
	 */
	private void check() throws LowMemoryException {
		if (fCount % CHECK_INTERVAL == 0) {

			Runtime runtime= Runtime.getRuntime();
			long maxMemory= runtime.maxMemory();
			long maxFreeMemory= maxMemory - (runtime.totalMemory() - runtime.freeMemory());

			if (((float) (maxFreeMemory - MAXIMAL_INTERVAL_REQUIREMENT)) / maxMemory < THRESHOLD)
				throw fLowMemoryException;
		}
		if (++fCount >= MAX_INSTANCES)
			throw fLowMemoryException;
	}
}
