package com.interpress_project.modernshare.client.controller.delegate;

import java.io.File;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Date;

import org.tigris.subversion.svnclientadapter.ISVNDirEntry;
import org.tigris.subversion.svnclientadapter.ISVNInfo;
import org.tigris.subversion.svnclientadapter.SVNClientException;
import org.tigris.subversion.svnclientadapter.SVNNodeKind;
import org.tigris.subversion.svnclientadapter.SVNRevision;
import org.tigris.subversion.svnclientadapter.SVNUrl;

import com.interpress_project.modernshare.client.controller.delegate.exceptions.InternalFailException;
import com.interpress_project.modernshare.client.controller.model.ISVNFileItem;
import com.interpress_project.modernshare.client.controller.model.SVNCacheManager;
import com.interpress_project.modernshare.client.controller.model.SVNFile;
import com.interpress_project.modernshare.client.controller.model.SVNFolder;

/**
 * Represents a singleton.
 * @stereotype Singleton factory
 */
public class RemoteSVNManager extends ISVNManager {
	/**
	 * Holds singleton instance
	 */
	private static RemoteSVNManager instance;

	private SVNCacheManager cachemgr = new SVNCacheManager();

	/**
	 * RemoteSVNManager
	 */
	private RemoteSVNManager() {
	}

	/**
	 * Returns the singleton instance.
	 @return	the singleton instance
	 */
	static public RemoteSVNManager getInstance() {
		if (instance == null) {
			instance = new RemoteSVNManager();
		}
		return instance;
	}

	/**
	 * getSVNFileItems
	 * @param url
	 * @return
	 * @throws InternalFailException
	 */
	public ArrayList<ISVNFileItem> getSVNFileItems(String url) throws InternalFailException {
		ArrayList<ISVNFileItem> list = new ArrayList<ISVNFileItem>();
		SVNUrl svnurl = createSVNUrl(url);
		ISVNDirEntry[] entry = null;

		try {
			entry = svnClient.getList(svnurl, SVNRevision.HEAD, false);
		}
		catch (SVNClientException ex) {
			throw new InternalFailException(url + "に対するリスト取得に失敗しました。(" + ex.getMessage() + ")");
		}
		for (int i = 0; i < entry.length; i++) {
			ISVNDirEntry e = entry[i];
			if (e.getNodeKind() == SVNNodeKind.DIR) {
				list
				    .add(new SVNFolder(e.getPath(), e.getSize(), e.getLastCommitAuthor(), e.getLastChangedDate(), e
				        .getLastChangedRevision().getNumber(), e.getLastChangedRevision().getNumber(), url + "/" + e.getPath(),
				      ""));
			}
			else {
				list
				    .add(new SVNFile(e.getPath(), e.getSize(), e.getLastCommitAuthor(), e.getLastChangedDate(), e
				        .getLastChangedRevision().getNumber(), e.getLastChangedRevision().getNumber(), url + "/" + e.getPath(),
				      ""));
			}
		}
		return list;
	}

	/**
	 * getRevisionInfo
	 * @param url
	 * @return
	 * @throws InternalFailException
	 */
	public ArrayList<ISVNFileItem> getRevisionInfo(String url) throws InternalFailException {
		return _getRevisionInfo(url);
	}

	/**
	 * _getRevisionInfo
	 * @param url
	 * @return
	 * @throws InternalFailException
	 */
	private ArrayList<ISVNFileItem> _getRevisionInfo(String url) throws InternalFailException {
		SVNUrl svnurl = createSVNUrl(url);
		ISVNInfo info = null;
		String origurl = null;

		try {
			info = svnClient.getInfo(svnurl, SVNRevision.HEAD, SVNRevision.HEAD);
		}
		catch (SVNClientException ex) {
			throw new InternalFailException(url + "の情報取得(getInfo())が失敗しました。通信エラーかデータセンタにデータが存在していない可能性があります。\n"
			    + "リフレッシュをしてリスト情報を更新してください。", ex);
		}

		origurl = info.getUrlString();

		ISVNFileItem item = null;
		do {
			item = createSVNFileItem(info);
			/**
			 * RevisionとLastChgRevisionが同じものだけを表示することとする。なお、SVNRevnsion.PREVIOUSを
			 * 利用すると処理がエラーとなるので直接番号指定を利用する。	
			 */
			long rv = item.getRevision();
			long lcrv = item.getLastChangedRevision();
			// 同じsvnurlで取得すると異なるURLが返ることがある。これは不整合リソース修正後に見られる。
			// svn://localhost:111/yugos/1/test と svn://localhost:111/yugos/1/test.orig.1101121 など
			// そのため必ずURLが同じであるかを確認して、同じ場合だけリストに追加すること。
			if (!origurl.equals(item.getURL())) { // 異なるから飛ばすこと
				--rv;
			}
			else {
				if (rv == lcrv) {
					// Check if it exist in the cache.
					if (cachemgr.isCached(item)) {
						sb.getLogger().debug("Cache hit: " + item.getURL());
						return cachemgr.getSVNFileItems(item.getURL());
					}
					cachemgr.addSVNFileItem(item);
					--rv;
				}
				else {
					rv = lcrv;
				}
			}
			try {
				removeNotifyListener();
				SVNRevision r = new SVNRevision.Number(rv);
				info = svnClient.getInfo(svnurl, r, SVNRevision.HEAD);
			}
			catch (SVNClientException e) {
				break; // これ以上のリビジョンが存在しない場合
			}
			finally {
				addNotifyListener();
			}
		}
		while (info != null);

		ArrayList<ISVNFileItem> result = cachemgr.getSVNFileItems(origurl);
		return result != null ? result : new ArrayList<ISVNFileItem>();
	}

	/**
	 * createSVNFileItem
	 * @param info
	 * @return
	 */
	private ISVNFileItem createSVNFileItem(ISVNInfo info) {
		ISVNFileItem item = null;
		String lpath = info.getUrl().getLastPathSegment();
		String author = info.getLastCommitAuthor();
		Date lcdate = info.getLastChangedDate();
		long rv = info.getRevision().getNumber();
		long lcrv = info.getLastChangedRevision().getNumber();
		String u = info.getUrlString();
		String uuid = info.getUuid();
		if (SVNNodeKind.DIR == info.getNodeKind()) {
			item = new SVNFolder(lpath, 0, author, lcdate, rv, lcrv, u, uuid);
		}
		else {
			item = new SVNFile(lpath, 0, author, lcdate, rv, lcrv, u, uuid);
		}
		return item;
	}

	/**
	 * deleteRemoteItem
	 * @param urls
	 * @throws InternalFailException
	 */
	public void deleteRemoteItem(ISVNFileItem[] items) throws InternalFailException {
		ArrayList<SVNUrl> list = new ArrayList<SVNUrl>();
		try {
			for (int i = 0; i < items.length; i++) {
				list.add(new SVNUrl(items[i].getURL()));
			}
		}
		catch (MalformedURLException ex) {
			throw new InternalFailException(accounturl + "に対するアドレス変換が失敗しました。");
		}
		SVNUrl[] us = list.toArray(new SVNUrl[list.size()]);
		try {
			svnClient.remove(us, new Date().toString());
		}
		catch (SVNClientException ex) {
			throw new InternalFailException("リモートファイルの削除に失敗しました。:" + ex.getMessage());
		}
	}

	/**
	 * exportItem
	 * @param item
	 * @param file
	 * @throws InternalFailException
	 */
	public void exportItem(ISVNFileItem item, File file) throws InternalFailException {
		String url = item.getURL();
		SVNUrl svnurl = createSVNUrl(url);

		long rev = item.getRevision();
		SVNRevision revision = new SVNRevision.Number(rev);
		try {
			svnClient.doExport(svnurl, file, revision, true);
		}
		catch (SVNClientException ex) {
			throw new InternalFailException("リモートファイル" + url + "のエキスポートに失敗しました。:" + ex.getMessage());
		}
	}
}
