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

package jp.co.going.xbrl.base;

import java.io.File;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Date;

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 XbBaseschema {
	// 仕様番号確定後、使用する変数
	private String specification_no;
	// システム日付
	private Date system_date;

	private XbBaseSchemaDAO dbaBaseSchemaDAO;
	private XbItemTypeDAO dbaItemtypeDAO;

    /**
     * コンストラクタ
     * @param conn SQLコネクション
     * @throws XbException
     */
	public XbBaseschema(Connection conn) throws XbException {
        // Dateクラスインスタンス生成
        system_date = new Date();
        // 基本スキーマアクセスクラス
		dbaBaseSchemaDAO = XbBaseSchemaDAO.getInstance(conn);
		// 項目型アクセスクラス
		dbaItemtypeDAO = XbItemTypeDAO.getInstance(conn);
	}

    /**
     * 基本スキーマの取込みを行います
     * @param dirPath 基本スキーマ格納フォルダ
     * @param ver XBRLバージョン
     * @throws Exception
     */
	public void registerBaseSchema(String dirPath, String ver) throws XbException {

            // 読み込みファイル名取得
            File existsFile = new File(dirPath);
            File[] existsFileList = existsFile.listFiles();

            if (existsFileList.length == 0) {
                throw new XbException("ファイルが存在しません。");
            }

            // 仕様番号を取得する
            if ( ver.equals(XbConstants.SPECIFICATION_NO_20)) {
                specification_no = XbConstants.SPECIFICATION_NO_20;
            } else {
                specification_no = XbConstants.SPECIFICATION_NO_21;
            }

		   	// 基本スキーマを設定する
			this.setBaseSchema(dirPath, existsFileList);
	}

	/**
	 * 基本スキーマテーブルに設定する
	 * @param 			 String       ファイルパス
     * @throws          SQLException
     * @throws          ClassNotFoundException
     * @throws          Exception
	 * @see 			 なし
	 * @return 		 なし
	 */
	private void setBaseSchema(String path, File existsFileList[]) throws XbException {
	    int cnt = 0;
        for(int i=0; i < existsFileList.length; i++ ) {
			String str_Extension = this.getExtension(existsFileList[i]);
			// ファイル拡張子チェック　.xsdが対象とする
			if(str_Extension.equals(XbConstants.FILE_EXTENSION)){
			    if ((XbConstants.SPECIFICATION_NO_20.equals(specification_no) &&
                        existsFileList[i].getName().equals("xbrl-instance-2003-12-31.xsd")) ||
                     (XbConstants.SPECIFICATION_NO_21.equals(specification_no) &&
                        existsFileList[i].getName().equals("xbrl-instance.xsd"))) {
                    throw new XbException("選択したバージョンと定義ファイルのバージョンが一致しません。");
                }

                cnt ++;
                // finame
                String fileName = path + "/" + existsFileList[i].getName();
                // パーサの生成
                XbPAOImpl parser = new XbPAOImpl();
                // XMLファイルのパース
                parser.parse(fileName);
                // DOCUMENTノードの取得
                Document document = parser.getDocument();

                // **** 処理 ****
				this.getSchemaName(document);
				// xbrl-instanceファイル検索
				if(existsFileList[i].getName().indexOf(XbConstants.FILENAME_XBRL_INSTANCE) != -1){
					this.getItemType(document);
				}
			}
		}
        if (cnt == 0) {
            throw new XbException("定義ファイルが存在しません。");
        }
	}

    /**
     * 基本スキーマテーブルを更新する
     */
	private void getSchemaName(Document document) throws XbException {

    	String strUrl = "";	    	
	   	int cnt;
	   	XbBaseSchemaObj baseSchemaObj;

    	// --------------------------------------------------------------------
    	// 基本スキーマテーブル更新処理
    	// --------------------------------------------------------------------
    	NodeList lst = document.getElementsByTagNameNS("*", XbConstants.TAG_SCHEMA);
    	for (int i = 0; i<lst.getLength(); i++){
    		Node child = lst.item(i);
			NamedNodeMap attrs = child.getAttributes();
			for (int j = 0; j < attrs.getLength(); j++) {
				if(XbConstants.TARGET_NAMESPACE.equals(attrs.item(j).getNodeName())){
					// targetNamespaceで指定されているURLを保持する
                    strUrl = attrs.getNamedItem(attrs.item(j).getNodeName()).getNodeValue();
				}
				else if(strUrl.equals(attrs.getNamedItem(attrs.item(j).getNodeName()).getNodeValue())){
					// targetNamespaceの識別子と同じ識別子を持つ名前空間の場合、DBに更新する
				   	// select実行(主キー検索)
                    cnt = dbaBaseSchemaDAO.select(attrs.item(j).getNodeName(), specification_no);
					// なければ追加更新
					if(cnt == 0){
                        baseSchemaObj = new XbBaseSchemaObj();
						// 名前空間設定
                        baseSchemaObj.setNamespace(attrs.item(j).getNodeName());
						// 名前空間URL
                        baseSchemaObj.setNamespace_url(strUrl);
						// 仕様番号
                        baseSchemaObj.setSpecification_no(specification_no);
						// 日付
                        baseSchemaObj.setCreation_date(system_date);

					   	// Insert実行
                        dbaBaseSchemaDAO.insert(baseSchemaObj);
                    }
				}
		    }
    	}
	}

    /**
     * 項目型テーブルを更新する
     */
	private void getItemType(Document document) throws XbException {
		String strTypeName = "";  
		String strAttributeName = "";
		String strAttributeNameValue = "";
		XbItemTypeObj itemTypeObj;
	   	int cnt;

    	NodeList lst = document.getElementsByTagNameNS("*", XbConstants.TAG_COMPLEX_TYPE);

    	// --------------------------------------------------------------------
    	// 項型テーブル更新処理
    	// --------------------------------------------------------------------

    	for (int i = 0; i<lst.getLength(); i++){
    		Node child = lst.item(i);
    		// 属性値取得
			NamedNodeMap attrs = child.getAttributes();
            strTypeName = "";
			if(attrs.getLength() != 0){
                strTypeName = attrs.getNamedItem("name").getNodeValue();
                strAttributeName = "";
                strAttributeName = this.getAttributeName(child, strAttributeName);
		    }

            strAttributeNameValue = "";
			// バージョンに別に "numeric" or "nonnumeric" 型を判定する
			if((specification_no.equals(XbConstants.SPECIFICATION_NO_20) &&
                    strAttributeName.equals("numericContext")) || 
					(specification_no.equals(XbConstants.SPECIFICATION_NO_21) &&
                            strAttributeName.equals("xbrli:numericItemAttrs") ||
                            strAttributeName.equals("xbrli:essentialNumericItemAttrs"))){
                strAttributeNameValue =  XbConstants.ATTRIBUTE_NUMERIC;
			}else if((specification_no.equals(XbConstants.SPECIFICATION_NO_20) &&
                    strAttributeName.equals("nonNumericContext")) || 
					(specification_no.equals(XbConstants.SPECIFICATION_NO_21) &&
                            strAttributeName.equals("xbrli:nonNumericItemAttrs"))){
                strAttributeNameValue = XbConstants.ATTRIBUTE_NO_NUMERIC;
            }

            if(strTypeName != "" && strAttributeNameValue != ""){ 
			   	// select実行(主キー検索)
                cnt = dbaItemtypeDAO.select(strTypeName, specification_no);
				if(cnt == 0){
					// なければ追加更新
                    itemTypeObj = new XbItemTypeObj();
					// 論理名称
                    itemTypeObj.setType_name(strTypeName);
					// 型名称
                    itemTypeObj.setAttribute_name(strAttributeNameValue);
					// 仕様番号
                    itemTypeObj.setSpecification_no(specification_no);
					// 日付
                    itemTypeObj.setCreation_date(system_date);

				   	// Insert実行
                    dbaItemtypeDAO.insert(itemTypeObj);
				}
			}
    	}
	}

    /**
     * 属性名称を取得する
     */
	private String getAttributeName(Node node, String strAttributeName) throws XbException {
		if(node.getNodeType() == Node.ELEMENT_NODE){
			for(Node childnode = node.getFirstChild();
				childnode != null;
				childnode = childnode.getNextSibling()){
				if(childnode.getNodeType() == Node.ELEMENT_NODE){
					//COMPLEXTYPE_ATTRIBUTE_GROUP
					if(specification_no.equals(XbConstants.SPECIFICATION_NO_20) &&
							XbConstants.COMPLEXTYPE_ATTRIBUTE.equals(childnode.getNodeName())) {
			    		// 属性値取得
						NamedNodeMap attrs = childnode.getAttributes();
						if(attrs.getLength() != 0){
							// 仕様番号　2.0の場合
                            strAttributeName = attrs.getNamedItem("name").getNodeValue();
							break;
					    }
					}
					if(specification_no.equals(XbConstants.SPECIFICATION_NO_21) &&
							XbConstants.COMPLEXTYPE_ATTRIBUTE_GROUP.equals(childnode.getNodeName())){
			    		// 属性値取得
						NamedNodeMap attrs = childnode.getAttributes();
						if(attrs.getLength() != 0){
							// 仕様番号　2.1の場合
                            strAttributeName = attrs.getNamedItem("ref").getNodeValue();
							break;
					    }
					}
                    strAttributeName = this.getAttributeName(childnode, strAttributeName);
				}
			}
		}
		return strAttributeName;
	}

    /**
     * ファイルの拡張子を取得する
     */
	private String getExtension(File file)
	{
	  String fileName = file.getName();
	  int index = fileName.lastIndexOf('.');
	  if (index!=-1)
	  {
	    return fileName.substring(index + 1, fileName.length());
	  }
	  return "";
	}
}