/* This code is part of Freenet. It is distributed under the GNU General
 * Public License, version 2 (or at your option any later version). See
 * http://www.gnu.org/ for further details of the GPL. */
package freenet.support;

import java.util.HashMap;
import java.util.Map;

import freenet.client.ArchiveManager.ARCHIVE_TYPE;
import freenet.support.api.ManifestElement;

/**
 * Helper class to estaminate the container size,
 * @author saces
 *
 */
public final class ContainerSizeEstimator {

	public static final ARCHIVE_TYPE DEFAULT_ARCHIVE_TYPE = ARCHIVE_TYPE.TAR;

	public final static class ContainerSize {

		private long _sizeFiles;
		private long _sizeFilesNoLimit;
		private long _sizeSubTrees;
		private long _sizeSubTreesNoLimit;

		private ContainerSize() {
			_sizeFiles = 0;
			_sizeFilesNoLimit = 0;
			_sizeSubTrees = 0;
			_sizeSubTreesNoLimit = 0;
		}

		public long getSizeTotal() {
			return _sizeFiles+_sizeSubTrees;
		}

		public long getSizeTotalNoLimit() {
			return _sizeFilesNoLimit+_sizeSubTreesNoLimit;
		}

		public long getSizeFiles() {
			return _sizeFiles;
		}

		public long getSizeFilesNoLimit() {
			return _sizeFilesNoLimit;
		}

		public long getSizeSubTrees() {
			return _sizeSubTrees;
		}

		public long getSizeSubTreesNoLimit() {
			return _sizeSubTreesNoLimit;
		}
	}

	private ContainerSizeEstimator() {}

	public static ContainerSize getSubTreeSize(HashMap<String, Object> metadata, long maxItemSize, long maxContainerSize, int maxDeep) {
		ContainerSize result = new ContainerSize();
		getSubTreeSize(metadata, result, maxItemSize, maxContainerSize, maxDeep);
		return result;
	}

	private static void getSubTreeSize(HashMap<String, Object> metadata, ContainerSize result, long maxItemSize, long maxContainerSize,int maxDeep) {
		// files
		for(Map.Entry<String,Object> entry:metadata.entrySet()) {
			Object o = entry.getValue();
			if (o instanceof ManifestElement) {
				ManifestElement me = (ManifestElement)o;
				long itemsize = me.getSize();
				if (itemsize > -1) {
					result._sizeFilesNoLimit += getContainerItemSize(me.getSize());
					// Add some bytes for .metadata element.
					// FIXME 128 picked out of the air! Look up the format.
					result._sizeFilesNoLimit += 128 + me.getName().length();
					if (itemsize > maxItemSize)
						result._sizeFiles += 512;  // spare for redirect
					else {
						result._sizeFiles += getContainerItemSize(me.getSize());
						// Add some bytes for .metadata element.
						// FIXME 128 picked out of the air! Look up the format.
						result._sizeFilesNoLimit += 128 + me.getName().length();
						// FIXME The tar file will need the full name????
					}
					if (result._sizeFiles > maxContainerSize) break;
				} else {
					// Redirect.
					result._sizeFiles += 512;
					result._sizeFilesNoLimit += 512;
				}
			}
		}
		// sub dirs
		if (maxDeep > 0) {
			for(Map.Entry<String,Object> entry:metadata.entrySet()) {
				Object o = entry.getValue();
				if (o instanceof HashMap) {
					result._sizeSubTrees += 512;
					@SuppressWarnings("unchecked")
					HashMap<String, Object> hm = (HashMap<String, Object>) o;
					ContainerSize tempResult = new ContainerSize();
					getSubTreeSize(hm, tempResult, maxItemSize, (maxContainerSize-result._sizeSubTrees), maxDeep-1);
					result._sizeSubTrees += tempResult.getSizeTotal();
					result._sizeSubTreesNoLimit += tempResult.getSizeTotalNoLimit();
					if (result._sizeSubTrees > maxContainerSize) break;
				}
			}
		}
	}

	public static long getContainerItemSize(long size) {
		return getContainerItemSize(DEFAULT_ARCHIVE_TYPE, size);
	}

	private static long getContainerItemSize(ARCHIVE_TYPE archiveType, long size) {
		if (archiveType == ARCHIVE_TYPE.TAR)
			return tarItemSize(size);
		throw new UnsupportedOperationException("TODO, only TAR supportet atm.");
	}

	public static long tarItemSize(long size) {
		return 512 + (((size + 511) / 512) * 512);
	}
}
