package dareka.processor.util;

import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import dareka.processor.URLResource;
import dareka.processor.URLResourceCache;
import dareka.processor.impl.Cache;
import dareka.processor.impl.NLShared;
import dareka.processor.impl.NicoApiUtil;
import dareka.processor.impl.NicoCachingTitleRetriever;

public class GetThumbInfoUtil {
	
	private static URLResourceCache getthumbinfoCache =
		new URLResourceCache("cacheGetThumbInfoMax", "cacheGetThumbInfoExpire");
	
	private static final Pattern VIDEO_ID_PATTERN = Pattern.compile(
			"<video_id>([a-z]{2}(\\d+))</video_id>");
	private static final Pattern THUMBNAIL_URL_PATTERN = Pattern.compile(
			"<thumbnail_url>.+/smile\\?i=(\\d+)</thumbnail_url>");
	private static final NLShared NLSHARED = NLShared.getInstance();
	
	/**
	 * api/getthumbinfoを呼び出した結果のXML文書からsmidを取得する。
	 * @param content XML文書文字列
	 * @return 取得したsmid文字列、存在しなければnull
	 */
	public static String getSmid(String content) {
		if (content != null) {
			Matcher m = VIDEO_ID_PATTERN.matcher(content);
			if (m.find()) {
				String smid = m.group(1), id = m.group(2);
				// キャッシュが存在してかつサムネIDと異なるなら記録しておく
				if (Cache.getPreferredCachedVideo(smid) != null) {
					m = THUMBNAIL_URL_PATTERN.matcher(content);
					if (m.find() && !m.group(1).equals(id)) {
						NLSHARED.put_vid2cid(m.group(1), id);
					}
				}
				return smid;
			}
		}
		return null;
	}

	/**
	 * api/getthumbinfoを呼び出した結果のXML文書からタイトルを取得する。
	 * @param content XML文書文字列
	 * @return 取得したタイトル文字列、存在しなければnull
	 */
	public static String getTitle(String content) {
		if (content != null) {
			return NicoCachingTitleRetriever.getTitleFromXml(content);
		}
		return null;
	}

	/**
	 * api/getthumbinfoを呼び出して結果のXML文書を取得する。
	 * 既にキャッシュにあればそちらの結果を返す。
	 * また、smidが取得出来た場合はタイトルキャッシュを更新する。
	 * 
	 * @param id 取得するID文字列(smid or threadId)
	 * @return 取得したXML文書文字列(Rewriter処理済み)、失敗したらnull
	 * @throws IOException
	 */
	public static String get(String id) throws IOException {
		URLResource r = getURLResource(id);
		if (r == null) {
			return null;
		}
		byte[] responseBody = r.getResponseBody();
		if (responseBody == null) {
			return null;
		}
		String content = new String(responseBody, "UTF-8");
//		String content = Main.getRewriterProcessor().stringRewriter(
//				NicoApiUtil.getThumbURL("", id),
//				new String(responseBody, "UTF-8"),
//				null, r.getResponseHeader(null, null));
// TODO Rewriter内から呼ばれると無限ループに陥る可能性あり、要検討
		String smid = getSmid(content);
		if (smid != null) {
			if (id.matches("\\d+")) {
				NLSHARED.putThread2Smid(id, smid);
			}
			NicoCachingTitleRetriever.putTitleCache(smid, getTitle(content));
		}
		return content;
	}

	/**
	 * リトライつき{@link #get(String) get}
	 * 
	 * @param id 取得するID文字列(smid or threadId)
	 * @param num リトライ回数
	 * @param ms リトライ間隔(ミリ秒)
	 * @return 取得したXML文書文字列、失敗したらnull
	 * @throws IOException
	 */
	public static String getWithRetry(String id, int num, long ms)
			throws IOException {
		if (!isValidSmid(id) && !isValidThreadId(id)) return null;
		String content = null;
		for (int i = 0; i < num; i++) {
			content = get(id);
			if (content != null) break;
			try {
				Thread.sleep(ms);
			} catch (InterruptedException e) { }
		}
		return content;
	}

	/**
	 * api/getthumbinfoを呼び出して受信済の
	 * {@link dareka.processor.URLResource URLResource} を取得する。
	 * 既にキャッシュにあればそちらの結果を返す。なお、こちらは
	 * {@link #get(String)} とは異なりタイトルキャッシュは更新しない。
	 * 
	 * @param id 取得するID文字列(smid or threadId)
	 * @return 取得したURLResource、受信できなければnull
	 */
	public static URLResource getURLResource(String id) {
		if (!isValidSmid(id) && !isValidThreadId(id)) return null;
		String url = NicoApiUtil.getThumbURL("", id);
		return getthumbinfoCache.cacheAndGet(id, url);
	}

	/**
	 * XML文書がキャッシュされているか？
	 * 
	 * @param id 対象のID文字列(smid or threadId)
	 * @return キャッシュされていればtrue
	 * @since NicoCache_nl+110706mod
	 */
	public static boolean isCached(String id) {
		return getthumbinfoCache.isCached(id);
	}

	/**
	 * 個々のapi/getthumbinfoキャッシュ内容について古ければ破棄する。
	 */
	public static void expires() {
		getthumbinfoCache.expires();
	}

	/**
	 * 特定のapi/getthumbinfoキャッシュを削除する。
	 * 
	 * @param id 削除するapi/getthumbinfoキャッシュのID文字列
	 */
	public static void remove(String id) {
		getthumbinfoCache.remove(id);
	}

    private static final Pattern SMID_PATTERN = Pattern.compile("^\\w{2}\\d+$");
    private static final Pattern THREADID_PATTERN = Pattern.compile("^\\d+$");
    
    private static boolean isValidSmid(String id) {
        Matcher m = SMID_PATTERN.matcher(id);
        return m.matches();
    }

    private static boolean isValidThreadId(String id) {
        Matcher m = THREADID_PATTERN.matcher(id);
        return m.matches();
    }
}
