/*
 * Copyright (c) 2005- Shinji Kashihara.
 * All rights reserved. This program are made available under
 * the terms of the Eclipse Public License v1.0 which accompanies
 * this distribution, and is available at epl-v10.html.
 */
package jp.sourceforge.mergedoc.pleiades.generator.nls;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Map;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import jp.sourceforge.mergedoc.pleiades.log.Logger;
import jp.sourceforge.mergedoc.pleiades.resource.HelpHtmlParser;
import jp.sourceforge.mergedoc.pleiades.resource.Property;
import jp.sourceforge.mergedoc.pleiades.resource.PropertySet;
import jp.sourceforge.mergedoc.pleiades.resource.HelpHtmlParser.HtmlFragment;

import org.apache.commons.io.IOUtils;
import org.xml.sax.helpers.DefaultHandler;

/**
 * ヘルプ・ドキュメントを持つプラグイン・リソースを保持するクラスです。
 * <p>
 * @author cypher256
 */
public class HelpPlugin extends Plugin {

	/** ロガー */
	private static final Logger log = Logger.getLogger(HelpPlugin.class);
	
	/** HTML フラグメント・リスト・ユニット */
	private HtmlFragmentListUnit htmlListUnit;

	/**
	 * ヘルプ・プラグインを構築します。
	 * <p>
	 * @param pluginFile プラグイン・フォルダーまたはファイル
	 */
	public HelpPlugin(File pluginFile) {
		super(pluginFile);
	}
	
	/**
	 * HTML フラグメント・リスト・ユニットを取得します。
	 * @return HTML フラグメント・リスト・ユニット
	 */
	public HtmlFragmentListUnit getHtmlListUnit() {
		if (htmlListUnit == null) {
			htmlListUnit = new HtmlFragmentListUnit();
		}
		return htmlListUnit;
	}
	
	@Override
	protected void setupProperties(String path, Object in) {
		
		if (path.contains("/nl/") && !path.contains("/nl/ja/")) {
			return;
		}
		
		// toc.xml や topic*.xml などのヘルプ目次 XML を処理
		if (path.endsWith(".xml") &&
			!path.matches(".*?/(plugin.xml|feature.xml|fragment.xml)")) {
			
			if (path.contains("/nl/ja/")) {
				processTocXml(getJaPropertySet(), in, path);
			} else {
				processTocXml(getEnPropertySet(), in, path);
			}
		}
		
		// html ヘルプ・コンテンツを処理
		else if (
			path.matches(".*?\\.(html|htm)") &&
			!path.matches(".+/javadoc/.+") &&
			!path.matches(".+/reference/api/.+") &&
			!path.matches(".+/reference/osgi/.+") &&
			!path.matches(".+/doc.zip/api/.+") &&
			!path.matches(".+\\.birt\\..+")) {
			
			if (path.contains("/nl/ja/")) {
				processDocHtml(getHtmlListUnit().jaHtmlMap, in, path);
			} else {
				processDocHtml(getHtmlListUnit().enHtmlMap, in, path);
			}
		}
	}
	
	/**
	 * toc.xml や topic*.xml などのヘルプ目次 XML を処理します。
	 * <p>
	 * @param resultProp 結果を格納するプロパティー
	 * @param in ヘルプ目次 XML ファイルまたは入力ストリーム
	 * @param path パス
	 */
	private void processTocXml(PropertySet resultProp, Object in, String path) {
		
		// toc の label 属性からプロパティーを作成 (キー：href 属性、値：文)
		PropertySet tocProp = new PropertySet();
		
		try {
		    SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
		    DefaultHandler handler = new HelpTocHandler(tocProp);
			if (in instanceof File) {
				
			    parser.parse((File) in, handler);
			    
			} else if (in instanceof InputStream) {
				
				// SAXParser で is がクローズされるため ByteArrayInputStream に変換
				InputStream is = new ByteArrayInputStream(IOUtils.toByteArray((InputStream) in));
			    parser.parse(is, handler);
			    
			} else {
				
				throw new IllegalArgumentException(
					"予期しない型：" + in.getClass().getName() + " " + path);
			}
			
		} catch (Exception e) {
			
			String msg = "ヘルプ xml のロードに失敗しました。" + path + " " + e;
			log.warn(msg);
		}
		
		// 結果プロパティー作成 (キーの先頭にファイル名を付加)
		String keyPrefix = getLocation(path) + "@";
		for (Property p : tocProp) {
			
			String key = keyPrefix + p.key;
			if (resultProp.containsKey(key)) {
				throw new IllegalStateException("同一キー:" + key);
			}
			resultProp.put(key, p.value);
		}
	}
	
	/**
	 * doc.zip 内の html ヘルプ・コンテンツを処理します。
	 * <p>
	 * @param prop 結果を格納するプロパティー
	 * @param in ヘルプ目次 XML ファイルまたは入力ストリーム
	 * @param path パス
	 */
	private void processDocHtml(Map<String, HtmlFragmentList> htmlMap, Object in, String path) {
		
		String key = getLocation(path);
		if (htmlMap.containsKey(key)) {
			throw new IllegalStateException("同一キー:" + key);
		}
		
		HtmlFragmentList list = new HtmlFragmentList(path);
		htmlMap.put(key, list);
		
		InputStream is = null;
		try {
			if (in instanceof InputStream) {
				is = (InputStream) in;
			} else if (in instanceof File) {
				is = new FileInputStream((File) in);
			} else {
				throw new IllegalArgumentException(
					"予期しない型：" + in.getClass().getName() + " " + path);
			}
			for (HtmlFragment f : new HelpHtmlParser(is)) {
				list.add(f);
			}

		} catch (Exception e) {
			
			String msg = "ヘルプ html のロードに失敗しました。" + path + " " + e;
			log.warn(msg);
			
		} finally {
			if (in instanceof File) {
				IOUtils.closeQuietly(is);
			}
		}
		log.debug("%3d、%-40s - %s", list.size(), key, path);
	}
	
	/**
	 * パス文字列からロケーションを取得します。
	 * @param path パス
	 * @return ロケーション
	 */
	private String getLocation(String path) {
		
		return path
			.replaceFirst("^[^/]+", "")
			.replace("/nl/ja/", "/")
			.replaceFirst("^/doc(|\\.zip)/", "/");
	}
}
