package org.akvo.gae.remoteapi;

import java.util.Date;
import java.util.List;
import java.util.Map;

import org.akvo.flow.events.EventUtils;
import org.akvo.flow.events.EventUtils.EventTypes;
import org.akvo.flow.events.EventUtils.Kind;

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;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;

public class ExportDataToEventLog implements Process {

    private static final int BATCH_SIZE = 1000;
    private static final String EVENT_QUEUE = "EventQueue";
    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 };

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

        if (args.length != 2) {
            System.err
                    .println("Usage: \n"
                            + "This data script depends on code in GAE for generating json event data.\n"
                            + "* Compile the appengine code by running 'ant compile' in the GAE directory. \n"
                            + "* Add GAE/war/WEB-INF/classes and GAE/war/WEB-INF/lib/* to the classpath. \n"
                            + "* Example cli call: \n"
                            + "    java -classpath \"bin:lib/*:../../GAE/war/WEB-INF/classes:../../GAE/war/WEB-INF/lib/*\"\n"
                            + "         org.akvo.gae.remoteapi.RemoteAPI\n"
                            + "         ExportDataToEventLog\n"
                            + "         <app-id>\n"
                            + "         <remote-api-email>\n"
                            + "         <remote-api-password>\n"
                            + "         <app-id>\n"
                            + "         <event-notification-endpoint>");
            return;
        }

        final String orgId = args[0];
        final String servicesEndpoint = args[1];

        final Date firstEvent = findFirstEventDate(ds);

        for (final String kind : kinds) {
            System.out.println("Exporting " + kind);
            int batch = 0;
            Query kindQuery = new Query(kind);

            final EventTypes eventTypes = EventUtils
                    .getEventAndActionType(kind);

            Iterable<Entity> entities = ds.prepare(kindQuery).asIterable(
                    FetchOptions.Builder.withChunkSize(BATCH_SIZE));

            Iterable<Entity> entitiesNotInEventQueue = Iterables.filter(
                    entities, new Predicate<Entity>() {
                        @Override
                        public boolean apply(Entity entity) {
                            Date date = (Date) entity
                                    .getProperty("createdDateTime");
                            return date.before(firstEvent);
                        }
                    });

            Iterable<Map<String, Object>> events = Iterables.transform(
                    entitiesNotInEventQueue,
                    new Function<Entity, Map<String, Object>>() {
                        @Override
                        public Map<String, Object> apply(Entity entity) {
                            return createEvent(entity, eventTypes, orgId);
                        }
                    });

            Iterable<List<Map<String, Object>>> eventBatches = Iterables
                    .partition(events, BATCH_SIZE);

            for (List<Map<String, Object>> eventBatch : eventBatches) {
                System.out.println("  Batch #" + batch);
                batch++;
                EventUtils.sendEvents(servicesEndpoint + orgId, eventBatch);
            }
        }
    }

    private static Date findFirstEventDate(DatastoreService ds) {

        List<Entity> eventsInQueue = ds.prepare(new Query(EVENT_QUEUE)).asList(
                FetchOptions.Builder.withDefaults().limit(1));

        if (eventsInQueue.isEmpty()) {
            return new Date(Long.MAX_VALUE);
        } else {
            return (Date) eventsInQueue.get(0).getProperty("createdDateTime");
        }
    }

    private static Map<String, Object> createEvent(Entity entity,
            EventTypes eventTypes, String orgId) {

        Map<String, Object> source = EventUtils.newSource("import");
        Map<String, Object> context = EventUtils.newContext(new Date(), source);
        context.put("import", true);
        Map<String, Object> entityMap = EventUtils.newEntity(eventTypes.type,
                entity.getKey().getId());
        EventUtils.populateEntityProperties(eventTypes.type, entity, entityMap);
        Map<String, Object> event = EventUtils.newEvent(orgId,
                eventTypes.action + "Created", entityMap, context);
        return event;
    }
}

@@ -942,16 +942,119 @@
 WER %7D;%0A%0A
+    // The timestamp used for imported data.%0A    private static final Date IMPORT_DATE = new Date(0);%0A%0A
     @Ove
@@ -3120,32 +3120,328 @@
 atedDateTime%22);%0A
+                            if (date == null) %7B%0A                                // If the createdDateTime is not present, assume%0A                                // it's old enough to be part of the data import%0A                                return true;%0A                            %7D else %7B%0A    
                 
@@ -3480,24 +3480,54 @@
 irstEvent);%0A
+                            %7D%0A
             
@@ -5009,24 +5009,41 @@
 = EventUtils
+%0A                
 .newContext(
@@ -5046,18 +5046,19 @@
 ext(
-new Date()
+IMPORT_DATE
 , so

Set timestamp of imported data to Jan 1 1970