/*
 *  Copyright (C) 2015 Stichting Akvo (Akvo Foundation)
 *
 *  This file is part of Akvo FLOW.
 *
 *  Akvo FLOW is free software: you can redistribute it and modify it under the terms of
 *  the GNU Affero General Public License (AGPL) as published by the Free Software Foundation,
 *  either version 3 of the License or any later version.
 *
 *  Akvo FLOW is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *  See the GNU Affero General Public License included below for more details.
 *
 *  The full license text can also be seen at <http://www.gnu.org/licenses/agpl.html>.
 */

package org.akvo.gae.remoteapi;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.akvo.flow.events.EventUtils.Kind;
import org.codehaus.jackson.map.ObjectMapper;
import org.postgresql.util.PGobject;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Query;

/*
 * Exports GAE datastore data to PostgreSQL
 * Notes:
 * PG version 9.4+, with local connections set to 'trust'
 * The database must be created upfront
 */
public class ExportDataToPG implements Process {

    private static final int BATCH_SIZE = 2000;
    private static final String[] KINDS = {
            Kind.SURVEY_GROUP, Kind.FORM, Kind.QUESTION_GROUP,
            Kind.QUESTION, Kind.DATA_POINT, Kind.FORM_INSTANCE,
            Kind.DEVICE_FILE, Kind.ANSWER
    };
    private static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS %s (id bigint, created_at bigint, payload jsonb)";
    private static final String INSERT = "INSERT INTO %s VALUES (?, ?, ?)";

    private static final Map<String, String> TABLE_NAME = new HashMap<>();

    static {
        TABLE_NAME.put(Kind.ANSWER, "answer");
        TABLE_NAME.put(Kind.DATA_POINT, "data_point");
        TABLE_NAME.put(Kind.DEVICE_FILE, "device_file");
        TABLE_NAME.put(Kind.FORM, "form");
        TABLE_NAME.put(Kind.FORM_INSTANCE, "form_instance");
        TABLE_NAME.put(Kind.QUESTION, "question");
        TABLE_NAME.put(Kind.QUESTION_GROUP, "question_group");
        TABLE_NAME.put(Kind.SURVEY_GROUP, "survey_group");
    }

    @Override
    public void execute(DatastoreService ds, String[] args) throws Exception {

        Class.forName("org.postgresql.Driver");
        String dbName = args[0].replaceAll("-", "_");
        String url = String.format("jdbc:postgresql://localhost/%s?user=postgres", dbName);
        Connection conn = DriverManager.getConnection(url);
        conn.setAutoCommit(false);

        long t0 = System.currentTimeMillis();
        ObjectMapper om = new ObjectMapper();

        for (String kind : KINDS) {

            String tableName = TABLE_NAME.get(kind);

            PreparedStatement ps0 = conn.prepareStatement(String.format(CREATE_TABLE, tableName));
            ps0.execute();
            conn.commit();
            ps0.close();

            Query q = new Query(kind);

            PreparedStatement ps = conn
                    .prepareStatement(String.format(INSERT, tableName));

            long i = 0;
            long t1 = System.currentTimeMillis();

            for (Entity e : ds.prepare(q)
                    .asIterable(FetchOptions.Builder.withChunkSize(BATCH_SIZE))) {

                Date createdDateTime = (Date) e.getProperty("createdDateTime");
                long createdAt = createdDateTime == null ? 0 : createdDateTime.getTime();

                String val = om.writeValueAsString(e).replaceAll("\\\\u0000", "");

                PGobject payload = new PGobject();
                payload.setType("jsonb");
                payload.setValue(val);

                Long entityId = e.getKey().getId();
                Long parentId = e.getKey().getParent() == null ? 0 : e.getKey().getParent().getId();

                ps.setLong(1, entityId + parentId);
                ps.setLong(2, createdAt);
                ps.setObject(3, payload);

                ps.executeUpdate();
                i++;

                if (i % BATCH_SIZE == 0) {
                    System.out.print(".");
                    conn.commit();
                }

            }

            ps.close();
            conn.commit();
            System.out.println("\n");
            System.out.println(kind + " " + (System.currentTimeMillis() - t1) + " ms");
        }
        conn.close();
        System.out.println("Total " + (System.currentTimeMillis() - t0) + " ms");
    }
}

@@ -953,20 +953,21 @@
 ort 
-org.codehaus
+com.fasterxml
 .jac
@@ -975,11 +975,16 @@
 son.
-map
+databind
 .Obj

Fix dependency in remote script