package org.commcare.views;

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.TextView;

import org.commcare.dalvik.R;
import org.commcare.views.widgets.SpinnerWidget;

import java.lang.reflect.Method;
import java.util.Vector;

/**
 * Created by amstone326 on 1/4/17.
 */

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public class Combobox extends AutoCompleteTextView {

    private Vector<String> choices;
    private Vector<String> choicesAllLowerCase;

    private CharSequence lastAcceptableStringEntered = "";

    public Combobox(Context context, Vector<String> choices, boolean addEmptyFirstChoice, int fontSize) {
        super(context);

        this.choices = choices;
        this.choicesAllLowerCase = new Vector<>();
        for (String s : this.choices) {
            choicesAllLowerCase.add(s.toLowerCase());
        }

        String[] items = this.choices.toArray(new String[]{});
        if (addEmptyFirstChoice) {
            items = SpinnerWidget.getChoicesWithEmptyFirstSlot(items);
        }
        setAdapter(new ComboboxAdapter(context, R.layout.custom_spinner_item, items,
                TypedValue.COMPLEX_UNIT_DIP, fontSize));

        setForceIgnoreOutsideTouchWithReflection();
        setThreshold(0);
        setListeners();
        //setValidator(getAfterTextEnteredValidator());
    }

    public String getSelection() {
        String enteredText = getText().toString();
        if (choices.contains(enteredText)) {
            return enteredText;
        }
        return null;
    }

    private boolean setForceIgnoreOutsideTouchWithReflection() {
        try {
            Method method = android.widget.AutoCompleteTextView.class.getMethod("setForceIgnoreOutsideTouch", boolean.class);
            method.invoke(this, true);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    private void setListeners() {
        addTextChangedListener(getWhileTypingValidator());

        setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                showDropDown();
            }
        });
    }

    private TextWatcher getWhileTypingValidator() {
        return new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
            }

            @Override
            public void afterTextChanged(Editable s) {
                if (!isPrefixOfSomeChoiceValue(s.toString())) {
                    // Re-set the entered text to be what it was before this change was made
                    setText(lastAcceptableStringEntered);
                    // Move the cursor to the end of the text
                    setSelection(getText().length());
                } else {
                    lastAcceptableStringEntered = s.toString();
                }
            }
        };
    }

    private boolean isPrefixOfSomeChoiceValue(String text) {
        for (String choice : choicesAllLowerCase) {
            if (choice.startsWith(text.toLowerCase())) {
                return true;
            }
        }
        return false;
    }

    private AutoCompleteTextView.Validator getAfterTextEnteredValidator() {
        return new AutoCompleteTextView.Validator() {

            @Override
            public boolean isValid(CharSequence text) {
                return choices.contains(text.toString());
            }

            @Override
            public CharSequence fixText(CharSequence invalidText) {
                if (choicesAllLowerCase.contains(invalidText.toString().toLowerCase())) {
                    // If the user has entered a valid answer but with different case,
                    // just change the case for them
                    int index = choicesAllLowerCase.indexOf(invalidText.toString().toLowerCase());
                    return choices.get(index);
                } else {
                    // Otherwise delete their answer
                    return "";
                }

            }
        };
    }

    private class ComboboxAdapter extends ArrayAdapter<String> {
        final int textUnit;
        final float textSize;

        public ComboboxAdapter(final Context context, final int textViewResourceId,
                              final String[] objects, int textUnit, float textSize) {
            super(context, textViewResourceId, objects);
            this.textUnit = textUnit;
            this.textSize = textSize;
        }

        @Override
        // Defines the text view parameters for the drop down list entries
        public View getView(int position, View convertView, ViewGroup parent) {
            View view = super.getView(position, convertView, parent);
            TextView tv = (TextView)view.findViewById(android.R.id.text1);
            tv.setTextSize(textUnit, textSize);
            tv.setPadding(10, 10, 10, 10);
            return view;
        }
    }

}

@@ -268,16 +268,51 @@
 wGroup;%0A
+import android.widget.AdapterView;%0A
 import a
@@ -697,24 +697,89 @@
 TextView %7B%0A%0A
+    private static int TEXT_UNIT = TypedValue.COMPLEX_UNIT_DIP;%0A%0A
     private 
@@ -850,17 +850,16 @@
 erCase;%0A
-%0A
     priv
@@ -908,16 +908,97 @@
 ed = %22%22;
+%0A    private int lastValidCursorLocation;%0A    private boolean fixingInvalidEntry;
 %0A%0A    pu
@@ -1577,52 +1577,51 @@
 ems,
-%0A                TypedValue.COMPL
+ fontSize));%0A%0A        setTextSize(T
 EX
+T
 _UNIT
-_DIP
 , fo
@@ -1619,35 +1619,33 @@
 _UNIT, fontSize)
-);%0A
+;
 %0A        setForc
@@ -2810,24 +2810,151 @@
 nt after) %7B%0A
+                if (!fixingInvalidEntry) %7B%0A                    lastValidCursorLocation = getSelectionStart();%0A                %7D
 %0A           
@@ -3157,24 +3157,68 @@
 itable s) %7B%0A
+                fixingInvalidEntry = false;%0A
             
@@ -3265,24 +3265,71 @@
 tring())) %7B%0A
+                    fixingInvalidEntry = true;%0A
             
@@ -3494,12 +3494,11 @@
  // 
-Move
+Put
  the
@@ -3509,30 +3509,25 @@
 sor 
-to the end of the text
+back where it was
 %0A   
@@ -3560,26 +3560,31 @@
 ion(
-getText().length()
+lastValidCursorLocation
 );%0A 
@@ -4950,36 +4950,8 @@
 %3E %7B%0A
-        final int textUnit;%0A
     
@@ -5118,22 +5118,8 @@
 cts,
- int textUnit,
  flo
@@ -5194,46 +5194,8 @@
 s);%0A
-            this.textUnit = textUnit;%0A
     
@@ -5588,16 +5588,17 @@
 ize(
-textUnit
+TEXT_UNIT
 , te

handle resetting cursor more correctly