package asandatabasebrowser.util;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import asandatabasebrowser.model.ColumnInfo;
import asandatabasebrowser.model.DbException;
import asandatabasebrowser.model.TableInfo;



/**
 * SQLの生成を行う。
 * これは短命のオブジェクト。
 * @author Yoshinori-Watanabe
 *
 */
public class SqlGenerator {
    /** テンプレート */
    protected Document template;
    protected String timestamp;
    static SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

    /** 指定されたテンプレートにより、自動生成ツールを作成します. */
    public SqlGenerator(String templateFilename)
                throws ParserConfigurationException, SAXException, IOException {
        DocumentBuilderFactory dbf =
                          DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        DocumentBuilder builder = dbf.newDocumentBuilder();
        this.template = builder.parse(new File(templateFilename));
    }

    /** 指定されたテーブルからソースを生成する. */
    public String generate(ArrayList tableInfoList) throws DbException {
    	// 現在日時を取得
    	timestamp = formatter.format(Calendar.getInstance().getTime());

    	// 出力するソースのノードを探す.
    	Node source = findSourceNode(template, "type", "DDL");
    	if (source == null) return null;
        PrintWriter writer = null;
        try {
            // 出力するファイルを準備
        	StringWriter sw = new StringWriter();
            writer = new PrintWriter(sw);
            generateSource(source, tableInfoList, writer);
            return sw.toString();
        }
        finally {
            if (writer != null) writer.close();
        }
    }
    
    /** ソースノードを探します. */
    private Node findSourceNode(Node node, String name, String value) {
        NodeList list = node.getChildNodes();
        for (int i=0; i<list.getLength(); i++) {
            Node n = list.item(i);
            if (n.getNodeName().equals("source")) {
            	if (name != null) {
            		Node an = n.getAttributes().getNamedItem(name);
            		System.out.println(an);
            		System.out.println(an.getNodeName());
            		// TODO:
            		return n;
            	} else {
            		return n;
            	}
            } else if (n.getNodeType() == Node.	ELEMENT_NODE) {
            	Node result = findSourceNode(n, name, value);
            	if (result != null) return result;	// 見つかった
            }
        }
        return null;
   	
    }
    
    /** ソースを生成する. */
    private void generateSource(Node node, ArrayList tableInfoList, PrintWriter writer) throws DbException {
        NodeList list = node.getChildNodes();
        for (int i=0; i<list.getLength(); i++) {
            Node n = list.item(i);
            if (n.getNodeName().equals("forAllTables")) {
            	generateForAllTables(n, tableInfoList, writer);
            } else if (n.getNodeType()==Node.TEXT_NODE) {
                String str = n.getNodeValue();
                writer.print(replaceKeywords(str, null, null));
            }
        }
    }
    
    /** すべてのカラムについてソースを生成する. */
    private void generateForAllTables(Node node, ArrayList tableInfoList, PrintWriter writer) throws DbException {
        // すべてのカラムについて
        for (int i=0; i<tableInfoList.size(); i++) {
        	TableInfo info = (TableInfo) tableInfoList.get(i);
        	info.updateColumnInfoList();
            NodeList list = node.getChildNodes();
            for (int j=0; j<list.getLength(); j++) {
                Node n = list.item(j);
                if (n.getNodeType()==Node.TEXT_NODE) {
                    String str = n.getNodeValue();
                    System.out.println("["+str+"]");
                    writer.print(replaceKeywords(str, info, null));
                } else if (n.getNodeName().equals("forAllColumns")) {
                	generateForAllColumns(n, info, info.getColumnInfoList(), writer);
                }
            }
        }
    }


    /** すべてのカラムについてソースを生成する. */
    private void generateForAllColumns(Node node, TableInfo table, ArrayList columnInfoList, PrintWriter writer) throws DbException {
        // すべてのカラムについて
        for (int i=0; i<columnInfoList.size(); i++) {
            ColumnInfo info = (ColumnInfo) columnInfoList.get(i);
            NodeList list = node.getChildNodes();
            for (int j=0; j<list.getLength(); j++) {
                Node n = list.item(j);
                if (n.getNodeType()==Node.TEXT_NODE) {
                    String str = n.getNodeValue();
                    writer.print(replaceKeywords(str, table, info));
                } else if (n.getNodeName().equals("excludeFirst")) {
                    if (i != 0) {     // 最初のカラムでないとき
                        String str = n.getChildNodes().item(0).getNodeValue();
                        writer.print(replaceKeywords(str, table, info));
                    }
                } else if (n.getNodeName().equals("excludeLast")) {
                    if (i != columnInfoList.size()-1) {     // 最後のカラムでないとき
                        String str = n.getChildNodes().item(0).getNodeValue();
                        writer.print(replaceKeywords(str, table, info));
                    }
                }
            }
        }
    }

    /** すべてのキーワードを変換する.  */
    private String replaceKeywords(String str, TableInfo table, ColumnInfo col) {
    	if (col != null) {
	        str = replaceKeyword(str, "${COLUMN_NAME}", col.columnName.toUpperCase());
	        str = replaceKeyword(str, "${column_name}", col.columnName.toLowerCase());
	        str = replaceKeyword(str, "${REMARKS}", col.remarks);
	        str = replaceKeyword(str, "${TYPE}", table.db.getTypeStr(col));
    	}
    	if (table != null) {
//    		if (table.getCatalogName() != null) {
//    			str = replaceKeyword(str, "${CATALOG_NAME}",  table.getCatalogName().toUpperCase());
//    			str = replaceKeyword(str, "${catalog_name}",  table.getCatalogName().toLowerCase());
//    		} else {
//    			str = replaceKeyword(str, "${CATALOG_NAME}",  "");
//    			str = replaceKeyword(str, "${catalog_name}",  "");
//    		}
//            if (table.getSchemaName() != null) {
//            	str = replaceKeyword(str, "${SCHEMA_NAME}", table.getSchemaName().toUpperCase());
//                str = replaceKeyword(str, "${schema_name}", table.getSchemaName().toLowerCase());
//            } else {
//            	str = replaceKeyword(str, "${SCHEMA_NAME}", "");
//                str = replaceKeyword(str, "${schema_name}", "");
//            }
            str = replaceKeyword(str, "${TABLE_NAME}", table.getSqlTableName().toUpperCase());
            str = replaceKeyword(str, "${table_name}", table.getSqlTableName().toLowerCase());
            str = replaceKeyword(str, "${REMARKS}", table.getRemarks());
            if (table.pkList != null && table.pkList.size() > 0) {
	            String pk = "PRIMARY KEY(";
	            for (int i=0; i<table.pkList.size(); i++) {
	            	if (i != 0) pk += ", ";
	            	pk += table.pkList.get(i);
	            }
	            pk += ")\n";
	            str = replaceKeyword(str, "${PRIMARY_KEY}", pk);
            } else {
	            str = replaceKeyword(str, "${PRIMARY_KEY}", "");
            }
    	}
        str = replaceKeyword(str, "${TIMESTAMP}", timestamp);
        return str;
    }

    
    /** キーワードを変換する. 再帰 */
    private String replaceKeyword(String str, String key, String val) {
    	if (val == null) val = "";
        int pos = str.indexOf(key);
        if (pos == -1) return str;
        str = str.substring(0, pos) + val + str.substring(pos+key.length());
        return replaceKeyword(str, key, val);
    }

    /**
     * 大文字・小文字交じりの文字列に変換する。
     * ex. "ORDER_SLIP" -> "OrderSlip" or "orderSlip"
     */
    public static String getUpperLowerString(String arg, boolean firstUpper) {
        String result = "";
        boolean nextUpper = firstUpper;
        for (int i=0; i<arg.length(); i++) {
            if (arg.charAt(i)=='_') {
                nextUpper = true;
            }
            else {
                if (nextUpper) {
                    result += Character.toUpperCase(arg.charAt(i));
                    nextUpper = false;
                }
                else {
                    result += Character.toLowerCase(arg.charAt(i));
                }
            }
        }
        return result;
    }


//    /** メイン */
//    public static void main(String[] args) throws Exception {
//        // 引数チェック
//        if (args.length < 1) {
//            System.err.println("使い方: java EntityGenerator <テーブル名> ...");
//            System.err.println("        java EntityGenerator -all");
//            System.exit(-1);
//        }
//
//        // EntityGenerator.properties からいろんな設定を読み込む
//        Properties prop = new Properties();
//        prop.load(new FileInputStream("EntityGenerator.properties"));
//        String driverName          = prop.getProperty("driverName");
//        String dbUrl               = prop.getProperty("dbUrl");
//        String dbUser              = prop.getProperty("dbUser");
//        String dbPassword          = prop.getProperty("dbPassword");
//        String typeMapperClassName = prop.getProperty("typeMapperClassName");
//        String templateFilename    = prop.getProperty("templateFilename");
//        String catalog             = prop.getProperty("catalog");
//        String schemaPattern       = prop.getProperty("schemaPattern");
//        int types_length           = Integer.parseInt(prop.getProperty("types.length"));
//        String[] types = new String[types_length];
//        for (int i=0; i<types_length; i++) {
//            types[i] = prop.getProperty("types."+i);
//        }
//
//        // DBに依存した型変換クラスを生成する
//        //typeMapper = (TypeMapper) Class.forName(typeMapperClassName).newInstance();
//        // ドライバクラスをロード
//        Class.forName(driverName);
//
//        Connection conn = null;
//        ResultSet rs = null;
//        try {
//            conn = DriverManager.getConnection(dbUrl, dbUser, dbPassword);
//            DatabaseMetaData meta = conn.getMetaData();
//            SqlGenerator generator = new SqlGenerator(templateFilename,
//                         catalog, schemaPattern);
//
//            if (args[0].equals("-all")) {
//                // すべてのテーブルに対して
//                rs = meta.getTables(catalog, schemaPattern, null, types);
//                while (rs.next()) {
//                    String tableName = rs.getString("TABLE_NAME");
//                    System.out.println(tableName);
//                    generator.generate(conn, tableName);
//                }
//            }
//            else {
//                // 指定されたテーブルに対して
//                for (int i=0; i<args.length; i++) {
//                    String tableName = args[i];
//                    System.out.println(tableName);
//                    generator.generate(conn, tableName);
//                }
//            }
//        }
//        finally {
//            if (rs != null) rs.close();
//            if (conn != null) conn.close();
//        }
//    }

}
