/*
 * 作成日: 2007/07/06
 */
package asandatabasebrowser.model;

import java.awt.Font;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.sql.Connection;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Properties;

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

import net.sourceforge.swingx.SxTextUtilities;

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

import blanco.commons.sql.format.BlancoSqlRule;


/**
 * アプリケーションの設定。
 * データベースやオプションの設定を保持する。
 * ファイルに保存したり読み込んだりする。
 * 
 */
public class VvDocument {
	// 定数群
	// このアプリケーションで使用する日時フォーマット
	public static final String DATE_FORMAT = "yyyy/MM/dd HH:mm:ss.SSS";
	private static final String PROPERTIES_FILE = "./verticalviewer.properties";

	/** データベース情報. */
	public ArrayList databaseList = new ArrayList();	// <VvDatabase>
	/** お気に入りの情報. */
	public ArrayList favList = new ArrayList();	// <FavoriteGroup>
	/** 検索時の最大レコード件数. -1のとき制限なし */
	public int maxRecord = 1000;
	/** フォント名(SQL入力エリアなどで使用) */
	public String fontName = "Monospaced";
	/** フォントサイズ(SQL入力エリアなどで使用) */
	public int fontSize = 12;
//	/** オラクルのゴミ箱テーブルを無視するパターン 例:"BIN$H79aBaKrAr/gQAB/AQAKOw==$0" */
//	public String ignoreTableNamePattern = null;//"^BIN\\$.*==\\$0$";

	private Font font = null;
	/** 設定が変更されたかどうか？ */
	private boolean modified = false;
	public boolean isModified() { return modified; }
	public void setModified(boolean mod) { this.modified = mod; }
	
	public Font getFont() {
		if (font == null || font.getSize() != fontSize) {
			int style = Font.PLAIN;
			font = new Font(fontName, style, fontSize);
		}
		return font;
	}
    /** 接続先を取得する。*/
	public VvDatabase getDatabaseFromFavorite(FavoriteTable fav) {
        for (int i=0; i<databaseList.size(); i++) {
        	VvDatabase d = (VvDatabase) databaseList.get(i);
        	if (d.name.equals(fav.dbName)) {
        		return d;
        	}
        }
        return null;
	}
	
	/** お気に入りからテーブル情報を取得する。 */
	public TableInfo getTableInfoFromFavorite(FavoriteTable fav) {
        // 接続先を取得する。
        VvDatabase db = getDatabaseFromFavorite(fav);
        if (db == null) return null;
        
        // コネクションを取得する。
        Connection conn = db.getConnection();
        // 接続されていなかったらnullを返す。
        if (conn == null) return null;

        // 該当するカタログ/スキーマを取得する。
        SchemaInfo schema = null;
        ArrayList list = db.getSchemaInfoList();
        for (int i=0; i<list.size(); i++) {
        	SchemaInfo s = (SchemaInfo) list.get(i);
        	if (SxTextUtilities.equalsString(fav.tableCatalog, s.getCatalogName()) &&
        			SxTextUtilities.equalsString(fav.tableSchema, s.getSchemaName()) ) {
        		schema = s;
        		break;
        	}
        }
        if (schema == null) return null;
        // 該当するテーブルを取得する。
//        TableInfo table = db.getTableInfo(schema, fav.tableName, new String[] {fav.tableType});
        TableInfo table = schema.getTableTypeInfo(fav.tableType).getTableInfo(fav.tableName);
        return table;
	}
	
	
	
	
	
	/** プロパティを読み込む */
	public void loadProperties() throws DbException {
		Properties prop = new Properties();
		FileInputStream in = null;
		try {
			File file = new File(PROPERTIES_FILE);
			if (! file.exists()) {
				file = new File("./dbtools.properties");	// 旧バージョン対応
			}
			in = new FileInputStream(file);
			prop.load(in);
			int dbUserSize = Integer.parseInt(prop.getProperty("Database.size", "0"));
			for (int i=0; i<dbUserSize; i++) {
				String path = "Database."+i;
				String name       = prop.getProperty(path+".name");
				String driverName = prop.getProperty(path+".driverName");
				String dbUrl      = prop.getProperty(path+".url");
				String dbUser     = prop.getProperty(path+".user");
				String dbPassword = prop.getProperty(path+".password");
				//String nameFilter = prop.getProperty(path+".nameFilter");
				String highlight  = prop.getProperty(path+".highlight");
				int zipname_count = Integer.parseInt(prop.getProperty(path+".zipnames", "0"));
				ArrayList zipnames = new ArrayList();
				for (int z=0; z<zipname_count; z++) {
					String zipname = prop.getProperty(path+".zipnames."+z);
					zipnames.add(zipname);
				}
				boolean isShow    = "true".equals(prop.getProperty(path+".isShow"));
				VvDatabase db = new VvDatabase(name, driverName, dbUrl, dbUser, dbPassword, 
						zipnames, "other", highlight, isShow, true, true, false, false, false, BlancoSqlRule.KEYWORD_NONE, "");
				//db.nameFilter = nameFilter;
				databaseList.add(db);
			}
			
			maxRecord  = Integer.parseInt(prop.getProperty("maxRecord", "1000"));
//			rollbackBeforeClose = "true".compareToIgnoreCase(prop.getProperty("rollbackBeforeClose", "false")) == 0;
//			ignoreTableNamePattern = prop.getProperty("ignoreTableNamePattern", null);
			fontSize = Integer.parseInt(prop.getProperty("fontSize", "16"));
		}
		catch (IOException e) {
			//throw new DbException("プロパティの読み込みに失敗しました"+PROPERTIES_FILE, e);
			// ファイルがないときにはデフォルト値で。
		}
		finally {
			if (in!=null) try { in.close(); } catch (IOException e) {}
		}
	}
	/**
	 * プロパティを保存する.
	 * @deprecated
	 */
	public void storeProperties() throws DbException {
		Properties prop = new Properties();
		FileOutputStream out = null;
		try {
			prop.setProperty("Database.size", ""+databaseList.size());
			for (int i=0; i<databaseList.size(); i++) {
				VvDatabase db = (VvDatabase) databaseList.get(i);
				String path = "Database."+i;
				prop.setProperty(path+".name", db.name);
				prop.setProperty(path+".driverName", db.driverName);
				prop.setProperty(path+".url", db.url);
				prop.setProperty(path+".user", db.user);
				prop.setProperty(path+".password", db.password);
				//prop.setProperty(path+".nameFilter", db.nameFilter);
				prop.setProperty(path+".zipnames", ""+db.jarnames.size());
				for (int z=0; z<db.jarnames.size(); z++) {
					prop.setProperty(path+".zipnames."+z, (String) db.jarnames.get(z));
				}
				if (db.highlightFilename != null) {
					prop.setProperty(path+".highlight", db.highlightFilename);
				}
				prop.setProperty(path+".isShow", ""+db.isShowLoginDialog);
			}
			
			prop.setProperty("maxRecord", ""+maxRecord);
//			prop.setProperty("rollbackBeforeClose", ""+rollbackBeforeClose);
//			if (ignoreTableNamePattern != null) {
//				prop.setProperty("ignoreTableNamePattern", ignoreTableNamePattern);
//			}
			if (8 <= fontSize && fontSize < 100) {
				prop.setProperty("fontSize", ""+fontSize);
			}
			out = new FileOutputStream(new File(PROPERTIES_FILE));
			prop.store(out, "VerticalViewer");
		}
		catch (IOException e) {
			throw new DbException("プロパティの保存に失敗しました"+PROPERTIES_FILE, e);
		}
		finally {
			if (out!=null) try { out.close(); } catch (IOException e) {}
		}
	}
	
	/**
	 * 設定をファイルに保存する.
	 *　直接XML形式のファイルを出力していますが、DOMツリーを作成して出力させるべきです。
	 * 近い将来、実装が変わる可能性があります。 
	 */
	public void storeXml(File file) throws DbException {
		assert file != null;
		PrintWriter writer = null;
		try {
			writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF8"));
			writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
			String date = new SimpleDateFormat(DATE_FORMAT).format(new Date());
			writer.println("<!-- generated by AsanDatabaseBrowser "+date+" -->");
			writer.println("<AsanDatabaseBrowser>");
			for (int i=0; i<databaseList.size(); i++) {
				VvDatabase db = (VvDatabase) databaseList.get(i);
				writer.println("\t<Database>");
				writer.println("\t\t<name>"+SxTextUtilities.encodeXml(db.name)+"</name>");
				writer.println("\t\t<driverName>"+SxTextUtilities.encodeXml(db.driverName)+"</driverName>");
				writer.println("\t\t<url>"+SxTextUtilities.encodeXml(db.url)+"</url>");
				writer.println("\t\t<user>"+SxTextUtilities.encodeXml(db.user)+"</user>");
				writer.println("\t\t<password>"+SxTextUtilities.encodeXml(db.password)+"</password>");
				//prop.setProperty(path+".nameFilter", db.nameFilter);
				writer.println("\t\t<jarnames>");
				for (int z=0; z<db.jarnames.size(); z++) {
					writer.println("\t\t\t<jarname>"+SxTextUtilities.encodeXml((String) db.jarnames.get(z))+"</jarname>");
				}
				writer.println("\t\t</jarnames>");
				writer.println("\t\t<productName>"+SxTextUtilities.encodeXml(db.productName)+"</productName>");
				if (db.highlightFilename != null) {
					writer.println("\t\t<highlight>"+SxTextUtilities.encodeXml(db.highlightFilename)+"</highlight>");
				}
				writer.println("\t\t<isShow>"+db.isShowLoginDialog+"</isShow>");
				writer.println("\t\t<rollbackBeforeClose>"+db.rollbackBeforeClose+"</rollbackBeforeClose>");
				writer.println("\t\t<rollbackAfterQuery>"+db.rollbackAfterQuery+"</rollbackAfterQuery>");
				writer.println("\t\t<useOracleTableComments>"+db.useOracleTableComments+"</useOracleTableComments>");
				writer.println("\t\t<useOracleColumnComments>"+db.useOracleColumnComments+"</useOracleColumnComments>");
				writer.println("\t\t<hideOracleBinTable>"+db.hideOracleBinTable+"</hideOracleBinTable>");
				writer.println("\t\t<keywordCase>"+db.keywordCase+"</keywordCase>");
				writer.println("\t\t<functionNames>"+SxTextUtilities.encodeXml(db.functionNames)+"</functionNames>");
				writer.println("\t</Database>");
			}
			writer.println("\t<Favorite>");
			for (int i=0; i<favList.size(); i++) {
				FavoriteGroup group = (FavoriteGroup) favList.get(i);
				writer.println("\t\t<FavoriteGroup>");
				writer.println("\t\t\t<name>"+group.name+"</name>");
				for (int j=0; j<group.list.size(); j++) {
					if (group.get(j) instanceof FavoriteTable) {
						FavoriteTable table = (FavoriteTable) group.get(j);
						writer.println("\t\t\t<FavoriteTable>");
						writer.println("\t\t\t\t<name>"+SxTextUtilities.encodeXml(table.name)+"</name>");
						writer.println("\t\t\t\t<dbName>"+SxTextUtilities.encodeXml(table.dbName)+"</dbName>");
						writer.println("\t\t\t\t<tableCatalog>"+(table.tableCatalog!=null?table.tableCatalog:"")+"</tableCatalog>");
						writer.println("\t\t\t\t<tableSchema>"+(table.tableSchema!=null?table.tableSchema:"")+"</tableSchema>");
						writer.println("\t\t\t\t<tableName>"+table.tableName+"</tableName>");
						writer.println("\t\t\t\t<tableType>"+table.tableType+"</tableType>");
						writer.println("\t\t\t</FavoriteTable>");
					} else if (group.get(j) instanceof FavoriteSQL) {
						FavoriteSQL sql = (FavoriteSQL) group.get(j);
						writer.println("\t\t\t<FavoriteSQL>");
						writer.println("\t\t\t\t<name>"+SxTextUtilities.encodeXml(sql.name)+"</name>");
						writer.println("\t\t\t\t<dbName>"+SxTextUtilities.encodeXml(sql.dbName)+"</dbName>");
						for (int k=0; k<sql.getCount(); k++) {
							writer.println("\t\t\t\t<SQL>"+SxTextUtilities.encodeXml(sql.getSql(k))+"</SQL>");
						}
						writer.println("\t\t\t</FavoriteSQL>");
					}
				}
				writer.println("\t\t</FavoriteGroup>");
			}
			writer.println("\t</Favorite>");
			writer.println("\t<option>");
			writer.println("\t\t<maxRecord>"+maxRecord+"</maxRecord>");
//			writer.println("\t\t<rollbackBeforeClose>"+rollbackBeforeClose+"</rollbackBeforeClose>");
//			if (ignoreTableNamePattern != null) {
//				writer.println("\t\t<ignoreTableNamePattern>"+ignoreTableNamePattern+"</ignoreTableNamePattern>");
//			}
			if (8 <= fontSize && fontSize < 100) {
				writer.println("\t\t<fontSize>"+fontSize+"</fontSize>");
			}
			writer.println("\t</option>");
			writer.println("</AsanDatabaseBrowser>");
		} catch (UnsupportedEncodingException ex) {
			throw new DbException("プロジェクトファイルの読み込みに失敗しました。encoding=UTF8", ex);
		} catch (FileNotFoundException ex) {
			throw new DbException("プロジェクトファイルの読み込みに失敗しました。file="+file, ex);
		} finally {
			if (writer != null) writer.close();
		}
	}
	public void loadXml(File file) {
		assert file != null;
		assert file.exists();
		try {
			System.out.println("file="+file);
			DocumentBuilderFactory dbfactory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder builder = dbfactory.newDocumentBuilder();
			Document document = builder.parse(file);
			NodeList list = document.getChildNodes();
			for (int i=0; i<list.getLength(); i++) {
				Node n = list.item(i);
				if (n.getNodeName().equals("AsanDatabaseBrowser")) {
					loadAsanDatabaseBrowser(n);
					return;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void loadVerticalViewerXml(File file) {
		assert file != null;
		assert file.exists();
		try {
			System.out.println("file="+file);
			DocumentBuilderFactory dbfactory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder builder = dbfactory.newDocumentBuilder();
			Document document = builder.parse(file);
			NodeList list = document.getChildNodes();
			for (int i=0; i<list.getLength(); i++) {
				Node n = list.item(i);
				if (n.getNodeName().equals("VerticalViewer")) {
					loadAsanDatabaseBrowser(n);
					return;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	private void loadAsanDatabaseBrowser(Node node) {
		NodeList list = node.getChildNodes();
		for (int i=0; i<list.getLength(); i++) {
			Node n = list.item(i);
			if (n.getNodeType() == Node.ELEMENT_NODE) {
				if (n.getNodeName().equals("Database")) {
					try {
						VvDatabase db = loadDatabase(n);
						databaseList.add(db);
					} catch (NullPointerException e) {
						e.printStackTrace();
					}
				}
				else if (n.getNodeName().equals("Favorite")) {
					favList = loadFavorite(n);
				}
				else if (n.getNodeName().equals("option")) {
					loadOption(n);
				}
			}
		}
	}
	private VvDatabase loadDatabase(Node node) {
		String name       = null;
		String driverName = null;
		String url      = null;
		String user     = "";
		String password = "";
		String productName = "other";
		//String nameFilter = prop.getProperty(path+".nameFilter");
		String highlight  = "";
		ArrayList jarnames = new ArrayList();
		boolean isShow    = false;
		boolean rollbackBeforeClose = true;
		boolean rollbackAfterQuery = true;
		boolean useOracleTableComments = false;
		boolean useOracleColumnComments = false;
		boolean hideOracleBinTable = false;
		int keywordCase = BlancoSqlRule.KEYWORD_NONE;
		String functionNames = "";
		NodeList list = node.getChildNodes();
		for (int i=0; i<list.getLength(); i++) {
			Node n = list.item(i);
			if (n.getNodeType() == Node.ELEMENT_NODE) {
				if (n.getNodeName().equals("name")) {
					name = n.getFirstChild().getNodeValue();
				}
				else if (n.getNodeName().equals("driverName")) {
					driverName = n.getFirstChild().getNodeValue();
				}
				else if (n.getNodeName().equals("url")) {
					url = n.getFirstChild().getNodeValue();
				}
				else if (n.getNodeName().equals("user") && n.getFirstChild() != null) {
					user = n.getFirstChild().getNodeValue();
				}
				else if (n.getNodeName().equals("password") && n.getFirstChild() != null) {
					password = n.getFirstChild().getNodeValue();
				}
				else if (n.getNodeName().equals("productName") & n.getFirstChild() != null) {
					productName = n.getFirstChild().getNodeValue();
				}
				else if (n.getNodeName().equals("highlight") && n.getFirstChild() != null) {
					highlight = n.getFirstChild().getNodeValue();
				}
				else if (n.getNodeName().equals("isShow")) {
					isShow = "true".equals(n.getFirstChild().getNodeValue());
				}
				else if (n.getNodeName().equals("rollbackBeforeClose")) {
					rollbackBeforeClose = "true".equals(n.getFirstChild().getNodeValue());
				}
				else if (n.getNodeName().equals("rollbackAfterQuery")) {
					rollbackAfterQuery = "true".equals(n.getFirstChild().getNodeValue());
				}
				else if (n.getNodeName().equals("jarnames") && n.getFirstChild() != null) {
					jarnames = loadJarnames(n);
				}
				else if (n.getNodeName().equals("useOracleTableComments") && n.getFirstChild() != null) {
					useOracleTableComments = "true".equals(n.getFirstChild().getNodeValue());
				}
				else if (n.getNodeName().equals("useOracleColumnComments") && n.getFirstChild() != null) {
					useOracleColumnComments = "true".equals(n.getFirstChild().getNodeValue());
				}
				else if (n.getNodeName().equals("hideOracleBinTable") && n.getFirstChild() != null) {
					hideOracleBinTable = "true".equals(n.getFirstChild().getNodeValue());
				}
				else if (n.getNodeName().equals("keywordCase") && n.getFirstChild() != null) {
					keywordCase = Integer.parseInt(n.getFirstChild().getNodeValue());
				}
				else if (n.getNodeName().equals("functionNames") && n.getFirstChild() != null) {
					functionNames = n.getFirstChild().getNodeValue();
				}
			}
		}
		return new VvDatabase(name, driverName, url, user, password, 
				jarnames, productName, highlight, isShow, rollbackBeforeClose, rollbackAfterQuery,
				useOracleTableComments, useOracleColumnComments, hideOracleBinTable,
				keywordCase, functionNames);
	}
	private ArrayList loadJarnames(Node node) {
		ArrayList result = new ArrayList();
		NodeList list = node.getChildNodes();
		for (int i=0; i<list.getLength(); i++) {
			Node n = list.item(i);
			if (n.getNodeType() == Node.ELEMENT_NODE) {
				if (n.getNodeName().equals("jarname")) {
					String jarname = n.getFirstChild().getNodeValue();
					result.add(jarname);
				}
			}
		}
		return result;
	}
	private ArrayList loadFavorite(Node node) {
		ArrayList result = new ArrayList();
		NodeList list = node.getChildNodes();
		for (int i=0; i<list.getLength(); i++) {
			Node n = list.item(i);
			if (n.getNodeType() == Node.ELEMENT_NODE) {
				if (n.getNodeName().equals("FavoriteGroup")) {
					FavoriteGroup group = loadFaroviteGroup(n);
					result.add(group);
				}
			}
		}
		return result;
	}
	private FavoriteGroup loadFaroviteGroup(Node node) {
		FavoriteGroup group = null;
		String name       = null;
		NodeList list = node.getChildNodes();
		for (int i=0; i<list.getLength(); i++) {
			Node n = list.item(i);
			if (n.getNodeType() == Node.ELEMENT_NODE) {
				if (n.getNodeName().equals("name")) {
					name = n.getFirstChild().getNodeValue();
					group = new FavoriteGroup(name);
				}
				else if (n.getNodeName().equals("FavoriteTable") && n.getFirstChild() != null) {
					FavoriteTable table = loadFavoriteTable(n);
					group.add(table);
				}
				else if (n.getNodeName().equals("FavoriteSQL")){
					FavoriteSQL sql = loadFavoriteSQL(n);
					group.add(sql);
				}
			}
		}
		return group;
	}	
	private FavoriteTable loadFavoriteTable(Node node) {
		String name = null;
		String dbName = null;
		String tableCatalog = null;
		String tableSchema = null;
		String tableName = null;
		String tableType = null;

		NodeList list = node.getChildNodes();
		for (int i=0; i<list.getLength(); i++) {
			Node n = list.item(i);
			if (n.getNodeType() == Node.ELEMENT_NODE) {
				if (n.getNodeName().equals("name")) {
					name = n.getFirstChild().getNodeValue();
				}
				else if (n.getNodeName().equals("dbName")) {
					dbName = n.getFirstChild().getNodeValue();
				}
				else if (n.getNodeName().equals("tableCatalog") && n.getFirstChild() != null) {
					tableCatalog = n.getFirstChild().getNodeValue();
				}
				else if (n.getNodeName().equals("tableSchema") && n.getFirstChild() != null) {
					tableSchema = n.getFirstChild().getNodeValue();
				}
				else if (n.getNodeName().equals("tableName") && n.getFirstChild() != null) {
					tableName = n.getFirstChild().getNodeValue();
				}
				else if (n.getNodeName().equals("tableType") && n.getFirstChild() != null) {
					tableType = n.getFirstChild().getNodeValue();
				}
			}
		}
		return new FavoriteTable(name, dbName, tableCatalog, tableSchema, tableName, tableType);
	}
	private FavoriteSQL loadFavoriteSQL(Node node) {
		String dbName = null;
		String name = null;
		ArrayList sqlList = new ArrayList();

		NodeList list = node.getChildNodes();
		for (int i=0; i<list.getLength(); i++) {
			Node n = list.item(i);
			if (n.getNodeType() == Node.ELEMENT_NODE) {
				if (n.getNodeName().equals("name")) {
					name = n.getFirstChild().getNodeValue();
				}
				else if (n.getNodeName().equals("dbName")) {
					dbName = n.getFirstChild().getNodeValue();
				}
				else if (n.getNodeName().equals("SQL")) {
					String sql = n.getFirstChild().getNodeValue();
					sqlList.add(sql);
				}
			}
		}
		return new FavoriteSQL(dbName, name, sqlList);
	}
	private void loadOption(Node node) {
		NodeList list = node.getChildNodes();
		for (int i=0; i<list.getLength(); i++) {
			Node n = list.item(i);
			if (n.getNodeType() == Node.ELEMENT_NODE) {
				if (n.getNodeName().equals("maxRecord")) {
					maxRecord = Integer.parseInt(n.getFirstChild().getNodeValue());
				}
//				else if (n.getNodeName().equals("rollbackBeforeClose")) {
//					rollbackBeforeClose = "true".equals(n.getFirstChild().getNodeValue());
//				}
//				else if (n.getNodeName().equals("ignoreTableNamePattern")) {
//					ignoreTableNamePattern = n.getFirstChild().getNodeValue();
//				}
				else if (n.getNodeName().equals("fontSize")) {
					fontSize = Integer.parseInt(n.getFirstChild().getNodeValue());
				}
			}
		}
	}
	
}
