package org.commcare.xml;

import android.net.ParseException;
import android.net.Uri;
import android.util.Pair;

import net.sqlcipher.database.SQLiteDatabase;

import org.commcare.CommCareApplication;
import org.commcare.android.database.user.models.ACase;
import org.commcare.cases.model.Case;
import org.commcare.engine.references.JavaHttpReference;
import org.commcare.interfaces.HttpRequestEndpoints;
import org.commcare.logging.AndroidLogger;
import org.commcare.models.database.SqlStorage;
import org.commcare.models.database.user.models.AndroidCaseIndexTable;
import org.commcare.models.database.user.models.EntityStorageCache;
import org.commcare.utils.FileUtil;
import org.commcare.utils.GlobalConstants;
import org.commcare.xml.bulk.BulkProcessingCaseXmlParser;
import org.javarosa.core.io.StreamsUtil;
import org.javarosa.core.reference.InvalidReferenceException;
import org.javarosa.core.reference.Reference;
import org.javarosa.core.reference.ReferenceManager;
import org.javarosa.core.services.Logger;
import org.javarosa.core.services.storage.IStorageUtilityIndexed;
import org.javarosa.core.util.PropertyUtils;
import org.javarosa.xml.util.InvalidStructureException;
import org.kxml2.io.KXmlParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;

/**
 * A bulk processing parser for the android platform. Provides superior performance when
 * processing high case loads during syncing and/or processing.
 *
 * @author ctsims
 */
public class AndroidBulkCaseXmlParser extends BulkProcessingCaseXmlParser {
    private HttpRequestEndpoints generator;
    private final EntityStorageCache mEntityCache;
    private final AndroidCaseIndexTable mCaseIndexTable;
    private final SqlStorage<ACase> storage;

    public AndroidBulkCaseXmlParser(KXmlParser parser,
                                    SqlStorage<ACase> storage,
                                    HttpRequestEndpoints generator) {
        super(parser);
        this.generator = generator;
        mEntityCache = new EntityStorageCache("case");
        mCaseIndexTable = new AndroidCaseIndexTable();
        this.storage = storage;
    }

    protected SQLiteDatabase getDbHandle() {
        return CommCareApplication.instance().getUserDbHandle();
    }

    @Override
    protected Case buildCase(String name, String typeId) {
        return new ACase(name, typeId);
    }

    @Override
    protected void performBulkRead(Set<String> currentBulkReadSet, Map<String, Case> currentOperatingSet) throws InvalidStructureException, IOException, XmlPullParserException {
        SQLiteDatabase db;
        db = getDbHandle();
        db.beginTransaction();
        try {
            for (ACase c : storage.getBulkRecordsForIndex(Case.INDEX_CASE_ID, currentBulkReadSet)) {
                currentOperatingSet.put(c.getCaseId(), c);
            }
            db.setTransactionSuccessful();
        } finally {
            db.endTransaction();
        }
    }

    @Override
    protected void performBulkWrite(SortedMap<String, Case> writeLog) throws IOException {
        SQLiteDatabase db;
        db = getDbHandle();
        db.beginTransaction();
        ArrayList<Integer> recordIdsToWipe = new ArrayList<>();
        try {
            for(Case c : writeLog.values()) {
                storage.write(c);
                recordIdsToWipe.add(c.getID());
                mCaseIndexTable.indexCase(c);
            }
            mEntityCache.invalidateCaches(recordIdsToWipe);
            mCaseIndexTable.clearCaseIndices(recordIdsToWipe);
            for(Case c : writeLog.values()) {
                mCaseIndexTable.indexCase(c);
            }
            db.setTransactionSuccessful();
        } finally {
            db.endTransaction();
        }

    }
}

@@ -1365,16 +1365,48 @@
 ayList;%0A
+import java.util.LinkedHashMap;%0A
 import j
@@ -2017,16 +2017,21 @@
         
+     
 SqlStora
@@ -2049,16 +2049,464 @@
 torage,%0A
+                                         HttpRequestEndpoints generator) %7B%0A        this(parser, storage, new EntityStorageCache(%22case%22), new AndroidCaseIndexTable(), generator);%0A    %7D%0A%0A    public AndroidBulkCaseXmlParser(KXmlParser parser,%0A                                    SqlStorage%3CACase%3E storage,%0A                                    EntityStorageCache entityStorageCache,%0A                                    AndroidCaseIndexTable indexTable,%0A
         
@@ -2649,21 +2649,17 @@
 Cache = 
-new E
+e
 ntitySto
@@ -2667,24 +2667,16 @@
 ageCache
-(%22case%22)
 ;%0A      
@@ -2695,32 +2695,17 @@
 Table = 
-new AndroidCaseI
+i
 ndexTabl
@@ -2705,18 +2705,16 @@
 dexTable
-()
 ;%0A      
@@ -3612,22 +3612,26 @@
 lkWrite(
-Sorted
+LinkedHash
 Map%3CStri
@@ -3843,38 +3843,42 @@
             for(
-Case c
+String cid
  : writeLog.valu
@@ -3869,35 +3869,79 @@
  : writeLog.
-values()) %7B
+keySet()) %7B%0A                Case c = writeLog.get(cid);
 %0A           
@@ -4015,54 +4015,8 @@
 ));%0A
-                mCaseIndexTable.indexCase(c);%0A
     
@@ -4140,32 +4140,33 @@
 cordIdsToWipe);%0A
+%0A
             for(
@@ -4165,24 +4165,74 @@
     for(
+String cid : writeLog.keySet()) %7B%0A                
 Case c 
-:
+=
  writeLo
@@ -4237,19 +4237,17 @@
 Log.
-values()) %7B
+get(cid);
 %0A   

Fix for ordering concerns