package se.koc.trader;

import zutil.ObjectUtil;
import zutil.db.DBConnection;
import zutil.db.DBUpgradeHandler;
import zutil.db.SQLResultHandler;
import zutil.db.handler.PropertiesSQLResult;
import zutil.io.file.FileUtil;
import zutil.log.LogUtil;
import zutil.ui.UserMessageManager;

import java.io.File;
import java.io.FileReader;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.*;
import java.util.logging.Logger;

/**
 * Static context of the application
 */
public class TraderContext {
    private static final Logger logger = LogUtil.getLogger();

    public static final String RESOURCE_ROOT;
    static {
        if (FileUtil.find("src/main/resources") != null)
            RESOURCE_ROOT = "src/main/resources";
        else
            RESOURCE_ROOT = ".";
    }
    public static final String RESOURCE_WEB_ROOT = RESOURCE_ROOT + "/web";

    private static final String CONF_FILE       = "trader.conf";
    private static final String DB_FILE         = "trader.db";
    private static final String DEFAULT_DB_FILE = RESOURCE_ROOT + "/trader-default.db";

    public static final String PROPERTY_DB_VERSION = "trader.db_version";
    public static final String PROPERTY_HTTP_PORT  = "trader.http_port";

    private static DBConnection db;
    private static UserMessageManager messageManager = new UserMessageManager();

    private static Properties fileConf = new Properties();
    private static Properties dbConf   = new Properties();


    public static void initialize(){
        try {
            // Read conf
            if (FileUtil.find(CONF_FILE) != null) {
                FileReader in = new FileReader(CONF_FILE);
                fileConf.load(in);
                in.close();
            } else {
                logger.info("No hal.conf file found");
            }

            if (FileUtil.find(DEFAULT_DB_FILE) == null){
                logger.severe("Unable to find default DB: " + DEFAULT_DB_FILE);
                System.exit(1);
            }

            // Init DB
            File dbFile = FileUtil.find(DB_FILE);
            db = new DBConnection(DBConnection.DBMS.SQLite, DB_FILE);

            if(dbFile == null){
                logger.info("No database file found, creating new DB...");
            } else {
                dbConf = db.exec("SELECT * FROM conf", new PropertiesSQLResult());
            }

            // --------------------------------------------
            // Upgrade DB
            // --------------------------------------------

            DBConnection referenceDB = new DBConnection(DBConnection.DBMS.SQLite, DEFAULT_DB_FILE);
            Properties defaultDBConf =
                    referenceDB.exec("SELECT * FROM conf", new PropertiesSQLResult());

            // Check DB version
            final int defaultDBVersion = Integer.parseInt(defaultDBConf.getProperty(PROPERTY_DB_VERSION));
            final int dbVersion = (dbConf.getProperty(PROPERTY_DB_VERSION) != null ?
                    Integer.parseInt(dbConf.getProperty(PROPERTY_DB_VERSION)) :
                    -1);
            logger.info("DB version: "+ dbVersion);

            if(defaultDBVersion > dbVersion ) {
                logger.info("Starting DB upgrade from v" + dbVersion + " to v" + defaultDBVersion + "...");
                if(dbFile != null){
                    File backupDB = FileUtil.getNextFile(dbFile);
                    logger.fine("Backing up DB to: "+ backupDB);
                    FileUtil.copy(dbFile, backupDB);
                }

                logger.fine(String.format("Upgrading DB (from: v%s, to: v%s)...", dbVersion, defaultDBVersion));
                final DBUpgradeHandler handler = new DBUpgradeHandler(referenceDB);
                handler.addIgnoredTable("db_version_history");
                handler.addIgnoredTable("sqlite_sequence");	// sqlite internal
                handler.setTargetDB(db);

                logger.fine("Performing pre-upgrade activities");

                // Read upgrade path preferences from the reference database
                referenceDB.exec("SELECT * FROM db_version_history"
                                + " WHERE db_version <= " + defaultDBVersion
                                + " AND db_version > " + dbVersion,
                        new SQLResultHandler<Object>() {
                            @Override
                            public Object handleQueryResult(Statement stmt, ResultSet result) throws SQLException {
                                while(result.next()){
                                    if(result.getBoolean("force_upgrade")){
                                        logger.fine("Forced upgrade enabled");
                                        handler.setForcedDBUpgrade(true);	//set to true if any of the intermediate db version requires it.
                                    }
                                }
                                return null;
                            }
                        });

                handler.upgrade();

                logger.info("DB upgrade done");
                setProperty(PROPERTY_DB_VERSION, defaultDBConf.getProperty(PROPERTY_DB_VERSION));
            }
            referenceDB.close();
        } catch (Exception e){
            throw new RuntimeException(e);
        }
    }


    public static Map<String,String> getProperties() {
        HashMap map = new HashMap();
        map.putAll(dbConf);
        map.putAll(fileConf);
        return map;

    }

    public static boolean containsProperty(String key) {
        return !ObjectUtil.isEmpty(getStringProperty(key));
    }
    public static String getStringProperty(String key){
        String value = null;
        if (fileConf != null)
            value = fileConf.getProperty(key);
        if (dbConf != null && value == null)
            value = dbConf.getProperty(key);
        return value;
    }
    public static String getStringProperty(String key, String defaultValue){
        if (!containsProperty(key))
            return defaultValue;
        return getStringProperty(key);
    }

    public static int getIntegerProperty(String key){
        String value = getStringProperty(key);

        if (getStringProperty(key) == null)
            return 0;
        return Integer.parseInt(value);
    }
    public static int getIntegerProperty(String key, int defaultValue){
        if (!containsProperty(key))
            return defaultValue;
        return getIntegerProperty(key);
    }

    public static boolean getBooleanProperty(String key) {
        return Boolean.parseBoolean(getStringProperty(key));
    }
    public static boolean getBooleanProperty(String key, boolean defaultValue) {
        if (!containsProperty(key))
            return defaultValue;
        return getBooleanProperty(key);
    }

    public static void setProperty(String key, String value) throws SQLException {
        PreparedStatement stmt = db.getPreparedStatement("REPLACE INTO conf (key, value) VALUES (?, ?)");
        stmt.setObject(1, key);
        stmt.setObject(2, value);
        DBConnection.exec(stmt);
        dbConf.setProperty(key, value);
    }



    public static DBConnection getDB(){
        return db;
    }

    /**
     * For testing purposes.
     */
    public static void setDB(DBConnection db){
        db = db;
    }


    public static UserMessageManager getMessageManager() {
        return messageManager;
    }
}
