package org.commcare.models;

import org.javarosa.core.util.PrefixTree;
import org.javarosa.core.util.externalizable.PrototypeFactory;

import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

/**
 * This class overrides the core PrototypeFactory class primarily because we
 * can store our Android hashes as an int and thus store them in a map for faster lookups. Most
 * other functionality is the same, except we override how we store and retrieve hashes
 * so that we can use the Map.
 *
 * @author ctsims
 * @author wspride
 */
public class AndroidPrototypeFactory extends PrototypeFactory {

    private Hashtable<Integer, Class> prototypes;
    private static final HashMap<String, Class> migratedClasses = new HashMap<>();

    static {
        //migratedClasses.put();
    }

    public AndroidPrototypeFactory(PrefixTree classNames) {
        super(AndroidClassHasher.getInstance(), classNames);
    }

    @Override
    protected void lazyInit() {
        initialized = false;
        prototypes = new Hashtable<>();
        super.lazyInit();
    }

    private Integer hashAsInteger(byte[] hash) {
        return (hash[3]) + (hash[2] << 8) + (hash[1] << 16) + (hash[0] << 24);
    }

    @Override
    public Class getClass(byte[] hash) {
        if (!initialized) {
            lazyInit();
        }
        return prototypes.get(hashAsInteger(hash));
    }

    @Override
    protected void storeHash(Class c, byte[] hash) {
        prototypes.put(hashAsInteger(hash), c);
    }


    @Override
    protected void addMigratedClasses() {
        for (Map.Entry<String, Class> c : migratedClasses.entrySet()) {
            addMigratedClass(c.getKey(), c.getValue());
        }
    }

    private void addMigratedClass(String oldClassName, Class newClass) {
        if (!initialized) {
            lazyInit();
        }

        byte[] hash = AndroidClassHasher.getInstance().getClassnameHash(oldClassName);

        if (compareHash(hash, PrototypeFactory.getWrapperTag())) {
            throw new Error("Hash collision! " + oldClassName + " and reserved wrapper tag");
        }

        Class d = getClass(hash);
        if (d != null && d.getName().equals(oldClassName)) {
            throw new Error("Hash collision! " + oldClassName + " and " + d.getName());
        }
        prototypes.put(hashAsInteger(hash), newClass);
    }
}

@@ -21,24 +21,195 @@
 .models;%0D%0A%0D%0A
+import org.commcare.android.javarosa.AndroidXFormExtensions;%0D%0Aimport org.commcare.android.javarosa.IntentCallout;%0D%0Aimport org.commcare.android.javarosa.PollSensorAction;%0D%0A
 import org.j
@@ -965,10 +965,8 @@
     
-//
 migr
@@ -981,16 +981,326 @@
 ses.put(
+%22org.odk.collect.android.jr.extensions.AndroidXFormExtensions%22, AndroidXFormExtensions.class);%0D%0A        migratedClasses.put(%22org.odk.collect.android.jr.extensions.IntentCallout%22, IntentCallout.class);%0D%0A        migratedClasses.put(%22org.odk.collect.android.jr.extensions.PollSensorAction%22, PollSensorAction.class
 );%0D%0A    

Add classname migration for parser extensions