
import java.io.File;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.Timestamp;
import java.util.StringTokenizer;
import oracle.jdbc.pool.OracleDataSource;
import pervasync.SyncDirection;
import pervasync.SyncException;
import pervasync.SyncSummary;
import pervasync.client.SyncAgent;
import pervasync.client.SyncClientAdmin;
import pervasync.util.OrderedProperties;

/**
 * To invoke a synchronization, you need to set the sync client properties such as
 * the sync server URL, user name and password. Initial synchronization will
 * create the physical DB tables and populate them with initial data.
 * To generate DML operations on the client side tables. invoke "insert", "delete" 
 * and "update" methods. Synchronization will check in these changes to
 * the server DB on the server server side.
 *
 */
public class ClientAppOracle {

    String pervasyncHome = null;
    OrderedProperties clientAppOracleIni = null;
    boolean bUsage = false;
    String pervasyncClientDbUrl = null;
    String pervasyncClientDbSystemUser = null;
    String pervasyncClientDbSystemPassword = null;
    String pervasyncClientDemoSchema = null;
    OracleDataSource pervasyncClientDbSystemDataSource = null;
    int iEmp = -10;
    long taskNextId = 0;
    int taskInsertsPerEmployee = 0;
    int taskUpdatesPerEmployee = 0;
    int taskDeletesPerEmployee = 0;
    int totInsertCount = 0;
    int totUpdateCount = 0;
    int totDeleteCount = 0;
    static SyncClientAdmin syncClientAdmin = null;

    public static void main(String[] args) throws Exception, Throwable {

        long beginTime = System.currentTimeMillis();
        String strArgs = "";
        ClientAppOracle clientAppOracle = new ClientAppOracle();

        // Command line processing
        if (args.length < 1) {
            clientAppOracle.bUsage = true;
        }
        for (int i = 0; i < args.length; i++) {
            try {
                syncClientAdmin = SyncClientAdmin.getInstance();
                if (!args[i].startsWith("-")) {
                    strArgs += args[i];
                    if (args[i].equalsIgnoreCase("sync")) {
                        clientAppOracle.sync();
                    } else if (args[i].equalsIgnoreCase("insert")) {
                        clientAppOracle.insert();
                    } else if (args[i].equalsIgnoreCase("delete")) {
                        clientAppOracle.delete();
                    } else if (args[i].equalsIgnoreCase("update")) {
                        clientAppOracle.update();
                    } else {
                        clientAppOracle.bUsage = true;
                    }
                    continue;
                }
            } catch (Throwable t) {
                t.printStackTrace();
                System.exit(1);
            } finally {
                syncClientAdmin.destroy();
            }

        }

        if (clientAppOracle.bUsage) {
            System.out.println("Usage: java ClientAppOracle "
                    + "{setClientProperties | sync | insert | delete | update}");
            System.exit(1);
        }

        long endTime = System.currentTimeMillis();
        System.out.println("\njava ClientAppOracle " + strArgs
                + " total time: " + (endTime - beginTime) / 1000.0
                + " seconds");
    }

    /**
     * Constructor, read the app parameters from client_app_oracle.ini
     */
    public ClientAppOracle() throws Exception {

        File clientAppOracleIniFile = new File("client_app_oracle.ini");
        if (!clientAppOracleIniFile.exists()) {
            System.out.println("clientAppOracleIniFile="
                    + clientAppOracleIniFile.getCanonicalPath());
            throw new Exception("Can not find config file "
                    + clientAppOracleIniFile);
        }
        clientAppOracleIni = new OrderedProperties();
        FileInputStream clientAppOracleIniFileIs =
                new FileInputStream(clientAppOracleIniFile);
        clientAppOracleIni.load(clientAppOracleIniFileIs);
        clientAppOracleIniFileIs.close();

        pervasyncClientDbUrl =
                clientAppOracleIni.getPropertyValue("pervasync.client.db.url");
        pervasyncClientDbSystemUser =
                clientAppOracleIni.getPropertyValue("pervasync.client.db.system.user");
        pervasyncClientDbSystemPassword =
                clientAppOracleIni.getPropertyValue("pervasync.client.db.system.password");
        pervasyncClientDemoSchema =
                clientAppOracleIni.getPropertyValue("pervasync.client.demo.schema");

        pervasyncClientDbSystemDataSource = new OracleDataSource();
        pervasyncClientDbSystemDataSource.setURL(pervasyncClientDbUrl);
        pervasyncClientDbSystemDataSource.setUser(pervasyncClientDbSystemUser);
        pervasyncClientDbSystemDataSource.setPassword(pervasyncClientDbSystemPassword);

        taskInsertsPerEmployee =
                Integer.parseInt((clientAppOracleIni.getPropertyValue("task.inserts.per.employee")).trim());
        taskUpdatesPerEmployee =
                Integer.parseInt((clientAppOracleIni.getPropertyValue("task.updates.per.employee")).trim());
        taskDeletesPerEmployee =
                Integer.parseInt((clientAppOracleIni.getPropertyValue("task.deletes.per.employee")).trim());

        // Find department id from manager name
        SyncClientAdmin admin = SyncClientAdmin.getInstance();
        String syncUserName = admin.getSyncUserName();
        admin.destroy();
        StringTokenizer st = new StringTokenizer(syncUserName, "_");
        st.nextToken();
        iEmp = Integer.parseInt(st.nextToken());

    }

    /**
     * syncData, sync data
     */
    public void sync() throws Exception {
        SyncAgent syncAgent = SyncAgent.getInstance(null);
        SyncSummary syncSummary =
                syncAgent.sync(SyncDirection.TWO_WAY, null, null);
        System.out.println("dataSyncSummary:\n"
                + syncSummary.toString());

        // second if checkin skipped
        if (syncSummary.syncErrorMessages != null
                && syncSummary.syncErrorMessages.contains(
                "PVS-" + SyncException.PVS_CHECK_IN_SKIPPED + ":")) {
            System.out.println("\nWill sync again since last sync only refreshed def changes.\n");
            syncSummary =
                    syncAgent.sync(SyncDirection.TWO_WAY, null, null);
            System.out.println("syncSummary:\n"
                    + syncSummary.toString());
        }

        // third if checkin skipped
        if (syncSummary.syncErrorMessages != null
                && syncSummary.syncErrorMessages.contains(
                "PVS-" + SyncException.PVS_CHECK_IN_SKIPPED + ":")) {
            System.out.println("\nWill sync again since last sync only refreshed def changes.\n");
            syncSummary =
                    syncAgent.sync(SyncDirection.TWO_WAY, null, null);
            System.out.println("syncSummary:\n"
                    + syncSummary.toString());
        }

        syncAgent.destroy();
        if (syncSummary.syncException != null) {
            throw syncSummary.syncException;
        }
    }

    /**
     * Create some new tasks in the pervasync client
     * @throws Exception
     */
    public void insert() throws Exception {

        Connection pervasyncClientDbSystemConn =
                pervasyncClientDbSystemDataSource.getConnection();
        pervasyncClientDbSystemConn.setAutoCommit(false);

        totInsertCount = 0;

        long beginTime = System.currentTimeMillis();
        PreparedStatement pstmtTsk =
                pervasyncClientDbSystemConn.prepareStatement("INSERT INTO "
                + pervasyncClientDemoSchema
                + ".tasks(ID,NAME,DESCRIPTION,ASSIGN_DATE,START_DATE,"
                + "FINISH_DATE,PRIORITY,STATUS,COMMENTS)"
                + " VALUES (?,?,?,?,?,?,?,?,?)\n");
        PreparedStatement pstmtEmpTsk =
                pervasyncClientDbSystemConn.prepareStatement("INSERT INTO "
                + pervasyncClientDemoSchema
                + ".emp_task(EMP_ID, TASK_ID)"
                + " VALUES (?,?)\n");
        pstmtEmpTsk.setLong(1, iEmp);
        for (int iTsk = 0; iTsk < taskInsertsPerEmployee; iTsk++) {
            long nextTaskId = getNextTaskId();
            pstmtTsk.setLong(1, nextTaskId);
            pstmtEmpTsk.setLong(2, nextTaskId);
            pstmtTsk.setString(2, "Task " + nextTaskId + " Name ");
            pstmtTsk.setString(3, "Task " + nextTaskId + " Description ");
            pstmtTsk.setTimestamp(4,
                    new Timestamp(System.currentTimeMillis()));
            pstmtTsk.setTimestamp(5,
                    new Timestamp(System.currentTimeMillis()));
            pstmtTsk.setTimestamp(6, null);
            pstmtTsk.setInt(7, iTsk % 3);
            pstmtTsk.setString(8, iTsk % 2 == 0 ? "READY" : "PROCESSING");
            pstmtTsk.setString(9, null);
            totInsertCount += pstmtTsk.executeUpdate();
            totInsertCount += pstmtEmpTsk.executeUpdate();
        }

        pstmtTsk.close();
        pstmtEmpTsk.close();

        pervasyncClientDbSystemConn.commit();
        pervasyncClientDbSystemConn.close();

        long endTime = System.currentTimeMillis();
        System.out.println(" Total inserts=" + totInsertCount);
        System.out.println("Time: " + (endTime - beginTime) / 1000.0
                + " seconds");
    }

    /**
     * Delete some new tasks in the pervasync client
     * @throws Exception
     */
    public void delete() throws Exception {

        Connection pervasyncClientDbSystemConn =
                pervasyncClientDbSystemDataSource.getConnection();
        pervasyncClientDbSystemConn.setAutoCommit(false);
        totDeleteCount = 0;

        long beginTime = System.currentTimeMillis();
        PreparedStatement pstmtTsk =
                pervasyncClientDbSystemConn.prepareStatement("DELETE FROM "
                + pervasyncClientDemoSchema
                + ".tasks WHERE ROWNUM<=? ");
        pstmtTsk.setInt(1, taskDeletesPerEmployee);
        totDeleteCount += pstmtTsk.executeUpdate();
        pstmtTsk.close();

        PreparedStatement pstmtEmpTsk =
                pervasyncClientDbSystemConn.prepareStatement("DELETE FROM "
                + pervasyncClientDemoSchema
                + ".emp_task WHERE TASK_ID NOT IN (select ID FROM "
                + pervasyncClientDemoSchema
                + ".tasks)");
        totDeleteCount += pstmtEmpTsk.executeUpdate();
        pstmtEmpTsk.close();

        pervasyncClientDbSystemConn.commit();
        pervasyncClientDbSystemConn.close();

        long endTime = System.currentTimeMillis();
        System.out.println(" Total deletes=" + totDeleteCount);
        System.out.println("Time: " + (endTime - beginTime) / 1000.0
                + " seconds");
    }

    /**
     * Update some new tasks in the pervasync client
     * @throws Exception
     */
    public void update() throws Exception {

        Connection pervasyncClientDbSystemConn =
                pervasyncClientDbSystemDataSource.getConnection();
        pervasyncClientDbSystemConn.setAutoCommit(false);
        totUpdateCount = 0;

        long beginTime = System.currentTimeMillis();
        PreparedStatement pstmtTsk =
                pervasyncClientDbSystemConn.prepareStatement("UPDATE "
                + pervasyncClientDemoSchema
                + ".tasks SET NAME=NAME||'_S' WHERE ROWNUM<=? ");
        pstmtTsk.setInt(1, taskUpdatesPerEmployee);
        totUpdateCount += pstmtTsk.executeUpdate();

        pstmtTsk.close();

        PreparedStatement pstmtExecUpdate =
                pervasyncClientDbSystemConn.prepareStatement("UPDATE "
                + pervasyncClientDemoSchema
                + ".executives SET BIOGRAPHY=CONCAT('From client: ', BIOGRAPHY)");
        totUpdateCount += pstmtExecUpdate.executeUpdate();
        pstmtExecUpdate.close();

        pervasyncClientDbSystemConn.commit();
        pervasyncClientDbSystemConn.close();

        long endTime = System.currentTimeMillis();
        System.out.println(" Total Updates=" + totUpdateCount);
        System.out.println("Time: " + (endTime - beginTime) / 1000.0
                + " seconds");
    }

    long getNextTaskId() throws Exception {
        long nextTaskId = 0;

        // the simplest way to get next value
        nextTaskId =
                syncClientAdmin.sequenceNextval("schema1", "task_id_seq");
                //syncClientAdmin.sequenceNextval(pervasyncClientDemoSchema, "task_id_seq");

        // just to test the API
        //syncClientAdmin.sequenceCurrval(pervasyncClientDemoSchema,
        syncClientAdmin.sequenceCurrval("schema1", 
                "task_id_seq");


        // Another way:
        /*
        //syncClientAdmin.sequenceCurrval(pervasyncClientDemoSchema, "task_id_seq");
        syncClientAdmin.sequenceCurrval("schema1", "task_id_seq");

        Connection clientAdminDbConn = syncClientAdmin.getClientAdminDbConnection();
        PreparedStatement pstmt =
        clientAdminDbConn.prepareStatement("SELECT " +
        pervasyncClientDemoSchema +
        ".task_id_seq.NEXTVAL FROM DUAL");
        ResultSet rs = pstmt.executeQuery();
        if (rs.next()) {
        nextTaskId = rs.getLong(1);
        }
        rs.close();
        pstmt.close();
         */

        return nextTaskId;
    }
}
