/*
 * blanco Framework
 * Copyright (C) 2004-2008 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.svnconf;

import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import blanco.commons.util.BlancoStringUtil;
import blanco.svnconf.message.BlancoSvnConfMessage;
import blanco.svnconf.valueobject.BlancoSvnConfStructure;
import blanco.svnconf.valueobject.BlancoSvnConfUserStructure;
import blanco.xml.bind.BlancoXmlBindingUtil;
import blanco.xml.bind.BlancoXmlUnmarshaller;
import blanco.xml.bind.valueobject.BlancoXmlDocument;
import blanco.xml.bind.valueobject.BlancoXmlElement;

/**
 * 「Subversionリポジトリ定義書」Excel様式から情報を抽出します。
 * 
 * このクラスは、中間XMLファイルから構造体情報を抽出する機能を担います。
 * 
 * @author IGA Tosiki
 */
public class BlancoSvnConfParser {
    /**
     * メッセージ定義。
     */
    protected final BlancoSvnConfMessage fMsg = new BlancoSvnConfMessage();

    /**
     * 中間XMLファイルのXMLドキュメントをパースして、情報の配列を取得します。
     * 
     * @param argMetaXmlSourceFile
     *            中間XMLファイル。
     * @return パースの結果得られた情報の配列。
     */
    public BlancoSvnConfStructure[] parse(final File argMetaXmlSourceFile) {
        final BlancoXmlDocument documentMeta = new BlancoXmlUnmarshaller()
                .unmarshal(argMetaXmlSourceFile);
        if (documentMeta == null) {
            return null;
        }

        return parse(documentMeta);
    }

    /**
     * 中間XMLファイル形式のXMLドキュメントをパースして、バリューオブジェクト情報の配列を取得します。
     * 
     * @param argXmlDocument
     *            中間XMLファイルのXMLドキュメント。
     * @return パースの結果得られたバリューオブジェクト情報の配列。
     */
    public BlancoSvnConfStructure[] parse(final BlancoXmlDocument argXmlDocument) {
        final List<BlancoSvnConfStructure> listStructure = new ArrayList<BlancoSvnConfStructure>();
        // ルートエレメントを取得します。
        final BlancoXmlElement elementRoot = BlancoXmlBindingUtil
                .getDocumentElement(argXmlDocument);
        if (elementRoot == null) {
            // ルートエレメントが無い場合には処理中断します。
            return null;
        }

        // sheet(Excelシート)のリストを取得します。
        final List<BlancoXmlElement> listSheet = BlancoXmlBindingUtil
                .getElementsByTagName(elementRoot, "sheet");
        final int sizeListSheet = listSheet.size();
        for (int index = 0; index < sizeListSheet; index++) {
            final BlancoXmlElement elementSheet = (BlancoXmlElement) listSheet
                    .get(index);

            final BlancoSvnConfStructure structure = parseElementSheet(elementSheet);
            if (structure != null) {
                // 得られた情報を記憶します。
                listStructure.add(structure);
            }
        }

        final BlancoSvnConfStructure[] result = new BlancoSvnConfStructure[listStructure
                .size()];
        listStructure.toArray(result);
        return result;
    }

    /**
     * 中間XMLファイル形式の「sheet」XMLエレメントをパースして、バリューオブジェクト情報を取得します。
     * 
     * @param argElementSheet
     *            中間XMLファイルの「sheet」XMLエレメント。
     * @return パースの結果得られたバリューオブジェクト情報。「name」が見つからなかった場合には nullを戻します。
     */
    public BlancoSvnConfStructure parseElementSheet(
            final BlancoXmlElement argElementSheet) {
        final BlancoSvnConfStructure structure = new BlancoSvnConfStructure();
        // 入力パラメータ情報を取得します。

        final List<BlancoXmlElement> listCommon = BlancoXmlBindingUtil
                .getElementsByTagName(argElementSheet, "blancosvnconf-common");
        if (listCommon.size() == 0) {
            // commonが無い場合にはスキップします。
            return null;
        }

        // 最初のアイテムのみ処理しています。
        final BlancoXmlElement elementCommon = (BlancoXmlElement) listCommon
                .get(0);

        // シートから詳細な情報を取得します。
        structure.setName(BlancoXmlBindingUtil.getTextContent(elementCommon,
                "name"));
        if (BlancoStringUtil.null2Blank(structure.getName()).length() == 0) {
            return null;
        }

        structure.setDescription(BlancoXmlBindingUtil.getTextContent(
                elementCommon, "description"));
        structure.setHostname(BlancoXmlBindingUtil.getTextContent(
                elementCommon, "hostname"));
        structure.setBaseDirectory(BlancoXmlBindingUtil.getTextContent(
                elementCommon, "baseDirectory"));
        structure.setAdministratorName(BlancoXmlBindingUtil.getTextContent(
                elementCommon, "administratorName"));
        structure.setValidityPeriodBegin(parseDate(BlancoXmlBindingUtil
                .getTextContent(elementCommon, "validityPeriodBegin")));
        structure.setValidityPeriodEnd(parseDate(BlancoXmlBindingUtil
                .getTextContent(elementCommon, "validityPeriodEnd")));
        structure.setType(BlancoXmlBindingUtil.getTextContent(elementCommon,
                "type"));
        structure.setAnonymousAuth(BlancoXmlBindingUtil.getTextContent(
                elementCommon, "anonymousAuth"));

        if (BlancoStringUtil.null2Blank(structure.getHostname()).length() == 0) {
            throw new IllegalArgumentException(fMsg.getMbsvnc01(structure
                    .getName()));
        }
        if (BlancoStringUtil.null2Blank(structure.getBaseDirectory()).length() == 0) {
            throw new IllegalArgumentException(fMsg.getMbsvnc02(structure
                    .getName()));
        }
        if (structure.getBaseDirectory().endsWith("/")
                || structure.getBaseDirectory().endsWith("\\")) {
            throw new IllegalArgumentException(fMsg.getMbsvnc06(structure
                    .getName(), structure.getBaseDirectory()));
        }
        if (BlancoStringUtil.null2Blank(structure.getAdministratorName())
                .length() == 0) {
            throw new IllegalArgumentException(fMsg.getMbsvnc08(structure
                    .getName()));
        }

        final BlancoXmlElement elementUserList = BlancoXmlBindingUtil
                .getElement(argElementSheet, "blancosvnconf-list");
        if (elementUserList == null) {
            return null;
        }

        // 一覧の内容を取得します。
        final List<BlancoXmlElement> listField = BlancoXmlBindingUtil
                .getElementsByTagName(elementUserList, "user");
        for (int indexField = 0; indexField < listField.size(); indexField++) {
            final Object nodeField = listField.get(indexField);
            if (nodeField instanceof BlancoXmlElement == false) {
                continue;
            }

            final BlancoXmlElement elementField = (BlancoXmlElement) nodeField;

            final BlancoSvnConfUserStructure userStructure = new BlancoSvnConfUserStructure();

            userStructure.setNo(BlancoXmlBindingUtil.getTextContent(
                    elementField, "no"));
            userStructure.setName(BlancoXmlBindingUtil.getTextContent(
                    elementField, "name"));
            if (BlancoStringUtil.null2Blank(userStructure.getName()).length() == 0) {
                // nameが指定されていない場合には処理しません。
                continue;
            }

            userStructure.setAuth(BlancoXmlBindingUtil.getTextContent(
                    elementField, "auth"));
            userStructure.setDescription(BlancoXmlBindingUtil.getTextContent(
                    elementField, "description"));

            // 既に同じ内容が登録されていないかどうかのチェック。
            for (int indexPast = 0; indexPast < structure.getUserList().size(); indexPast++) {
                final BlancoSvnConfUserStructure fieldPast = (BlancoSvnConfUserStructure) structure
                        .getUserList().get(indexPast);
                if (fieldPast.getName().equals(userStructure.getName())) {
                    throw new IllegalArgumentException(fMsg.getMbsvnc03(
                            structure.getName(), userStructure.getName()));
                }
            }

            if (userStructure.getName().indexOf('*') >= 0) {
                throw new IllegalArgumentException(fMsg.getMbsvnc05(structure
                        .getName(), userStructure.getName()));
            }
            if (BlancoStringUtil.null2Blank(userStructure.getAuth()).length() == 0) {
                throw new IllegalArgumentException(fMsg.getMbsvnc04(structure
                        .getName(), userStructure.getName()));
            }

            structure.getUserList().add(userStructure);
        }

        return structure;
    }

    /**
     * 日付文字列をパースします。
     * 
     * @param arg
     * @return
     */
    private Date parseDate(final String arg) {
        if (arg == null) {
            return null;
        }
        try {
            final SimpleDateFormat sdFormat = new SimpleDateFormat("yyyy/MM/dd");
            return sdFormat.parse(arg);
        } catch (ParseException e) {
            // TODO 例外処理の実装。
            e.printStackTrace();
            return null;
        }
    }
}
