package dareka;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.lang.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.positrium.ui.GenkidamaSettingsDto;
import org.positrium.ui.NicocacheSettingsDto;
import org.positrium.ui.TrayControl;
import org.seasar.framework.container.SingletonS2Container;
import org.seasar.framework.container.factory.SingletonS2ContainerFactory;

import com.dokukino.genkidama.GenkidamaManager;
import com.dokukino.genkidama.VersionInfo;
import com.dokukino.genkidama.XmlRpcInvoker;

import ow.routing.RoutingException;

import dareka.common.CloseUtil;
import dareka.common.Config;
import dareka.processor.impl.Cache;

public class Main {
	public static Logger logger = LoggerFactory.getLogger(Main.class);

	// public so that external tools can read.
	public static final String VER_STRING = VersionInfo.getFullVersion();

	private static Server server;
	private static TrayControl tray;

	public static void stop(int errorCode) {
		logger.info("Shutting down");
		if (server != null) {
			server.stop();
		}
		if (GenkidamaManager.getInstance().isConnected())
			try {
				GenkidamaManager.getInstance().disconnect();
			} catch (RoutingException e) {
				logger.warn(e.toString());
			}
		System.exit(errorCode);
	}

	public static void main(String[] args) {
		SingletonS2ContainerFactory.init();

		tray = SingletonS2Container.getComponent(TrayControl.class);
		tray.openTray();

		try {
			mainBody();
		} catch (Exception e) {
			logger.error(
					"Error Occurs:",
					e);
			tray.fatalStop(
					"Error",
					e.toString());
		}
	}

	private static void mainBody() throws Exception {
		// // [nl] iniにしたい人用。でも正確にはiniファイルじゃないよ
		// File configFile = new File("config.ini");
		// if (configFile.exists() == false) {
		// File configFile = new File("config.properties");
		// }
		//
		// // 設定ファイルがなくてデフォルト設定ファイルがあるならリネームして使う
		// if (configFile.exists() == false) {
		// File defFile = new File("config.properties.default");
		// if (defFile.exists()) {
		// defFile.renameTo(configFile);
		// }
		// }

		File configFile = new File("config.properties");
		Config config = configure(configFile);

		{
			{
				/* 依存性を解消しない代わりに、部分的にインジェクションを利用しつつSingletonでオブジェクトを得る */
				GenkidamaSettingsDto app = SingletonS2Container.getComponent("genkidamaSettings");
				app.dhtDataPort = Integer.getInteger("dhtDataPort");
				app.dhtRingPort = Integer.getInteger("dhtRingPort");
				app.proxyPac = System.getProperty("dhtProxyPac");

				Object[] param = {
						app.version,
						app.dhtDataPort,
						app.dhtRingPort,
						"http://localhost:" + app.dhtDataPort + "/" + app.proxyPac };

				logger.info(
						app.log_msg_format,
						param);
			}

			{
				NicocacheSettingsDto app = SingletonS2Container.getComponent("nicocacheSettings");
				app.listenPort = Integer.getInteger("listenPort");
				app.proxyHost = System.getProperty("proxyHost");
				app.proxyPort = Integer.getInteger("proxyPort");
				app.isTitleRetrieve = Boolean.getBoolean("title");
				app.isResumeDownload = Boolean.getBoolean("resumeDownload");
				app.isTouchCache = Boolean.getBoolean("touchCache");

				Object[] param = {
						app.listenPort,
						app.proxyHost,
						app.proxyPort,
						app.isTitleRetrieve,
						app.isResumeDownload,
						app.isTouchCache };

				logger.info(
						app.log_msg_format,
						param);
			}

			{
				if (logger.isDebugEnabled()) {
					// The TEST.
					// get orphan object from SingletonS2Container
					GenkidamaSettingsDto app = SingletonS2Container.getComponent("genkidamaSettings");
					logger.debug(app.dhtDataPort.toString());
				}
			}

		}

		{
			/* version check */
			HashMap versionCheckResult = (HashMap) XmlRpcInvoker.invoke(
					"version.check",
					null);
			String versionUpUrl = (String) versionCheckResult.get("version_up_url");
			if ((Integer) versionCheckResult.get("required_version") > VersionInfo.APPLICATION_MINOR_VERSION) {
				logger.info(
						"You need to version up the program in following URL:\n{}",
						versionUpUrl);
				return;
			}
			if ((Integer) versionCheckResult.get("newest_version") > VersionInfo.APPLICATION_MINOR_VERSION) {
				logger.info(
						"New version found:\n{}",
						versionUpUrl);
			}
		}

		GenkidamaManager.getInstance().connect();
		Cache.init();
		// for resume
		// Cache.cleanup();
		logger.info(String.format(
				"total self cache size=%,dbytes\n",
				Long.valueOf(Cache.size())));

		server = new Server(config);

		server.start();
	}

	private static Config configure(File configFile) throws IOException {
		Properties p = new Properties();
		setDefaultsFromFiles(p);

		Config config = new Config();
		if (configFile.exists()) {
			// append configed properties to PropertiesObject
			loadFrom(
					configFile,
					p);

		} else {
			p.setProperty(
					"dhtSecret",
					RandomStringUtils.randomAlphanumeric(40));
			saveTo(
					p,
					configFile);
		}

		for (Map.Entry<Object, Object> entry : p.entrySet()) {
			String key = (String) entry.getKey();
			String value = (String) entry.getValue();
			System.setProperty(
					key,
					value.trim());
		}

		return config;
	}

	/**
	 * load properties from file
	 * 
	 * @param configFile
	 * @param p
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	private static void loadFrom(File configFile, Properties p) throws FileNotFoundException, IOException {
		FileInputStream in = new FileInputStream(configFile);
		try { // ensure closing in
			p.load(in);
		} finally {
			CloseUtil.close(in);
		}
	}

	/**
	 * save properties to file
	 * 
	 * @param p
	 * @param configFile
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	private static void saveTo(Properties p, File configFile) throws FileNotFoundException, IOException {
		FileOutputStream out = new FileOutputStream(configFile);
		try { // ensure closing out
			p.store(
					out,
					"AUTO GENERATED Genkidama CONFIG FILE");
		} finally {
			CloseUtil.close(out);
		}
	}

	@Deprecated
	private static void setDefaults(Properties p) {
		// try {
		// setDefaultsFromFiles(p);
		// if (p.size() > 0) {
		// return;
		// }
		// } catch (IOException e) {
		// logger.debug(e.toString());
		// }
		//
	}

	/**
	 * set default properties to PropertiesObject from defaults/*.properties
	 * 
	 * @param p
	 */
	private static void setDefaultsFromFiles(Properties p) throws IOException {
		File defaultsDir = new File("defaults");

		File[] files = defaultsDir.listFiles(new FilenameFilter() {
			public boolean accept(File dir, String name) {
				if (name.endsWith(".properties")) {
					return true;
				} else {
					return false;
				}
			}
		});

		if (files == null) {
			throw new IOException("failed to read: " + defaultsDir);
		}

		Arrays.sort(files);
		for (File f : files) {
			loadFrom(
					f,
					p);
		}
	}
}
