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.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.positrium.chikarawo.Chikarawo;
import org.positrium.chikarawo.ErrorDialog;

import com.dokukino.genkidama.DHTManager;
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 Log logger = LogFactory.getLog(Main.class);

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

	private static Server server;
	private static Chikarawo gui;

	public static void stop() throws RoutingException {
		logger.info("Shutting down");
		if (server != null) {
			server.stop();
		}
		if (DHTManager.getInstance().isConnected())
			DHTManager.getInstance().disconnect();
	}

	public static void main(String[] args) {
		Runtime.getRuntime().addShutdownHook(new Thread() {
			public void run() {
				try {
					Main.stop();
				} catch (RoutingException e) {
					// TODO Auto-generated catch block
					logger.error("Error Occurs:", e);
				}
			}
		});
		
		gui = new Chikarawo();
		gui.openTray();
		
		try {
			mainBody();
		} catch (Exception e) {
			logger.error("Error Occurs:", e);
			new ErrorDialog("Error", e.toString());
		}
	}

	private static void mainBody() throws Exception {
		// [nl] iniにしたい人用。でも正確にはiniファイルじゃないよ
		File configFile = new File("config.ini");
		if (configFile.exists() == false) {
			configFile = new File("config.properties");
		}

		// 設定ファイルがなくてデフォルト設定ファイルがあるならリネームして使う
		if (configFile.exists() == false) {
			File defFile = new File("config.properties.default");
			if (defFile.exists()) {
				defFile.renameTo(configFile);
			}
		}

		Config config = configure(configFile);

		logger.info(VER_STRING);
		logger.info("port=" + Integer.getInteger("listenPort"));
		if (System.getProperty("proxyHost").equals("")) {
			logger.info("direct mode (no secondary proxy)");
		} else {
			logger.info("proxy host=" + System.getProperty("proxyHost"));
			logger.info("proxy port=" + Integer.getInteger("proxyPort"));
		}
		logger.info("title=" + Boolean.getBoolean("title"));

		if (Boolean.getBoolean("resumeDownload")) {
			logger.info("Resume suspended download On");
		}

		if (Boolean.getBoolean(("touchCache"))) {
			logger.info("Touch Cache File On");
		}

		// if (Boolean.getBoolean("dareka.debug")) {//
		// logger.info("debug mode");
		// }

		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);
		}
		
		DHTManager.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();
		setDefaults(p);

		Config config = new Config();
		if (configFile.exists()) {
			loadFrom(configFile, p);

//			String v;
//
//			v = p.getProperty(Config.LISTEN_PORT);
//			if (v != null) {
//				config.setListenPort(Integer.parseInt(v));
//			}
//
//			v = p.getProperty(Config.PROXY_HOST);
//			if (v != null) {
//				config.setProxyHost(v);
//			}
//
//			v = p.getProperty(Config.PROXY_PORT);
//			if (v != null) {
//				config.setProxyPort(Integer.parseInt(v));
//			}
//
//			v = p.getProperty(Config.TITLE);
//			if (v != null) {
//				config.setTitle(Boolean.parseBoolean(v));
//			}
		} else {
			p
					.setProperty("dhtSecret", RandomStringUtils
							.randomAlphanumeric(40));
			FileOutputStream out = new FileOutputStream(configFile);
			try { // ensure closing out
				p.store(out, "NicoCache config file");
			} finally {
				CloseUtil.close(out);
			}
		}

		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;
	}

	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);
		}
	}

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

		p.setProperty("listenPort", "8080");
		p.setProperty("proxyHost", "");
		p.setProperty("proxyPort", "8081");
		p.setProperty("title", "true");
		p.setProperty("touchCache", "true");
	}

	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);
		}
	}
}
