package gmt.db;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

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

import org.apache.ibatis.datasource.pooled.PooledDataSource;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import gmt.common.type.LogLevelType;
import gmt.db.mapper.Mapper;
import gmt.db.vo.GmtDbInfoVO;
import gmt.io.client.GmtClientManager;
import gmt.logger.GmtLogManager;

public class GmtDb {

	private Map<String, SqlSessionFactory> sqlSessionFactoryMap = new HashMap<String, SqlSessionFactory>();
	private Map<String, GmtDbInfoVO> dbMap = new HashMap<String, GmtDbInfoVO>();

	private JSONObject dbCfg = new JSONObject();
	private JSONObject dbList = new JSONObject();
	private JSONArray dbArry = new JSONArray();
	
	private GmtLogManager logger;

	public GmtDb() {
		logger = GmtLogManager.getInstance();
	}

	@SuppressWarnings("unchecked")
	public void initializeDbFromXML(String path) {
		try {
			if (path.equals("") || path == null) {
				path = System.getProperty("user.dir");
			}
			
			File file = new File(path);
			if (!file.isDirectory()) {
				logger.writeLevelLog("[initializeDbFromXML] : Directory["+ path +"] does not exist!!", LogLevelType.LOG_INFO, "AllLog");
				return;
			}

			File DBxmlFile = new File(path + "/cfg/database.xml");
			
			if(!DBxmlFile.exists()) {
				logger.writeLevelLog("[initializeDbFromXML] : Database.xml does not exist in [" + path + "\\cfg\\database.xml] !!", LogLevelType.LOG_INFO, "AllLog");
				return;
			}

			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = factory.newDocumentBuilder();
			Document doc = builder.parse(DBxmlFile);
			doc.getDocumentElement().normalize();

			NodeList Info = doc.getElementsByTagName("Info");

			for (int i = 0; i < Info.getLength(); i++) {
				Node infoNode = Info.item(i);

				Element infoElement = (Element) infoNode;

				GmtDbInfoVO dbInfo = new GmtDbInfoVO();

				dbInfo.setIsUse(Boolean.valueOf(infoElement.getAttribute("IsUse")));
				dbInfo.setID(infoElement.getAttribute("ID"));
				dbInfo.setIP(infoElement.getAttribute("IP"));
				dbInfo.setPort(infoElement.getAttribute("Port"));
				dbInfo.setDBConType(infoElement.getAttribute("DBConType"));
				dbInfo.setDBName(infoElement.getAttribute("DBName"));
				dbInfo.setUserName(infoElement.getAttribute("Username"));
				dbInfo.setPassword(infoElement.getAttribute("Password"));
				dbInfo.setPingTime(infoElement.getAttribute("PingTime"));
				dbInfo.setDirectFlag(Boolean.valueOf(infoElement.getAttribute("DirectFlag")));
				dbInfo.setDBType(((Element) infoNode.getParentNode()).getAttribute("DBType"));

				dbMap.put(dbInfo.getID(), dbInfo);

				SqlSessionFactory sf = getSqlSessionFactory(dbInfo);
				sqlSessionFactoryMap.put(dbInfo.getID(), sf);

				JSONObject db = new JSONObject();
				db.put("ID", infoElement.getAttribute("ID"));
				db.put("Username", infoElement.getAttribute("Username"));
				db.put("IP", infoElement.getAttribute("IP"));
				db.put("Port", infoElement.getAttribute("Port"));
				db.put("DBType", ((Element) infoNode.getParentNode()).getAttribute("DBType"));
				db.put("DBName", infoElement.getAttribute("DBName"));
				db.put("IsUse", infoElement.getAttribute("IsUse"));
				db.put("DBConType", infoElement.getAttribute("DBConType"));
				db.put("Password", infoElement.getAttribute("Password"));
				db.put("PingTime", infoElement.getAttribute("PingTime"));
				db.put("DirectFlag", infoElement.getAttribute("DirectFlag"));

				dbCfg.put(infoElement.getAttribute("ID"), db);
			}

			dbArry.add(dbCfg);
			dbList.put("result", dbArry);

		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
	}

	private SqlSessionFactory getSqlSessionFactory(GmtDbInfoVO vo) {
		DataSource dataSource = getDataSource(vo);

		TransactionFactory transactionFactory = new JdbcTransactionFactory();

		Environment environment = new Environment(vo.getID(), transactionFactory, dataSource);

		Configuration configuration = new Configuration(environment);

		configuration.addMapper(Mapper.class);

		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);

		return sqlSessionFactory;
	}

	/* Mapper */
	public SqlSessionFactory getSqlSessionFactory(GmtDbInfoVO vo, Class<?> mapper) {
		DataSource dataSource = getDataSource(vo);

		TransactionFactory transactionFactory = new JdbcTransactionFactory();

		Environment environment = new Environment(vo.getID(), transactionFactory, dataSource);

		Configuration configuration = new Configuration(environment);

		configuration.addMapper(mapper);

		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);

		return sqlSessionFactory;
	}

	private DataSource getDataSource(GmtDbInfoVO vo) {
		// �������� �ʱ�ȭ
		String driver = null;
		String url = null;
		String userName = null;
		String password = null;
		String conType = null;
		String ip = null;
		String port = null;
		String dbName = null;
		int dbType = 0;
		int pingTime = 0;
		String pingQuery = null;
		Boolean pingEnabled = false;

		// ������ �� ��Ī
		dbType = Integer.parseInt(vo.getDBType());
		userName = vo.getUserName();
		password = vo.getPassword();
		conType = vo.getDBConType();
		ip = vo.getIP();
		port = vo.getPort();
		dbName = vo.getDBName();
		pingTime = Integer.parseInt(vo.getPingTime());

		switch (dbType) {
		case 0: // None

			break;

		case 1: // Oracle DBConType 0: ServiceName , 1: SID
			pingQuery = "select sysdate from dual";
			driver = "oracle.jdbc.driver.OracleDriver";
			if (conType.equals("0")) {
				url = "jdbc:oracle:thin:@//" + ip + ":" + port + "/" + dbName;
			} else if (conType.equals("1")) {
				url = "jdbc:oracle:thin:@" + ip + ":" + port + ":" + dbName;
			}

			break;

		case 2: // SQLServer
			pingQuery = "select 1";
			driver = "com.mysql.jdbc.Driver";
			url = "jdbc:mtsql://" + ip + ":" + port + "/" + dbName;

			break;

		case 3: // MiddleWare

			break;

		case 4: // Cubrid
			pingQuery = "select sysdate from db_root";
			driver = "cubrid.jdbc.driver.CUBRIDDriver";
			url = "jdbc:cubrid:" + ip + ":" + port + ":" + dbName + ":::";
			break;

		case 5: // Tibero
			pingQuery = "select 1 from dual";
			driver = "com.tmax.tibero.jdbc.TbDriver";
			url = "jdbc:tibero:thin:@" + ip + ":" + port + ":" + dbName;

			break;

		case 6: // MariaDB
			pingQuery = "select 1";
			driver = "org.mariadb.jdbc.Driver";
			url = "jdbc:mariadb://" + ip + ":" + port + "/" + dbName;

			break;

		case 7: // PostgreSQL
			pingQuery = "select 1";
			driver = "org.postgresql.Driver";
			url = "jdbc:postgresql://" + ip + ":" + port + "/" + dbName;

			break;

		default:
			break;
		}

		PooledDataSource dataSource = new PooledDataSource();

		dataSource.setDriver(driver);
		dataSource.setUrl(url);
		dataSource.setUsername(userName);
		dataSource.setPassword(password);

		// ping
		// if (pingTime > 0) {
		// pingEnabled = true;
		//
		// dataSource.setPoolPingEnabled(pingEnabled);
		// dataSource.setPoolPingQuery(pingQuery);
		// dataSource.setPoolPingConnectionsNotUsedFor(pingTime);
		// }

		return dataSource;
	}

	public Map<String, SqlSessionFactory> getSqlSessionFactoryMap() {
		return sqlSessionFactoryMap;
	}

	public synchronized Map<String, GmtDbInfoVO> getDbMap() {
		return dbMap;
	}

	public JSONObject getDbJson() {
		return dbList;
	}
}
