/*
 * Copyright (c) 2007, Going Dot Com Inc. All rights reserved.
 */

package jp.co.going.xbrl.taxonomy;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import jp.co.going.xbrl.common.XbException;
import jp.co.going.xbrl.common.XbConstants;
import jp.co.going.xbrl.common.parse.XbPAOImpl;

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

/**
 * @author Going Dot Com Inc.
 */
public class XbLinkBase {

    // LinkBaseRef ID
    private long lLinkBaseRef_ID;

    // DAO
    XbLinkbaseRefDAO dbaLinkbaserefDAO;
    XbLinkbaseArcDAO dbaLinkbaseArcDAO;
    XbLinkbaseLocatorDAO dbaLinkbaseLocatorDAO;
    XbLinkbaseResourceDAO dbaLinkbaseResourceDAO;
    XbResourceItemDAO dbaResourceItemDAO;
    XbResourceItemAttrDAO dbaResourceItemAttrDAO;
    XbNextIdDAO arcIdDAO;
    XbNextIdDAO locIdDAO;
    XbNextIdDAO resourceIdDAO;
    XbNextIdDAO resourceItemIdDAO;
    XbNextIdDAO resourceItemAttrIdDAO;

    // コネクト
    Connection conn;

    Map<String, String> linkBaseMap;

    /**
     * コンストラクタ
     * @param           Connection DBコネクション
     * @throws      
     * @see             
     * @return      なし
     */
    protected XbLinkBase(Connection conn) throws XbException {
        this.conn = conn;
        // リンクベースアクセスクラス
        dbaLinkbaserefDAO = XbLinkbaseRefDAO.getInstance(conn);
        // Arcアクセスクラス
        dbaLinkbaseArcDAO = XbLinkbaseArcDAO.getInstance(conn);
        // Locatorアクセスクラス
        dbaLinkbaseLocatorDAO = XbLinkbaseLocatorDAO.getInstance(conn);
        // Resourceアクセスクラス
        dbaLinkbaseResourceDAO = XbLinkbaseResourceDAO.getInstance(conn);
        // Resourceアイテムアクセスクラス
        dbaResourceItemDAO = XbResourceItemDAO.getInstance(conn);
        // Resourceアイテム属性アクセスクラス
        dbaResourceItemAttrDAO = XbResourceItemAttrDAO.getInstance(conn);

        // アークID取得
        arcIdDAO = XbLinkArcNextIdDAO.getInstance(conn);
        // ロケータID取得
        locIdDAO = XbLinkLocNextIdDAO.getInstance(conn);
        // リソースID取得
        resourceIdDAO = XbLinkResourceNextIdDAO.getInstance(conn);
        // リソースアイテムID取得
        resourceItemIdDAO = XbLinkResourceItemNextIdDAO.getInstance(conn);
        // リソースアイテム属性ID取得
        resourceItemAttrIdDAO = XbLinkResourceItemAttrNextIdDAO.getInstance(conn);
   }

    /**
     * リンクベース情報を設定する
     * @param           ArrayList   リンクベースオブジェクトリスト
     * @throws          SQLException
     * @throws          ClassNotFoundException
     * @see             なし
     * @return          なし
     */
    protected void setLinkBase(XbLinkBaseObj obj) throws XbException {

        // --------------------------------------------
        // calculationファイル読み込み
        // --------------------------------------------
        if (obj.getCalculationFileName() != null) {
            // linkbaseRefテーブルに指定されたタクソノミーIDが存在するかチェックする
            lLinkBaseRef_ID = dbaLinkbaserefDAO.select(
                                                obj.getTaxonomyId(),
                                                XbConstants.ROLE_URL_CALCULATION);

            this.readXmlFile(lLinkBaseRef_ID, obj.getCalculationFileName());
        }

        // --------------------------------------------
        // definitionファイル読み込み
        // --------------------------------------------
        if (obj.getDefinitionFileName() != null) {
            // linkbaseRefテーブルに指定されたタクソノミーIDが存在するかチェックする
            lLinkBaseRef_ID = dbaLinkbaserefDAO.select(
                                                obj.getTaxonomyId(), 
                                                XbConstants.ROLE_URL_DEFINITION);

            this.readXmlFile(lLinkBaseRef_ID, obj.getDefinitionFileName());
        }

        // --------------------------------------------
        // presentationファイル読み込み
        // --------------------------------------------
        if (obj.getPresentationFileName() != null) {
            // linkbaseRefテーブルに指定されたタクソノミーIDが存在するかチェックする
            lLinkBaseRef_ID = dbaLinkbaserefDAO.select(
                                                obj.getTaxonomyId(),
                                                XbConstants.ROLE_URL_PRESENTATION);

            this.readXmlFile(lLinkBaseRef_ID, obj.getPresentationFileName());
        }

        // --------------------------------------------
        // labelファイル読み込み
        // --------------------------------------------
        if (obj.getLabelFileName() != null) {
            // linkbaseRefテーブルに指定されたタクソノミーIDが存在するかチェックする
            lLinkBaseRef_ID = dbaLinkbaserefDAO.select(
                                                obj.getTaxonomyId(),
                                                XbConstants.ROLE_URL_LABEL);

            this.readXmlFile(lLinkBaseRef_ID, obj.getLabelFileName());
        }

        // --------------------------------------------
        // referenceファイル読み込み
        // --------------------------------------------
        if (obj.getReferenceFileName() != null) {
            // linkbaseRefテーブルに指定されたタクソノミーIDが存在するかチェックする
            lLinkBaseRef_ID = dbaLinkbaserefDAO.select(
                                                obj.getTaxonomyId(),
                                                XbConstants.ROLE_URL_REFERENCE);

            this.readXmlFile(lLinkBaseRef_ID, obj.getReferenceFileName());
        }
    }

    /**
     * XMLファイル読み込み
     * @param           String          ファイルパス
     * @throws          なし
     * @see             なし
     * @return          なし
     */
    private void readXmlFile(long lLinkBaseRef_ID, String fileName) throws XbException {
        // lLinkBaseRef_IDが0なら処理しない
        if (lLinkBaseRef_ID == 0) {
            return ;
        }
        // 名前空間用ハッシュテーブル
        // 対照ファイル別にインスタンスを生成する
        linkBaseMap = new HashMap<String, String>();

        // パーサの生成
        XbPAOImpl parser = new XbPAOImpl();
        // XMLファイルのパース
        parser.parse(fileName);
        // DOCUMENTノードの取得
        Document document = parser.getDocument();
        // DOM解析処理
        this.walk(document);
    }

    /**
     * ノードをたどる
     * @param           Node          ノード
     * @throws          なし
     * @see             なし
     * @return          なし
     */
    private void walk(Node node) throws XbException {
        // arc情報格納変数
        XbLinkbaseArcObj arcObj;
        // locator情報格納変数
        XbLinkbaseLocatorObj locatorObj;
        // resource情報格納変数
        XbLinkbaseResourceObj resourceObj;

        // nodeの取得
        for(Node n = node.getFirstChild();n != null;n = n.getNextSibling()) {
            // elementの判定
            if (n.getNodeType() == Node.ELEMENT_NODE) {
                // 属性値取得
                NamedNodeMap attrs = n.getAttributes();
                for (int i = 0;i < attrs.getLength();i ++) {
                    // 名前空間と名前空間URLを保持する。
                    if ("linkbase".equals(n.getNodeName())) {
                        String strwk = null;
                        String sp[] = attrs.item(i).getNodeName().split(":");
                        if (sp.length >= 2) {
                            // 名前空間
                            strwk = sp[1];
                        }
                        // 名前空間がnullでない場合、ハッシュテーブルに登録する
                        if (strwk != null) {
                            linkBaseMap.put(strwk, attrs.item(i).getNodeValue());
                        }
                    }

                    // "xlink:type"属性の値をチェックする
                    if (XbConstants.ATTRIBUTE_XLINK_TYPE.equals(attrs.item(i).getNodeName())) {
                        // "arc"の場合、Arcテーブル設定
                        if (XbConstants.ATTRIBUTE_ARC.equals(
                                attrs.getNamedItem(attrs.item(i).getNodeName()).getNodeValue())) {
                            arcObj = new XbLinkbaseArcObj();
                            // アークID
                            arcObj.setArc_id(arcIdDAO.getNextValue());
                            // LinkBaseRef ID
                            arcObj.setLinkbaseref_id(lLinkBaseRef_ID);
                            for (int j = 0;j < attrs.getLength();j ++) {
                                // show属性
                                if (XbConstants.ATTRIBUTE_XLINK_SHOW.equals(attrs.item(j).getNodeName())) {
                                    arcObj.setArc_show(
                                            attrs.getNamedItem(attrs.item(j).getNodeName()).getNodeValue());
                                }
                                // title属性
                                if (XbConstants.ATTRIBUTE_XLINK_TITLE.equals(attrs.item(j).getNodeName())) {
                                    arcObj.setArc_title(
                                            attrs.getNamedItem(attrs.item(j).getNodeName()).getNodeValue());
                                }
                                // from属性
                                if (XbConstants.ATTRIBUTE_XLINK_FROM.equals(attrs.item(j).getNodeName())) {
                                    arcObj.setArc_from(
                                            attrs.getNamedItem(attrs.item(j).getNodeName()).getNodeValue());
                                }
                                // to属性
                                if (XbConstants.ATTRIBUTE_XLINK_TO.equals(attrs.item(j).getNodeName())) {
                                    arcObj.setArc_to(
                                            attrs.getNamedItem(attrs.item(j).getNodeName()).getNodeValue());
                                }
                                // arcrole属性
                                if (XbConstants.ATTRIBUTE_XLINK_ARCROLE.equals(attrs.item(j).getNodeName())) {
                                    arcObj.setArc_arcrole(
                                            attrs.getNamedItem(attrs.item(j).getNodeName()).getNodeValue());
                                }
                                // use属性
                                if (XbConstants.ATTRIBUTE_USE.equals(attrs.item(j).getNodeName())) {
                                    arcObj.setArc_use(
                                            attrs.getNamedItem(attrs.item(j).getNodeName()).getNodeValue());
                                }
                                // priority属性
                                if (XbConstants.ATTRIBUTE_PRIORITY.equals(attrs.item(j).getNodeName())) {
                                    arcObj.setArc_priority(
                                            Long.parseLong(attrs.getNamedItem(attrs.item(j).getNodeName()).getNodeValue()));
                                }
                                // weight属性
                                if (XbConstants.ATTRIBUTE_WEIGHT.equals(attrs.item(j).getNodeName())) {
                                    arcObj.setCalculation_weight(
                                            Double.parseDouble(attrs.getNamedItem(attrs.item(j).getNodeName()).getNodeValue()));
                                }
                                 // order属性
                                if (XbConstants.ATTRIBUTE_ORDER.equals(attrs.item(j).getNodeName())) {
                                    arcObj.setPresentation_order(
                                            Double.parseDouble(attrs.getNamedItem(attrs.item(j).getNodeName()).getNodeValue()));
                                }
                            }
                            // Insert実行
                            dbaLinkbaseArcDAO.insert(arcObj);
                        }

                        // "locator"の場合、Locatorテーブル設定
                        if (XbConstants.ATTRIBUTE_LOCATOR.equals(
                                attrs.getNamedItem(attrs.item(i).getNodeName()).getNodeValue())) {
                            locatorObj = new XbLinkbaseLocatorObj();
                            // ロケータID
                            locatorObj.setLocator_id(locIdDAO.getNextValue());
                            // LinkBaseRef ID
                            locatorObj.setLinkbaseref_id(lLinkBaseRef_ID);
                            for (int j = 0;j < attrs.getLength();j ++) {
                                // href属性
                                if (XbConstants.ATTRIBUTE_XLINK_HREF.equals(attrs.item(j).getNodeName())) {
                                    String strHref = attrs.getNamedItem(attrs.item(j).getNodeName()).getNodeValue();

                                    locatorObj.setLoc_href(strHref);
                                    // 識別子を設定する
                                    locatorObj.setLoc_identifier(strHref.substring(strHref.lastIndexOf(XbConstants.SHARP_VALUE) + 1));
                                }
                                // label属性
                                if (XbConstants.ATTRIBUTE_XLINK_LABEL.equals(attrs.item(j).getNodeName())) {
                                    locatorObj.setLoc_label(
                                            attrs.getNamedItem(attrs.item(j).getNodeName()).getNodeValue());
                                }
                                // title属性
                                if (XbConstants.ATTRIBUTE_XLINK_TITLE.equals(attrs.item(j).getNodeName())) {
                                    locatorObj.setLoc_title(
                                            attrs.getNamedItem(attrs.item(j).getNodeName()).getNodeValue());
                                }
                                // role属性
                                if (XbConstants.ATTRIBUTE_XLINK_ROLE.equals(attrs.item(j).getNodeName())) {
                                    locatorObj.setLoc_role(
                                            attrs.getNamedItem(attrs.item(j).getNodeName()).getNodeValue());
                                }
                            }
                            // Insert実行
                            dbaLinkbaseLocatorDAO.insert(locatorObj);
                        }

                        // "resource"の場合、Resourceテーブル設定
                        if (XbConstants.ATTRIBUTE_RESOURCE.equals(
                                attrs.getNamedItem(attrs.item(i).getNodeName()).getNodeValue())) {
                            String strNodeVal = null;
                            resourceObj = new XbLinkbaseResourceObj();
                            // リソースID
                            resourceObj.setResource_id(resourceIdDAO.getNextValue());

                            // LinkBaseRef ID
                            resourceObj.setLinkbaseref_id(lLinkBaseRef_ID);
                            for (int j = 0;j < attrs.getLength();j ++) {
                                // label属性
                                if (XbConstants.ATTRIBUTE_XLINK_LABEL.equals(attrs.item(j).getNodeName())) {
                                    resourceObj.setResource_label(
                                            attrs.getNamedItem(attrs.item(j).getNodeName()).getNodeValue());
                                }
                                // title属性
                                if (XbConstants.ATTRIBUTE_XLINK_TITLE.equals(attrs.item(j).getNodeName())) {
                                    resourceObj.setResource_title(
                                            attrs.getNamedItem(attrs.item(j).getNodeName()).getNodeValue());
                                }
                                // role属性
                                if (XbConstants.ATTRIBUTE_XLINK_ROLE.equals(attrs.item(j).getNodeName())) {
                                    resourceObj.setLabel_role(
                                            attrs.getNamedItem(attrs.item(j).getNodeName()).getNodeValue());
                                }
                                // lang属性
                                if (XbConstants.ATTRIBUTE_XML_LANG.equals(attrs.item(j).getNodeName())) {
                                    resourceObj.setLabel_lang(
                                            attrs.getNamedItem(attrs.item(j).getNodeName()).getNodeValue());
                                }
                                // 値
                                strNodeVal = getNodeVal(n);

                                if (strNodeVal != null) {
                                    resourceObj.setResource_value(strNodeVal);
                                }
                            }
                            // Insert実行
                            dbaLinkbaseResourceDAO.insert(resourceObj);
                            // 値がnullの場合、子ノードを検索する
                            if (strNodeVal == null) {
                                // 子ノードがない場合、処理は行わない
                                this.setResourceItem(resourceObj,n);
                            }
                        }
                    }
                }
            }
            walk(n);
        }
    }

    /**
     * リソースアイテム情報を登録する
     * @param           XbLinkbaseResourceObj   LinkbaseResourceオブジェクト
     * @param           Node   ノード
     * @throws          なし
     * @see             なし
     * @return          なし
     */
    private void setResourceItem(XbLinkbaseResourceObj resourceObj, Node node) throws XbException {
        XbResourceItemObj reitemObj;
        XbResourceItemAttrObj reitemAttrObj;
        ArrayList<XbResourceItemAttrObj> List;

        NodeList ls = node.getChildNodes();
        Node child = null;

        for(int i = 0;i < ls.getLength();i ++) {
            child = ls.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                reitemObj = new XbResourceItemObj();
                List = new ArrayList<XbResourceItemAttrObj>();
                String sp[] = child.getNodeName().split(":");

                reitemObj.setResource_item_id(resourceItemIdDAO.getNextValue());
                reitemObj.setResource_id(resourceObj.getResource_id());

                reitemObj.setNamespace(sp[0]);
                reitemObj.setNamespace_url((String)linkBaseMap.get(sp[0]));
                reitemObj.setItem_name(sp[1]);
                reitemObj.setItem_value(this.getNodeVal(child));

                if (linkBaseMap.get(sp[0]) == null) {
                    NamedNodeMap attrs = child.getAttributes();
                    for (int j = 0;j < attrs.getLength();j ++) {
                        reitemAttrObj = new XbResourceItemAttrObj();
                        reitemAttrObj.setResource_item_attr_id(resourceItemAttrIdDAO.getNextValue());
                        reitemAttrObj.setResource_item_id(reitemObj.getResource_item_id());
                        reitemAttrObj.setAttribute_name(attrs.getNamedItem(attrs.item(j).getNodeName()).getNodeName());
                        reitemAttrObj.setAttribute_value(attrs.getNamedItem(attrs.item(j).getNodeName()).getNodeValue());

                        if (attrs.getNamedItem(attrs.item(j).getNodeName()).getNodeName().indexOf("xmlns") != -1) {
                            reitemObj.setNamespace_url(attrs.getNamedItem(attrs.item(j).getNodeName()).getNodeValue());
                        }
                        List.add(reitemAttrObj);
                    }
                }
                // リソースアイテムテーブル更新
                // Insert実行
                dbaResourceItemDAO.insert(reitemObj);
                // リソースアイテム属性テーブル更新
                for (int k = 0;k < List.size();k ++) {
                    XbResourceItemAttrObj obj = List.get(k);
                    // Insert実行
                    dbaResourceItemAttrDAO.insert(obj);
                }
            }
        }
        return ;
    }

    /**
     * 指定したノードのValueを取得する
     * @param           Node        ノード
     * @throws      なし
     * @see             なし
     * @return      String      Value値
     */
    private String getNodeVal(Node node) {
        if (node == null) {
            return null;
        }
        NodeList ls = node.getChildNodes();
        Node child = null;
        for(int i = 0; i<ls.getLength(); i++) {
            child = ls.item(i);
            if (child.getNodeType() == Node.TEXT_NODE) {
                return child.getNodeValue();
            }
        }
        return null;
    }
}