package org.commcare.android.framework;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

import org.javarosa.core.services.locale.Localization;

import java.lang.reflect.Field;

/**
 * Functions for managing the setup and persistence of activity fields with
 * '@UiElement' annotations.
 *
 * @author Phillip Mates (pmates@dimagi.com).
 */
public class ManagedUiFramework {

    public static boolean isManagedUi(Class c) {
        return c.isAnnotationPresent(ManagedUi.class);
    }

    public static void setContentView(CommCareActivity activity) {
        if (activity.usesUIController()) {
            activity.setContentView(
                    getUIController(activity).getClass().getAnnotation(ManagedUi.class).value());
        } else {
            activity.setContentView(
                    activity.getClass().getAnnotation(ManagedUi.class).value());
        }
    }

    /**
     * Set text for activity's UiElement annotated fields
     */
    public static void loadUiElements(CommCareActivity activity) {

        Class classHoldingFields = getClassHoldingFields(activity);

        for (Field f : classHoldingFields.getDeclaredFields()) {
            if (f.isAnnotationPresent(UiElement.class)) {
                UiElement element = f.getAnnotation(UiElement.class);
                try {
                    f.setAccessible(true);

                    try {
                        View v = activity.findViewById(element.value());
                        setValueOfField(activity, v, f);

                        String localeString = element.locale();
                        if (!"".equals(localeString)) {
                            if (v instanceof EditText) {
                                ((EditText)v).setHint(Localization.get(localeString));
                            } else if (v instanceof TextView) {
                                ((TextView)v).setText(Localization.get(localeString));
                            } else {
                                throw new RuntimeException("Can't set the text for a " + v.getClass().getName() + " View!");
                            }
                        }
                    } catch (IllegalArgumentException e) {
                        e.printStackTrace();
                        throw new RuntimeException("Bad Object type for field " + f.getName());
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException("Couldn't access the activity field for some reason");
                    }
                } finally {
                    f.setAccessible(false);
                }
            }
        }
    }

    /**
     * Restore activity UiElement annotated fields with text, visibility, and
     * enabled settings stored in bundle
     */
    public static void restoreUiElements(CommCareActivity activity,
                                         Bundle savedInstanceState) {

        Class classHoldingFields = getClassHoldingFields(activity);

        for (Field f : classHoldingFields.getDeclaredFields()) {
            if (f.isAnnotationPresent(UiElement.class)) {
                UiElement element = f.getAnnotation(UiElement.class);
                try {
                    f.setAccessible(true);

                    try {
                        View v = activity.findViewById(element.value());
                        setValueOfField(activity, v, f);
                        restoredFromSaved(v, f, element, savedInstanceState);
                    } catch (IllegalArgumentException e) {
                        e.printStackTrace();
                        throw new RuntimeException("Bad Object type for field " + f.getName());
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException("Couldn't access the activity field for some reason");
                    }
                } finally {
                    f.setAccessible(false);
                }
            }
        }
    }

    @SuppressWarnings("ResourceType")
    private static void restoredFromSaved(View v, Field f, UiElement element, Bundle bundle) {
        if (bundle != null) {
            if (v != null) {
                final String elementKey = getElementKey(element);
                if (isFieldInBundle(elementKey, bundle)) {
                    v.setVisibility(bundle.getInt(elementKey + "_visibility"));
                    v.setEnabled(bundle.getBoolean(elementKey + "_enabled"));
                    if (v instanceof TextView) {
                        ((TextView)v).setText(bundle.getString(elementKey + "_text"));
                    }
                }
            } else {
                Log.d("loadFields", "NullPointerException when trying to find view with id: " +
                        element.value() + ", element is: " + f + " (" + f.getName() + ")");
            }
        }
    }

    private static String getElementKey(UiElement element) {
        return String.valueOf(element.value());
    }

    private static boolean isFieldInBundle(String elementKey, Bundle bundle) {
        return bundle.containsKey(elementKey + "_visibility");
    }

    /**
     * Store the state of the activity's UiElement annotated fields.
     */
    public static Bundle saveUiStateToBundle(CommCareActivity activity) {

        Bundle bundle = new Bundle();
        Class classHoldingFields = getClassHoldingFields(activity);

        for (Field f : classHoldingFields.getDeclaredFields()) {
            if (f.isAnnotationPresent(UiElement.class)) {
                UiElement element = f.getAnnotation(UiElement.class);
                try {
                    f.setAccessible(true);
                    try {
                        View v;
                        if (activity.usesUIController()) {
                            v = (View)f.get(getUIController(activity));
                        } else {
                            v = (View)f.get(activity);
                        }
                        String elementKey = getElementKey(element);
                        int vis = v.getVisibility();
                        bundle.putInt(elementKey + "_visibility", vis);
                        boolean enabled = v.isEnabled();
                        bundle.putBoolean(elementKey + "_enabled", enabled);
                        if (v instanceof TextView) {
                            bundle.putString(elementKey + "_text", ((TextView)v).getText().toString());
                        }
                    } catch (IllegalArgumentException e) {
                        e.printStackTrace();
                        throw new RuntimeException("Bad Object type for field " + f.getName());
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException("Couldn't access the activity field for some reason");
                    }
                } finally {
                    f.setAccessible(false);
                }
            }
        }
        return bundle;
    }

    private static Class getClassHoldingFields(CommCareActivity activity) {
        if (activity.usesUIController()) {
            return getUIController(activity).getClass();
        } else {
            return activity.getClass();
        }
    }

    private static void setValueOfField(CommCareActivity activity, View v, Field f)
            throws IllegalAccessException {
        if (activity.usesUIController()) {
            f.set(getUIController(activity), v);
        } else {
            f.set(activity, v);
        }
    }

    private static CommCareActivityUIController getUIController(CommCareActivity withController) {
        return ((WithUIController)withController).getUIController();

    }
}

@@ -1142,37 +1142,38 @@
 dingFields = get
-Class
+Object
 HoldingFields(ac
@@ -1171,32 +1171,43 @@
 Fields(activity)
+.getClass()
 ;%0A%0A        for (
@@ -3052,37 +3052,38 @@
 dingFields = get
-Class
+Object
 HoldingFields(ac
@@ -3081,32 +3081,43 @@
 Fields(activity)
+.getClass()
 ;%0A%0A        for (
@@ -5460,35 +5460,37 @@
 ();%0A        
-Class class
+Object object
 HoldingField
@@ -5492,29 +5492,30 @@
 Fields = get
-Class
+Object
 HoldingField
@@ -5543,37 +5543,38 @@
  for (Field f : 
-class
+object
 HoldingFields.ge
@@ -5563,32 +5563,43 @@
 ctHoldingFields.
+getClass().
 getDeclaredField
@@ -5858,254 +5858,44 @@
 ew v
-;%0A                        if (activity.usesUIController()) %7B%0A                            v = (View)f.get(getUIController(activity));%0A                        %7D else %7B%0A                            v = (View)f.get(activity);%0A                        %7D
+ = (View)f.get(objectHoldingFields);
 %0A   
@@ -6953,22 +6953,24 @@
 tic 
-Class getClass
+Object getObject
 Hold
@@ -7095,27 +7095,16 @@
 ctivity)
-.getClass()
 ;%0A      
@@ -7141,27 +7141,16 @@
 activity
-.getClass()
 ;%0A      

simplify logic for object to set fields in