package com.one.file;

import com.one.Container;
import com.one.ModuleRegistry;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import javax.microedition.io.Connection;

/**
 * Класс для прозрачного открытия контейнеров (архивов) как обычных папок.
 * Содержит пул контейнеров для предотвращения повторных открытий одного и того же контейнера.
 * Учитывает зависимости вложенных контейнеров от базовых при их закрытии.
 * Отслеживает частоту использования контейнеров.
 */
public class TransparentConnector
{
	protected static final String FILE_PREFIX = "file:///";
	protected static final int PREFIX_LEN = FILE_PREFIX.length();

	protected static Hashtable contpool = new Hashtable();
	protected static Hashtable actpool = new Hashtable();

	public static Connection open(String name, int mode) throws IOException
	{
		if(name.startsWith(FILE_PREFIX))
		{
			name = name.substring(PREFIX_LEN);

			if(name.endsWith("/"))
			{
				/*
				 * Это - папка.
				 */

				int fromindex = name.length() - 1;

				while(true)
				{
					int pindex = name.lastIndexOf('.', fromindex);

					if(pindex >= 0)
					{
						/*
						 * Это папка, но тем не менее в имени есть точка.
						 * Это может означать только одно - перед нами контейнер. -- НЕ ФАКТ!
						 * Теперь надо найти первый слеш, который идет за этой точкой,
						 * и взять строку от начала до этого слеша - будет имя контейнера.
						 * Потом надо будет достать контейнер из пула,
						 * а если там нет, то открыть.
						 */

						int sindex = name.indexOf('/', pindex);

						String contname = name.substring(0, sindex + 1);
						Container container = (Container)contpool.get(contname);

						if(container == null)
						{
							String ext = name.substring(pindex + 1, sindex);

							if(ModuleRegistry.getAction(ext) == ModuleRegistry.ACTION_EXPLORE)
							{
								container = (Container)ModuleRegistry.getModuleInstance(ModuleRegistry.getModule(ext));
								container.init(name.substring(0, sindex), false);

								contpool.put(contname, container);
							}
							else
							{
								/*
								 * Излучаем в пространство пучок короткосложных экспрессивных выражений,
								 * поскольку нам попалась обычная папка, в имени которой за каким-то чертом есть точка.
								 */

								 fromindex = name.lastIndexOf('/', pindex - 1) - 1;
								 continue;
							}
						}

						Integer usecount = (Integer)actpool.get(contname);

						if(usecount == null)
						{
							actpool.put(contname, new Integer(1));
						}
						else
						{
							actpool.put(contname, new Integer(usecount.intValue() + 1));
						}

						return container.getFileConnection(name.substring(sindex + 1), mode);
					}
					else
					{
						return Connector.openDirectConnection(name, mode);
					}
				}
			}
			else
			{
				/*
				 * Это - файл
				 */
				
				int sindex = name.lastIndexOf('/');
				String path = name.substring(0, sindex);

				int fromindex = path.length() - 1;

				while(true)
				{
					int pindex = path.lastIndexOf('.', fromindex);

					if(pindex >= 0)
					{
						/*
						 * Был файл, имя отбросили, но точка все равно еще где-то есть.
						 * Значит, перед нами опять контейнер.
						 */

						sindex = name.indexOf('/', pindex);

						String contname = name.substring(0, sindex + 1);
						Container container = (Container)contpool.get(contname);

						if(container == null)
						{
							String ext = name.substring(pindex + 1, sindex);

							if(ModuleRegistry.getAction(ext) == ModuleRegistry.ACTION_EXPLORE)
							{
								container = (Container)ModuleRegistry.getModuleInstance(ModuleRegistry.getModule(ext));
								container.init(name.substring(0, sindex), false);

								contpool.put(contname, container);
							}
							else
							{
								/*
								 * Излучаем в пространство пучок короткосложных экспрессивных выражений,
								 * поскольку нам попалась обычная папка, в имени которой за каким-то чертом есть точка.
								 */

								 fromindex = path.lastIndexOf('/', pindex - 1) - 1;
								 continue;
							}
						}

						Integer usecount = (Integer)actpool.get(contname);

						if(usecount == null)
						{
							actpool.put(contname, new Integer(1));
						}
						else
						{
							actpool.put(contname, new Integer(usecount.intValue() + 1));
						}

						return container.getFileConnection(name.substring(sindex + 1), mode);
					}
					else
					{
						return Connector.openDirectConnection(name, mode);
					}
				}
			}
		}
		else
		{
			throw new IllegalArgumentException("File URL expected");
		}
	}

	/**
	 * Сколько раз использовался указанный контейнер.
	 */
	public static int getUseCount(String contname)
	{
		int res;

		Integer usecount = (Integer)actpool.get(contname);

		if(usecount != null)
		{
			res = usecount.intValue();

			Enumeration dependents = listDependents(contname);

			while(dependents.hasMoreElements())
			{
				res += getUseCount((String)dependents.nextElement());
			}

			return res;
		}
		else
		{
			return 0;
		}
	}

	/**
	 * Проверить, имеются ли открытые контейнеры.
	 */
	public static boolean hasOpenContainers()
	{
		return contpool.size() > 0;
	}

	/**
	 * Проверить, есть ли среди открытых данный контейнер.
	 */
	public static boolean isPathInContainer(String contname)
	{
		if(contpool.containsKey(contname))
		{
			return true;
		}

		Enumeration contnames = contpool.keys();

		while(contnames.hasMoreElements())
		{
			if(contname.startsWith((String)contnames.nextElement()))
			{
				return true;
			}
		}

		return false;
	}

	/**
	 * Найти контейнер, который использовался наименьшее число раз.
	 */
	public static String findLeastUsedContainer()
	{
		String contname = null;

		Enumeration contnames = contpool.keys();
		String s;

		int mincount = Integer.MAX_VALUE;
		int usecount;

		while(contnames.hasMoreElements())
		{
			s = (String)contnames.nextElement();
			usecount = getUseCount(s);

			if(usecount < mincount)
			{
				mincount = usecount;
				contname = s;
			}
		}

		return contname;
	}

	/**
	 * Вернуть список всех открытых контейнеров.
	 */
	public static Enumeration listContainers()
	{
		return contpool.keys();
	}

	/**
	 * Вернуть список контейнеров, которые открыты на базе указанного.
	 */
	public static Enumeration listDependents(String base)
	{
		Vector res = new Vector();

		Enumeration contnames = contpool.keys();
		String contname;

		while(contnames.hasMoreElements())
		{
			contname = (String)contnames.nextElement();

			if(contname.startsWith(base) && !contname.equals(base))
			{
				res.addElement(contname);
			}
		}

		return res.elements();
	}

	/**
	 * Вернуть число контейнеров, которые открыты на базе указанного.
	 */
	public static int getDependentCount(String base)
	{
		int res = 0;

		Enumeration contnames = contpool.keys();
		String contname;

		while(contnames.hasMoreElements())
		{
			contname = (String)contnames.nextElement();

			if(contname.startsWith(base) && !contname.equals(base))
			{
				res++;
			}
		}

		return res;
	}

	/**
	 * Закрыть указанный контейнер и удалить его из пула.
	 *
	 * Перед закрытием контейнера закрываются все открытые на его базе контейнеры.
	 * Если контейнер уже закрыт (его нет в пуле), ничего не происходит.
	 */
	public static void closeContainer(String contname) throws IOException
	{
		Enumeration dependents = listDependents(contname);

		while(dependents.hasMoreElements())
		{
			closeContainer((String)dependents.nextElement());
		}
		
		Container container = (Container)contpool.get(contname);

		if(container != null)
		{
			container.close();

			contpool.remove(contname);
			actpool.remove(contname);
		}
	}

	/**
	 * Закрыть все контейнеры, которые есть в пуле.
	 * Сначала закрываются зависимые, затем базовые (см. closeContainer()).
	 */
	public static void closeContainers() throws IOException
	{
		StringBuffer errors = new StringBuffer();

		Enumeration contnames = contpool.keys();
		String contname;

		while(contnames.hasMoreElements())
		{
			contname = (String)contnames.nextElement();

			try
			{
				closeContainer(contname);
			}
			catch(Exception e)
			{
				e.printStackTrace();
				
				if(errors.length() > 0)
				{
					errors.append("; ");
				}

				errors.append(contname);
				errors.append(": ");
				errors.append(e.toString());
			}
		}

		if(errors.length() > 0)
		{
			throw new IOException(errors.toString());
		}
	}

	/**
	 * Переименовать контейнер в пуле.
	 */
	public static void renameContainer(String oldname, String newname)
	{
		Object temp = contpool.remove(oldname);

		if(temp != null)
		{
			contpool.put(newname, temp);
		}

		temp = actpool.remove(oldname);

		if(temp != null)
		{
			actpool.put(newname, temp);
		}
	}
}
