/*
 * blanco Framework
 * Copyright (C) 2004-2005 IGA Tosiki
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 */
package blanco.doclisting;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import blanco.commons.util.BlancoXmlUtil;

/**
 * 中間XMLファイルを集約してひとつの集約ファイルを作り上げます。
 * 
 * このクラスは電文処理定義書の専用です。内部的にソートをおこないます。
 * 
 * @author IGA Tosiki
 */
public class BlancoDocListingXml2CombinedXmlTelegramProcess {
    /**
     * テンポラリファイルのファイル名パターンを正規表現であらわしたもの。
     */
    private static final String PATTERN = "^.*[.]xml$";

    /**
     * 走査する際のルートエレメント名
     */
    private static final String ROOT_ELEMENT_NAME = "blancotelegramprocess-common";

    /**
     * 走査する際の説明エレメント名(description)
     */
    private static final String ROOT_ELEMENT_NAME_DESCRIPTION = "blancotelegramprocess-description";

    /**
     * キー(ID)を表現するエレメント名。
     * 
     * このエレメント名をキーにソートなどを行います。
     */
    private static final String KEY_ELEMENT_NAME = "telegramProcessId";

    /*
     * ここからは内部的な情報。
     */

    /**
     * デバッグモードで動作しているかどうか。
     */
    private static final boolean IS_DEBUG = false;

    /**
     * 共通情報のリスト。
     */
    private final ArrayList fListCommon = new ArrayList();

    /**
     * 「説明」のマップ。
     */
    private final HashMap fMapDescription = new HashMap();

    /**
     * 入力の中間XMLファイルから一覧の中間XMLファイルを作成します。
     * 
     * @param inputTmpDir
     *            中間XMLファイルが格納されているtmpフォルダのルート部分。tmpを推奨。
     * @param outputTmpDir
     *            出力先となるフォルダ。tmp/doclisting/ を推奨。
     */
    public void process(final File inputTmpDir, final File outputTmpDir) {
        if (outputTmpDir.exists() == false) {
            // 出力を行うフォルダが存在しなければこれを作成。
            outputTmpDir.mkdirs();
        }

        parseDirectory(inputTmpDir);

        BlancoDocListingUtil.sortListByKey(KEY_ELEMENT_NAME, fListCommon);

        writeXml(outputTmpDir);
    }

    /**
     * 指定のディレクトリをパースします。
     * 
     * 処理すべきファイルをファイル名でマッチングして処理していきます。
     * 
     * @param sourceListDirectory
     *            入力ディレクトリ。
     */
    private void parseDirectory(final File sourceListDirectory) {
        final File[] list = sourceListDirectory.listFiles();
        if (list == null) {
            // ファイルが一つもありません。
            if (IS_DEBUG) {
                System.out.println("テンポラリフォルダ["
                        + sourceListDirectory.getAbsolutePath()
                        + "]にファイルがひとつもありません");
            }
            return;
        }

        for (int index = 0; index < list.length; index++) {
            Pattern patternCheck = null;
            try {
                patternCheck = Pattern.compile(PATTERN);
            } catch (PatternSyntaxException ex) {
                throw new IllegalArgumentException("ルートエレメント名["
                        + ROOT_ELEMENT_NAME + "]に与えられたパターン[" + PATTERN
                        + "]は不正です。処理中断します");
            }

            final Matcher matcher = patternCheck.matcher(list[index].getName());
            if (matcher.matches()) {
                if (IS_DEBUG) {
                    System.out.println("ファイル名[" + list[index].getName()
                            + "]はパターン[" + PATTERN + "]に一致しました");
                }
                parseXml(list[index], ROOT_ELEMENT_NAME);
            } else {
                if (IS_DEBUG) {
                    System.out.println("ファイル名[" + list[index].getName()
                            + "]はパターン[" + PATTERN + "]に一致しませんでした");
                }
            }
        }
    }

    /**
     * 指定されたファイル(XML)をパースして情報を取得します。
     * 
     * パースの結果としてリストに記憶します。
     * 
     * @param fileRead
     * @param listDocument
     * @param rootElementName
     * @param elementNameList
     */
    private void parseXml(final File fileRead, final String rootElementName) {

        final Element documentElement = BlancoDocListingUtil
                .parseXmlDocument(fileRead);
        if (documentElement == null) {
            if (IS_DEBUG) {
                System.out.println("ルートノードが見つかりません");
            }
            return;
        }
        final NodeList rootNodeList = documentElement.getChildNodes();
        if (rootNodeList == null) {
            return;
        }
        final Element eleRoot = (Element) rootNodeList;

        final NodeList sheetList = eleRoot.getElementsByTagName("sheet");
        if (sheetList == null) {
            if (IS_DEBUG) {
                System.out.println("sheetタグがありません。");
            }
            return;
        }

        final int sizeSheetList = sheetList.getLength();
        for (int indexSheet = 0; indexSheet < sizeSheetList; indexSheet++) {
            final Element eleSheet = (Element) sheetList.item(indexSheet);

            final NodeList listBlancoTelegramCommon = eleSheet
                    .getElementsByTagName(rootElementName);
            if (listBlancoTelegramCommon == null) {
                continue;
            }

            final int sizeListBlancoTelegramCommon = listBlancoTelegramCommon
                    .getLength();
            for (int index = 0; index < sizeListBlancoTelegramCommon; index++) {
                final Node nodeCommon = listBlancoTelegramCommon.item(index);
                if (nodeCommon instanceof Element == false) {
                    continue;
                }
                fListCommon.add(nodeCommon);

                /*
                 * ノード以下の詳細情報サブノードを出力先にコピーします。
                 * 
                 * ここで実施しているのはマップへの記憶のみです。
                 */
                final String keyId = BlancoXmlUtil.getTextContent(
                        (Element) nodeCommon, KEY_ELEMENT_NAME);
                final NodeList listDescription = eleSheet
                        .getElementsByTagName(ROOT_ELEMENT_NAME_DESCRIPTION);
                for (int indexDescription = 0; indexDescription < listDescription
                        .getLength(); indexDescription++) {
                    final Node nodeDescription = listDescription
                            .item(indexDescription);
                    if (nodeDescription instanceof Element == false) {
                        continue;
                    }

                    fMapDescription.put(keyId, nodeDescription);
                }
            }
        }
    }

    /**
     * 集まった情報をXMLファイルへと出力します。
     * 
     * @param outputTmpDir
     *            出力先ディレクトリ。
     */
    private void writeXml(final File outputTmpDir) {
        final Document document = BlancoXmlUtil.newDocument();
        final Element elementWorkbook = document.createElement("workbook");
        document.appendChild(elementWorkbook);
        final Element elementSheet = document.createElement("sheet");
        elementWorkbook.appendChild(elementSheet);
        for (int index2 = 0; index2 < fListCommon.size(); index2++) {
            final Node node = (Node) fListCommon.get(index2);
            final Element subRootElement = document
                    .createElement(ROOT_ELEMENT_NAME);
            elementSheet.appendChild(subRootElement);

            BlancoDocListingUtil.copyNode(node, document, subRootElement);

            // 説明に関するノードを一式コピーします。
            final Element elementDescription = document
                    .createElement(ROOT_ELEMENT_NAME_DESCRIPTION);
            subRootElement.appendChild(elementDescription);

            final String keyElementName = BlancoXmlUtil.getTextContent(
                    (Element) node, KEY_ELEMENT_NAME);
            BlancoDocListingUtil.copyNode((Element) fMapDescription
                    .get(keyElementName), document, elementDescription);
        }
        BlancoXmlUtil.transformDom2File(document, new File(outputTmpDir
                .getAbsolutePath()
                + "/" + ROOT_ELEMENT_NAME + ".combinedxml"));
    }
}
