/* $Id: Proxy.java,v 1.1 2004/01/09 16:54:18 burkhard Exp $
 * Created on 30.05.2003 by sell
 *
 */
package de.skyrix.zsp.logic;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import javax.swing.Timer;
import javax.swing.event.EventListenerList;

import de.skyrix.zsp.conf.ZSPConfig;
import de.skyrix.zsp.event.ProxyStateEvent;
import de.skyrix.zsp.event.ProxyStateListener;
import de.skyrix.zsp.logic.cache.CacheManageException;
import de.skyrix.zsp.logic.cache.CacheManager;
import de.skyrix.zsp.logic.httpclient.HttpRequest;
import de.skyrix.zsp.logic.httpclient.HttpResponse;

/**
 * 
 * @author sell
 * @version 
 */
public class Proxy {

	/**True if proxy is online (ZideStore server is available)*/
	private static boolean online = true;

	/**Referenz to connector class*/
	private ZidestoreConnection connection;

	/**Used by {@link #getInstance() getInstance()} method*/
	private static Proxy selfInstance = null;

	/**Timer to check online state if offline*/
	private static Timer checkTimer = null;

	/**The url of the ZideStore server*/
	private String url = null;

	/**Container for the event listeners*/
	private static EventListenerList listeners;

	public Proxy() {
		connection = new ZidestoreConnection();
		checkTimer = new Timer(15000, new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				checkOnlineState();
			}
		});

		selfInstance = this;
		checkOnlineState();
	}

	private void checkOnlineState() {
		//System.out.print("checking online state...");
		try {
			HttpRequest request =
				connection.createRequest(
					ZSPConfig.getProperty("zidestore_user"),
					"GET");
			request.setRequestProperty("Depth", "1");
			connection.sendData(request);
		}
		catch (Exception e) {
		}
	}

	public static void setOnline(boolean online) {
		if (online == Proxy.online)
			return;

		System.out.println(
			"Switching online mode to " + (online ? "online" : "offline"));

		if (!online)
			checkTimer.start();
		else if (checkTimer.isRunning())
			checkTimer.stop();

		Proxy.online = online;

		//call listeners
		if (listeners != null
			&& listeners.getListenerCount(ProxyStateListener.class) > 0) {
			Object[] lobj = listeners.getListenerList();

			for (int i = 0; i < lobj.length; i++) {
				if (lobj[i] instanceof ProxyStateListener) {
					ProxyStateEvent event =
						new ProxyStateEvent(getInstance(), 100, online);
					((ProxyStateListener) lobj[i]).proxyStateChanged(event);
				}
			}
		}
	}

	private String patchLocation(String location) {
		try {
			URL url = new URL(location);
			return "http://"
				+ System.getProperty("zsp.proxyName")
				+ ":"
				+ ZSPConfig.getProperty("proxy_port")
				+ url.getPath();
		}
		catch (Exception e) {
		}

		return location;
	}

	private DAVItem patchRootFolder(DAVItem rootFolder) {
		if (rootFolder == null)
			return null;

		//System.out.println("rootFolder found: " + rootFolder.getLocation());

		Enumeration keyEnumeration = rootFolder.keys();
		while (keyEnumeration.hasMoreElements()) {
			try {
				String key = (String) keyEnumeration.nextElement();

				rootFolder.setProperty(
					key,
					patchLocation(rootFolder.getValue(key)),
					rootFolder.getNameSpacePrefix(key),
					rootFolder.getNameSpaceURI(key));
			}
			catch (Exception e) {
			}
		}

		return rootFolder;
	}

	public DAVItem getFolder(String folder) {
		DAVItem item = null;
		if (folder == null)
			return null;

		CacheManager cacheManager = CacheManager.getInstance();

		if (online) {
			try {
				item = connection.getFolderAttribs(folder);
				//cacheManager.putFolder(item);
			}
			catch (Exception e) {
				if (!online)
					return getFolder(folder);

				e.printStackTrace();
			}
		}
		else {
			item = cacheManager.getFolder(folder);
		}

		if (item == null)
			return null;

		item.setLocation(patchLocation(item.getLocation()));

		//patching href tag to local proxy server
		try {
			item.setProperty(
				"href",
				patchLocation(item.getValue("href")),
				item.getNameSpacePrefix("href"),
				item.getNameSpaceURI("href"));
		}
		catch (Exception e) {
			e.printStackTrace();
		}

		if (folder
			.endsWith(
				"/zidestore/so/" + ZSPConfig.getProperty("zidestore_user") + "/"))
			return patchRootFolder(item);
		else
			return item;
	}

	public List getMessages(String uri) throws MalformedURLException {
		if (uri == null)
			return null;

		//split the uri into the uri part and the range part
		String uriPart = null;
		String rangePart = null;

		try {
			uriPart = uri.substring(0, uri.lastIndexOf("/"));
			if (!uriPart.endsWith("/"))
				uriPart += "/";
			rangePart = uri.substring(uri.lastIndexOf("/") + 1);
			if (!rangePart.startsWith("_range_"))
				rangePart = "_range_" + rangePart;
		}
		catch (Exception e) {
		}

		System.out.println("uri = " + uriPart);
		System.out.println("range = " + rangePart);

		List messages = null;

		if (online) {
			try {
				//messages = connection.getAttributes(uriPart, rangePart);
				//getIDsAndVersions(uriPart);
			}
			catch (Exception e) {
				if (!online)
					return getMessages(uri);
			}
		}

		messages = CacheManager.getInstance().getAttributes(uriPart, rangePart);

		if (messages != null && !messages.isEmpty()) {
			ArrayList items = new ArrayList();
			Iterator itemsIterator = messages.iterator();
			while (itemsIterator.hasNext()) {
				try {
					DAVItem item = (DAVItem) itemsIterator.next();
					item.setLocation(patchLocation(item.getLocation()));
					items.add(item);
				}
				catch (Exception e) {
				}
			}
			return items;
		}

		return null;
	}

	public boolean createMessage(DAVItem item, String location) {
		//TODO: createMessage implementieren (PROPPATCH)
		CacheManager cacheManager = CacheManager.getInstance();
		if (!cacheManager.createMessage(item, location))
			return false;

		//if online...add message to update Q
		if (online) {
			System.out.println("adding message to update Q");
		}

		return true;
	}

	public void updateMessage(DAVItem item, String location) {
		//TODO: updateMessage implementieren (PROPPATCH)
	}

	public List getIDsAndVersions(String folder) throws MalformedURLException {
		CacheManager cacheManager = CacheManager.getInstance();

		if (online) {
			try {
				ArrayList idvList = connection.getIDsAndVersions(folder);
				if (idvList != null) {
					//System.out.println("idv list size ="+idvList.size());

					//TODO: modify this so it needs only one iteration
					List updateList =
						cacheManager.checkMessageVersion(folder, idvList, false);
					List removeList =
						cacheManager.checkMessageVersion(folder, idvList, true);

					boolean hasChanges = false;

					//System.out.println("updates found: "+(updateList != null));

					if (updateList != null) {
						hasChanges = true;
						//System.out.println("update list size = "+updateList.size());

						//for RAM optimation
						//split update list into part of 64 max elements
						ArrayList updateLists = new ArrayList();
						if (updateList.size() <= 64) {
							updateLists.add(updateList);
						}
						else {
							Iterator updateItemIterator = updateList.iterator();
							int count = 0;
							ArrayList currentList = new ArrayList();
							while (updateItemIterator.hasNext()) {
								currentList.add(updateItemIterator.next());
								count++;

								if (count >= 63) {
									//System.out.println("creating new part");
									updateLists.add(currentList);
									currentList = new ArrayList();
									count = 0;
								}
							}
						}

						updateList = null;
						//Seems to have no effect, but why not trying it ;)
						System.gc();
						//System.out.println("got " + updateLists.size() + " parts");

						Iterator listIterator = updateLists.iterator();
						while (listIterator.hasNext()) {
							ArrayList list = (ArrayList) listIterator.next();

							//update messages
							ArrayList attribs = connection.getItemAttributes(folder, list);

							//System.out.println("writing " + attribs.size() + " elements into cache");

							Iterator itemIterator = attribs.iterator();
							while (itemIterator.hasNext()) {
								try {
									//Store the message into cache
									cacheManager.putMessage((DAVItem) itemIterator.next());
								}
								catch (Exception e) {
								}
							}
						}
					}
					if (removeList != null) {
						hasChanges = true;

						Iterator itemIterator = removeList.iterator();
						while (itemIterator.hasNext()) {
							try {
								IdVersionPair pair = (IdVersionPair) itemIterator.next();
								cacheManager.removeMessage(folder, pair.getID());
							}
							catch (Exception e) {
							}
						}
					}

					if (hasChanges) {
						cacheManager.putIDsAndVersions(folder, idvList);
					}

					return idvList;
				}
			}
			catch (Exception e) {
				e.printStackTrace();
				if (!online)
					return getIDsAndVersions(folder);
			}
		}

		try {
			return cacheManager.getIDsAndVersions(folder);
		}
		catch (CacheManageException e) {
			e.printStackTrace();
		}

		return null;
	}

	public static boolean isOnline() {
		return online;
	}

	public static Proxy getInstance() {
		return selfInstance;
	}

	public List listFolderItems(String folder) {
		List folderItems = null;
		try {
			if (online) {
				try {
					folderItems = connection.listFolderItems(folder);
				}
				catch (Exception e) {
					if (!online)
						return listFolderItems(folder);
				}
			}
			else
				folderItems = CacheManager.getInstance().listFolderItems(folder);
		}
		catch (Exception e) {
		}

		if (folderItems != null && !folderItems.isEmpty()) {
			ArrayList items = new ArrayList();
			Iterator itemsIterator = folderItems.iterator();
			while (itemsIterator.hasNext()) {
				try {
					DAVItem item = (DAVItem) itemsIterator.next();
					item.setLocation(patchLocation(item.getLocation()));
					items.add(item);
				}
				catch (Exception e) {
				}
			}
			return items;
		}

		return null;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getUrl() {
		return url;
	}

	public HttpResponse loopThrough(
		String uri,
		String method,
		Properties header,
		String body) {

		return connection.loopThrough(uri, method, header, body);
	}

	/**Adds the specified proxy state listener to receive 
	 * events if the proxy changes it's online state.
	 * 
	 * @param listener the listener to register
	 */
	public static void addProxyStateLister(ProxyStateListener listener) {
		if (listener == null)
			return;

		if (listeners == null)
			listeners = new EventListenerList();
		listeners.add(ProxyStateListener.class, listener);
	}

	/**Removes a previously registered ProxyStateListener.
	 * 
	 * @param listener the listener to remove
	 */
	public static void removeProxyStateLister(ProxyStateListener listener) {
		if (listener == null || listeners == null)
			return;

		listeners.remove(ProxyStateListener.class, listener);
	}
}
