package com.interpress_project.modernshare.svnserver;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;

import com.interpress_project.modernshare.ipcommon.SystemBase;
import com.interpress_project.modernshare.model.PropertyManager;
import com.interpress_project.modernshare.serveradm.CommandExecutor;
import com.interpress_project.modernshare.serveradm.exceptions.CommandExecutorException;
import com.interpress_project.modernshare.serveradm.exceptions.NoSVNInstalledException;

public class SVNServeManager extends Thread {
	private PropertyManager propmgr;
	private final SystemBase sb = SystemBase.getInstance();
	private final String pidfile = "./pid";
	private String rootdir = null, port = null, svnserve = null;

	/**
	 * SVNServeManager
	 */
	public SVNServeManager() {
		propmgr = PropertyManager.getInstance();
	}

	/**
	 * run
	 */
	public void run() {
		sb.getLogger().debug("Enter start().");

		// Initialize all stuffs.
		init();

		// Check if the svnserve is already running on the host.
		if (isSVNServeRunning()) {
			// Kill it.
			sb.getLogger().warn("svnserve already may run, so it get to be terminated forcely.");
			killSVNServeCommand();
			sb.getLogger().warn("svnserver is terminated.");
		}

		// Check if the port is occupancy.
		if (isPortOccupancy()) {
			sb.getLogger().fatal("Port " + port + " is occupancy by other process. Please make it vacancy.");
			sb.getLogger().fatal("Server is forcely terminated.");
			System.exit(-1);
		}
		// Boot it up.
		startupSVNServeCommand();
		sb.getLogger().info("svnserver started.");
	}

	/**
	 * init
	 */
	private void init() {
		try {
			svnserve = propmgr.getSVNServerCommand();
			rootdir = propmgr.getSVNRootdir(); // この段階でsvnrootdirの存在確認は済んでいる。
			port = propmgr.getSVNServerCommandPort();
		}
		catch (NoSVNInstalledException ex) {
			sb.getLogger().fatal(ex.getMessage(), ex);
			System.exit(-1);
		}
	}

	/**
	 * isSVNServeRunning
	 * @return
	 */
	private boolean isSVNServeRunning() {
		File file = new File(pidfile);
		if (file.exists()) {
			return true;
		}
		return false;
	}

	/**
	 * killSVNServeCommand
	 */
	private void killSVNServeCommand() {
		String targetport = null;

		File file = new File(pidfile);
		FileInputStream fis = null;
		BufferedReader br = null;

		try {
			fis = new FileInputStream(file);
		}
		catch (FileNotFoundException ex) {
			return;
		}

		try {
			br = new BufferedReader(new InputStreamReader(fis, "US-ASCII"));
		}
		catch (UnsupportedEncodingException ex) {
			sb.getLogger().fatal(ex);
			sb.getLogger().fatal("Server is forcely terminated.");
			System.exit(-1);
		}

		try {
			targetport = br.readLine();
		}
		catch (IOException ex) {
			sb.getLogger().fatal("IOException reading a line in pidfile, " + pidfile + " : ", ex);
			sb.getLogger().fatal("Server is forcely terminated.");
			System.exit(-1);
		}
		finally {
			try {
				br.close();
			}
			catch (IOException ex) {
			}
		}

		try {
			// Validation of port number.
			Integer.valueOf(targetport.trim());
		}
		catch (NumberFormatException ex) {
			sb.getLogger().fatal("Invalid port number," + targetport + " found in " + pidfile);
			sb.getLogger().fatal("Server is forcely terminated.");
			System.exit(-1);
		}

		ArrayList<String> list = new ArrayList<String>();
		CommandExecutor command = new CommandExecutor(true);
		list.add("kill");
		list.add(targetport.trim());

		try {
			command.execute(list);
		}
		catch (CommandExecutorException ex) {
			sb.getLogger().fatal("Exception executing command: " + list.toString(), ex);
			sb.getLogger().fatal("Server is forcely terminated.");
			System.exit(-1);
		}

		file.delete(); // clean up old pidfile.
	}

	/**
	 * isPortOccupancy
	 * @return
	 */
	private boolean isPortOccupancy() {
		Socket socket = new Socket();
		InetSocketAddress endpoint = new InetSocketAddress("127.0.0.1", Integer.parseInt(port));
		try {
			socket.connect(endpoint);
			return true;
		}
		catch (ConnectException ex) {
		}
		catch (IOException ex) {
			sb.getLogger().fatal("Exception connecting with socket. Server is forcely terminated. ", ex);
			System.exit(-1);
		}
		finally {
			try {
				socket.close();
			}
			catch (IOException ex) {
			}
		}
		return false;
	}

	/**
	 * startupSVNServeCommand
	 */
	private void startupSVNServeCommand() {
		ArrayList<String> list = new ArrayList<String>();
		//  svnserve + " " + "-i" + " " + "--root" + " " + rootdir;
		CommandExecutor command = new CommandExecutor(false);
		list.add(svnserve);
		list.add("-d");
		list.add("--pid-file");
		list.add(pidfile);
		list.add("--listen-host");
		list.add("127.0.0.1");
		list.add("--listen-port");
		list.add(port);
		list.add("--root");
		list.add(rootdir);

		try {
			command.execute(list);
		}
		catch (CommandExecutorException ex) {
			sb.getLogger().fatal("Server is forcely terminated.", ex);
			System.exit(-1);
		}
	}

	/**
	 * dispose
	 */
	public void dispose() {
		sb.getLogger().debug("Enter dispose()");
		killSVNServeCommand();
	}
}
