/*
 *  Copyright 2018 Philipp Niedermayer (github.com/eltos)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package eltos.simpledialogfragment.input;

import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import com.google.android.material.textfield.TextInputLayout;
import android.telephony.PhoneNumberFormattingTextWatcher;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.TextView;

import eltos.simpledialogfragment.CustomViewDialog;
import eltos.simpledialogfragment.R;

/**
 * An simple dialog with an input field. Supports suggestions, input validations and
 * max length options.
 *
 * Results:
 *      TEXT    String      The entered text
 *
 * Created by eltos on 14.10.2015.
 */
public class SimpleInputDialog extends CustomViewDialog<SimpleInputDialog> {

    public static final String TAG = "SimpleInputDialog.";

    public static final String
            TEXT = TAG + "text";


    public static SimpleInputDialog build(){
        return new SimpleInputDialog();
    }


    /**
     * Sets the EditText's hint
     *
     * @param hint the hint as string
     * @return this instance
     */
    public SimpleInputDialog hint(String hint){ return setArg(HINT, hint); }

    /**
     * Sets the EditText's hint
     *
     * @param hintResourceId the hint as android string resource
     * @return this instance
     */
    public SimpleInputDialog hint(@StringRes int hintResourceId){ return setArg(HINT, hintResourceId); }

    /**
     * Sets the EditText's initial text
     *
     * @param text initial text as string
     * @return this instance
     */
    public SimpleInputDialog text(String text){ return setArg(TEXT, text); }

    /**
     * Sets the EditText's initial text
     *
     * @param textResourceId initial text as android string resource
     * @return this instance
     */
    public SimpleInputDialog text(@StringRes int textResourceId){ return setArg(TEXT, textResourceId); }

    /**
     * Sets the input type
     * The default is {@link InputType#TYPE_CLASS_TEXT}.
     *
     * @param inputType the InputType
     * @return this instance
     */
    public SimpleInputDialog inputType(int inputType){ return setArg(INPUT_TYPE, inputType); }

    /**
     * Allow empty input. Default is to disable the positive button until text is entered.
     *
     * @param allow whether to allow empty input
     * @return this instance
     */
    public SimpleInputDialog allowEmpty(boolean allow){ return setArg(ALLOW_EMPTY, allow); }

    /**
     * Sets a max limit to the EditText.
     *
     * @param maxLength the maximum text length
     * @return this instance
     */
    public SimpleInputDialog max(int maxLength){ return setArg(MAX_LENGTH, maxLength); }

    /**
     * Provide an array of suggestions to be shown while the user is typing
     *
     * @param context a context to resolve the resource ids
     * @param stringResourceIds suggestion array as android string resources
     * @return this instance
     */
    public SimpleInputDialog suggest(Context context, int[] stringResourceIds){
        String[] strings = new String[stringResourceIds.length];
        for (int i = 0; i < stringResourceIds.length; i++) {
            strings[i] = context.getString(stringResourceIds[i]);
        }
        return suggest(strings);
    }

    /**
     * Provide an array of suggestions to be shown while the user is typing
     *
     * @param strings suggestion string array
     * @return this instance
     */
    public SimpleInputDialog suggest(String[] strings){
        getArguments().putStringArray(SUGGESTIONS, strings);
        return this;
    }




    public interface InputValidator {
        /**
         * Let the hosting fragment or activity implement this interface to control
         * when a user can proceed or to display an error message on an invalid input.
         * The method is called every time the user hits the positive button
         *
         * @param dialogTag the tag of this fragment
         * @param input the text entered by the user
         * @param extras the extras passed with {@link SimpleInputDialog#extra(Bundle)}
         * @return an error message to display or null if the input is valid
         */
        String validate(String dialogTag, @Nullable String input, @NonNull Bundle extras);
    }




    protected static final String
            HINT = TAG + "hint",
            INPUT_TYPE = TAG + "input_type",
            ALLOW_EMPTY = TAG + "allow_empty",
            MAX_LENGTH = TAG + "max_length",
            SUGGESTIONS = TAG + "suggestions";

    private AutoCompleteTextView mInput;
    private TextInputLayout mInputLayout;


    protected String onValidateInput(@Nullable String input){
        Bundle extras = getExtras();
        if (getTargetFragment() instanceof InputValidator) {
            return ((InputValidator) getTargetFragment())
                    .validate(getTag(), input, extras);
        }
        if (getActivity() instanceof InputValidator) {
            return ((InputValidator) getActivity())
                    .validate(getTag(), input, extras);
        }
        return null;
    }



    /**
     * @return the current text or null
     */
    @Nullable
    public String getText(){
        return mInput.getText() != null ? mInput.getText().toString() : null;
    }

    public boolean isInputEmpty(){
        return getText() == null || getText().trim().isEmpty();
    }

    /**
     * Helper for opening the soft keyboard
     */
    public void openKeyboard(){
        InputMethodManager imm = (InputMethodManager) getActivity()
                .getSystemService(Context.INPUT_METHOD_SERVICE);
        if (imm != null) {
            imm.showSoftInput(mInput, InputMethodManager.SHOW_IMPLICIT);
        }
    }

    @Override
    public View onCreateContentView(Bundle savedInstanceState) {
        // inflate and set your custom view here
        View view = inflate(R.layout.simpledialogfragment_input);
        mInput = (AutoCompleteTextView) view.findViewById(R.id.editText);
        mInputLayout = (TextInputLayout) view.findViewById(R.id.inputLayout);

        // Note: setting TYPE_CLASS_TEXT as default is very important!
        mInput.setInputType(getArguments().getInt(INPUT_TYPE, InputType.TYPE_CLASS_TEXT));
        if ((getArguments().getInt(INPUT_TYPE) & InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_PHONE) {
            // format phone number automatically
            mInput.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
        }
        //mInput.setHint(getArgString(HINT));
        mInputLayout.setHint(getArgString(HINT));
        if (getArguments().getInt(MAX_LENGTH) > 0) {
            mInputLayout.setCounterMaxLength(getArguments().getInt(MAX_LENGTH));
            mInputLayout.setCounterEnabled(true);
        }


        if (savedInstanceState != null) {
            mInput.setText(savedInstanceState.getString(TEXT));
        } else {
            mInput.setText(getArgString(TEXT));
            mInput.setSelection(0, mInput.length());
        }

        mInput.setImeOptions(EditorInfo.IME_ACTION_DONE);
        mInput.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if (actionId == EditorInfo.IME_ACTION_DONE){
                    pressPositiveButton();
                    return true;
                }
                return false;
            }
        });

        mInput.addTextChangedListener(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) {
                setPositiveButtonEnabled(posEnabled());
            }
        });

        // Auto complete
        String[] suggestionList = getArguments().getStringArray(SUGGESTIONS);
        if (suggestionList != null) {
            ArrayAdapter<String> adapter = new ArrayAdapter<>(getContext(),
                    // android.R.layout.simple_dropdown_item_1line
                    android.R.layout.simple_list_item_1, suggestionList);
            mInput.setAdapter(adapter);
            mInput.setThreshold(1);
        }

        return view;
    }

    protected boolean posEnabled(){
        return (!isInputEmpty() || getArguments().getBoolean(ALLOW_EMPTY)) && (getText() == null
                || getText().length() <= getArguments().getInt(MAX_LENGTH, getText().length()));
    }



    @Override
    protected void onDialogShown() {
        setPositiveButtonEnabled(posEnabled());
        //mInput.requestFocus();
        openKeyboard();
    }

    @Override
    protected boolean acceptsPositiveButtonPress() {
        String input = getText();
        String error = onValidateInput(input);
        if (error == null) {
            return true;
        } else {
            mInputLayout.setError(error);
            mInputLayout.setErrorEnabled(true);
            return false;
        }
    }


    @Override
    public Bundle onResult(int which) {
        Bundle result = new Bundle();
        result.putString(TEXT, getText());
        return result;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString(TEXT, getText());
    }
}