package it.integry.integrywmsnative.core.di;

import android.app.DatePickerDialog;
import android.content.res.ColorStateList;
import android.text.SpannableString;
import android.util.Log;
import android.view.View;
import android.widget.AutoCompleteTextView;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;

import androidx.annotation.ColorRes;
import androidx.appcompat.widget.AppCompatCheckBox;
import androidx.appcompat.widget.AppCompatMultiAutoCompleteTextView;
import androidx.appcompat.widget.AppCompatTextView;
import androidx.appcompat.widget.LinearLayoutCompat;
import androidx.constraintlayout.widget.Guideline;
import androidx.core.content.res.ResourcesCompat;
import androidx.core.util.Pair;
import androidx.databinding.BindingAdapter;
import androidx.databinding.BindingConversion;
import androidx.databinding.Observable;
import androidx.databinding.ObservableField;

import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;

import it.integry.integrywmsnative.MainApplication;
import it.integry.integrywmsnative.R;
import it.integry.integrywmsnative.core.expansion.BaseDialogFragment;
import it.integry.integrywmsnative.core.expansion.RunnableArgs;
import it.integry.integrywmsnative.core.utility.UtilityBigDecimal;
import it.integry.integrywmsnative.core.utility.UtilityDate;
import it.integry.integrywmsnative.core.utility.UtilityNumber;
import it.integry.integrywmsnative.core.utility.UtilityString;
import it.integry.integrywmsnative.view.dialogs.base.DialogSimpleMessageView;

public class Converters {


    @BindingConversion
    public static String convertBindableToString(BindableString bindableString) {
        if(bindableString != null) return bindableString.get();
        return "";
    }

    @BindingConversion
    public static boolean convertBindableToBoolean(BindableBoolean bindableBoolean) {
        return bindableBoolean.get();
    }

    @BindingConversion
    public static boolean convertObservableToBoolean(ObservableField<Boolean> bindableBoolean) {
        return bindableBoolean.get();
    }

    @BindingAdapter("binding")
    public static void bindEditText(AppCompatTextView view, final BindableInteger bindableInteger) {
        Pair<BindableInteger, TextWatcherAdapter> pair = (Pair) view.getTag(R.id.bound_observable);
        if (pair == null || pair.first != bindableInteger) {
            if (pair != null) {
                view.removeTextChangedListener(pair.second);
            }
            TextWatcherAdapter watcher = new TextWatcherAdapter() {
                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    if(bindableInteger != null) bindableInteger.set(Integer.parseInt(s.toString()));
                }
            };
            view.setTag(R.id.bound_observable, new Pair<>(bindableInteger, watcher));
            view.addTextChangedListener(watcher);
        }
        String newValue = bindableInteger != null && bindableInteger.get() != null ? bindableInteger.get().toString() : "0";
        if (!view.getText().toString().equals(newValue)) {
            view.setText(newValue);
        }
    }

    @BindingAdapter("binding")
    public static void bindEditText(AppCompatTextView view, final ObservableField<String> observableField) {
        Pair<ObservableField<String>, TextWatcherAdapter> pair = (Pair) view.getTag(R.id.bound_observable);
        if (pair == null || pair.first != observableField) {
            if (pair != null) {
                view.removeTextChangedListener(pair.second);
            }
            TextWatcherAdapter watcher = new TextWatcherAdapter() {
                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    if(observableField != null) observableField.set(s.toString());
                }
            };
            view.setTag(R.id.bound_observable, new Pair<>(observableField, watcher));
            view.addTextChangedListener(watcher);
        }
        String newValue = observableField.get();
        if (!view.getText().toString().equals(newValue)) {
            view.setText(newValue);
        }
    }

    @BindingAdapter("binding")
    public static void bindEditText(EditText view, final BindableString bindableString) {
        Pair<BindableString, TextWatcherAdapter> pair = (Pair) view.getTag(R.id.bound_observable);
        if (pair == null || pair.first != bindableString) {
            if (pair != null) {
                view.removeTextChangedListener(pair.second);
            }
            TextWatcherAdapter watcher = new TextWatcherAdapter() {
                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    bindableString.set(s.toString());
                }
            };
            view.setTag(R.id.bound_observable, new Pair<>(bindableString, watcher));
            view.addTextChangedListener(watcher);
        }
        String newValue = bindableString.get();
        if (!view.getText().toString().equals(newValue)) {
            view.setText(newValue);
        }
    }

    @BindingAdapter("binding")
    public static void bindEditText(EditText view, final ObservableField<String> observableString) {
        Pair<ObservableField<String>, TextWatcherAdapter> pair = (Pair) view.getTag(R.id.bound_observable);
        if (pair == null || pair.first != observableString) {
            if (pair != null) {
                view.removeTextChangedListener(pair.second);
            }
            TextWatcherAdapter watcher = new TextWatcherAdapter() {
                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    observableString.set(s.toString());
                }
            };
            view.setTag(R.id.bound_observable, new Pair<>(observableString, watcher));
            view.addTextChangedListener(watcher);
        }
        String newValue = observableString.get();
        if (!view.getText().toString().equals(newValue)) {
            view.setText(newValue);
        }
    }

    @BindingAdapter("binding")
    public static void bindEditText(AutoCompleteTextView view, final ObservableField<String> observableString) {
        Pair<ObservableField<String>, TextWatcherAdapter> pair = (Pair) view.getTag(R.id.bound_observable);
        if (pair == null || pair.first != observableString) {
            if (pair != null) {
                view.removeTextChangedListener(pair.second);
            }
            TextWatcherAdapter watcher = new TextWatcherAdapter() {
                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    observableString.set(s.toString());
                }
            };
            view.setTag(R.id.bound_observable, new Pair<>(observableString, watcher));
            view.addTextChangedListener(watcher);
        }
        String newValue = observableString.get();
        if (!view.getText().toString().equals(newValue)) {
            view.setText(newValue, false);
        }
    }

    @BindingAdapter("binding")
    public static void bindTextInputEditText(TextInputEditText view, final BindableString bindableString) {
        Pair<BindableString, TextWatcherAdapter> pair = (Pair) view.getTag(R.id.bound_observable);
        if (pair == null || pair.first != bindableString) {
            if (pair != null) {
                view.removeTextChangedListener(pair.second);
            }
            TextWatcherAdapter watcher = new TextWatcherAdapter() {
                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    bindableString.set(s.toString());
                }
            };
            view.setTag(R.id.bound_observable, new Pair<>(bindableString, watcher));
            view.addTextChangedListener(watcher);
        }
        String newValue = bindableString.get();
        if (!view.getText().toString().equals(newValue)) {
            view.setText(newValue);
        }
    }

    @BindingAdapter("binding")
    public static void bindTextInputEditTextBigDecimal(TextInputEditText view, final ObservableField<BigDecimal> observableBigDecimal) {
        Pair<ObservableField<BigDecimal>, TextWatcherAdapter> pair = (Pair) view.getTag(R.id.bound_observable);
        if (pair == null || pair.first != observableBigDecimal) {
            if (pair != null) {
                view.removeTextChangedListener(pair.second);
            }
            TextWatcherAdapter watcher = new TextWatcherAdapter() {
                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    BigDecimal value = null;
                    if(!UtilityString.isNullOrEmpty(s.toString())) value = new BigDecimal(s.toString());
                    observableBigDecimal.set(value);
                }
            };
            view.setTag(R.id.bound_observable, new Pair<>(observableBigDecimal, watcher));
            view.addTextChangedListener(watcher);
        }
        BigDecimal newValue = observableBigDecimal.get();
        BigDecimal viewValue = view.getText().toString().trim().length() > 0 ? new BigDecimal(view.getText().toString()) : null;

        if(!UtilityBigDecimal.equalsTo(viewValue, newValue)) {
            view.setText(UtilityNumber.decimalToString(newValue));
        }
    }

    @BindingAdapter("binding")
    public static void bindTextInputEditTextString(TextInputEditText view, final ObservableField<String> observableString) {
        Pair<ObservableField<String>, TextWatcherAdapter> pair = (Pair) view.getTag(R.id.bound_observable);
        if (pair == null || pair.first != observableString) {
            if (pair != null) {
                view.removeTextChangedListener(pair.second);
            }
            TextWatcherAdapter watcher = new TextWatcherAdapter() {
                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    observableString.set(s.toString());
                }
            };
            view.setTag(R.id.bound_observable, new Pair<>(observableString, watcher));
            view.addTextChangedListener(watcher);
        }
        String newValue = observableString.get();
        String viewValue = view.getText().toString();

        if(!viewValue.equalsIgnoreCase(newValue)) {
            view.setText(newValue);
        }
    }

    @BindingAdapter(value = {"binding", "parentView", "warningOnOldDates"}, requireAll = false)
    public static void bindTextInputEditTextDate(TextInputEditText view, final ObservableField<Date> observableDate, BaseDialogFragment parentFragment, boolean warningOnOldDates) {
        Pair<ObservableField<Date>, TextWatcherAdapter> pair = (Pair) view.getTag(R.id.bound_observable);
        if (pair == null || pair.first != observableDate) {
            if (pair != null) {
                view.removeTextChangedListener(pair.second);
            }
            TextWatcherAdapter watcher = new TextWatcherAdapter() {
                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                }
            };
            view.setTag(R.id.bound_observable, new Pair<>(observableDate, watcher));
            view.addTextChangedListener(watcher);

            RunnableArgs<View> onClick = v -> {
                // Get Current Date
                Calendar c = UtilityDate.getCalendarInstance();

                if(observableDate.get() != null) {
                    c.setTime(observableDate.get());
                }

                int mYear = c.get(Calendar.YEAR);
                int mMonth = c.get(Calendar.MONTH);
                int mDay = c.get(Calendar.DAY_OF_MONTH);

                DatePickerDialog datePickerDialog = new DatePickerDialog(view.getContext(),
                        (cView, year, month, day) -> {
                            Date resultDate = new GregorianCalendar(year, month, day).getTime();
                            
                            if(parentFragment != null && warningOnOldDates && resultDate.before(new Date())) {
                                DialogSimpleMessageView
                                        .makeWarningDialog(new SpannableString("Hai scelto una data precedente a quella odierna. Continuare?"), null, () -> {
                                            view.setText(UtilityDate.formatDate(resultDate, UtilityDate.COMMONS_DATE_FORMATS.DMY_HUMAN));
                                            observableDate.set(resultDate);
                                        }, () -> {

                                        })
                                        .show(parentFragment.requireActivity().getSupportFragmentManager(), "tag");
                            } else {
                                view.setText(UtilityDate.formatDate(resultDate, UtilityDate.COMMONS_DATE_FORMATS.DMY_HUMAN));
                                observableDate.set(resultDate);
                            }
                        }, mYear, mMonth, mDay);
                datePickerDialog.show();
            };

            //Adding click-listener
            view.setOnClickListener(onClick::run);
            if(view.getParent() != null && view.getParent().getParent() != null && view.getParent().getParent() instanceof TextInputLayout) {
                ((FrameLayout)view.getParent()).setOnClickListener(onClick::run);
                ((TextInputLayout) view.getParent().getParent()).setOnClickListener(onClick::run);
            }
        }
        Date newValue = observableDate.get();
        view.setText(UtilityDate.formatDate(newValue, UtilityDate.COMMONS_DATE_FORMATS.DMY_HUMAN));
    }

    @BindingAdapter("binding")
    public static void bindAutoCompleteTextView(AutoCompleteTextView view, final BindableString bindableString) {
        Pair<BindableString, TextWatcherAdapter> pair = (Pair) view.getTag(R.id.bound_observable);
        if (pair == null || pair.first != bindableString) {
            if (pair != null) {
                view.removeTextChangedListener(pair.second);
            }
            TextWatcherAdapter watcher = new TextWatcherAdapter() {
                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    bindableString.set(s.toString());
                }
            };
            view.setTag(R.id.bound_observable, new Pair<>(bindableString, watcher));
            view.addTextChangedListener(watcher);
        }
        String newValue = bindableString.get();
        if (!view.getText().toString().equals(newValue)) {
            view.setText(newValue);
        }
    }

    @BindingAdapter("binding")
    public static void bindAutoCompleteTextView(AppCompatMultiAutoCompleteTextView view, final BindableString bindableString) {
        Pair<BindableString, TextWatcherAdapter> pair = (Pair) view.getTag(R.id.bound_observable);
        if (pair == null || pair.first != bindableString) {
            if (pair != null) {
                view.removeTextChangedListener(pair.second);
            }
            TextWatcherAdapter watcher = new TextWatcherAdapter() {
                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    bindableString.set(s.toString());
                }
            };
            view.setTag(R.id.bound_observable, new Pair<>(bindableString, watcher));
            view.addTextChangedListener(watcher);
        }
        String newValue = bindableString.get();
        if (!view.getText().toString().equals(newValue)) {
            view.setText(newValue);
        }
    }

    @BindingAdapter("binding")
    public static void bindTextView(AppCompatTextView view, final BindableString bindableString) {
        Pair<BindableString, TextWatcherAdapter> pair = (Pair) view.getTag(R.id.bound_observable);
        if (pair == null || pair.first != bindableString) {
            if (pair != null) {
                view.removeTextChangedListener(pair.second);
            }
            TextWatcherAdapter watcher = new TextWatcherAdapter() {
                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    bindableString.set(s.toString());
                }
            };
            view.setTag(R.id.bound_observable, new Pair<>(bindableString, watcher));
            view.addTextChangedListener(watcher);
        }
        String newValue = bindableString.get();
        if (!view.getText().toString().equals(newValue)) {
            view.setText(newValue);
        }
    }

    @BindingAdapter("binding")
    public static void bindTextInputEditText(TextInputEditText view, final BindableFloat bindableFloat) {
        DecimalFormatSymbols otherSymbols = new DecimalFormatSymbols(Locale.getDefault());
        otherSymbols.setDecimalSeparator('.');
        otherSymbols.setGroupingSeparator(',');


        final DecimalFormat decimalFormat = new DecimalFormat();
        decimalFormat.setMaximumFractionDigits(3);
        decimalFormat.setDecimalFormatSymbols(otherSymbols);

        Pair<BindableFloat, TextWatcherAdapter> pair = (Pair) view.getTag(R.id.bound_observable);
        if (pair == null || pair.first != bindableFloat) {
            if (pair != null) {
                view.removeTextChangedListener(pair.second);
            }
            TextWatcherAdapter watcher = new TextWatcherAdapter() {
                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    try {
                        if (s.toString().trim().isEmpty()) {
                            bindableFloat.set(null);
                        } else bindableFloat.set(decimalFormat.parse(s.toString()).floatValue());
                    } catch (Exception ex) {
                        Log.e("FloatFormat", ex.getMessage());
                    }
                }
            };
            view.setTag(R.id.bound_observable, new Pair<>(bindableFloat, watcher));
            view.addTextChangedListener(watcher);
        }

        Float newValue = bindableFloat.get();
        if (!view.getText().toString().equals(decimalFormat.format(newValue))) {

            view.setText(decimalFormat.format(newValue));
            view.setSelection(view.getText().toString().length());
        }
    }

    @BindingAdapter("binding")
    public static void bindRadioGroup(RadioGroup view, final BindableBoolean bindableBoolean) {
        if (view.getTag(R.id.bound_observable) != bindableBoolean) {
            view.setTag(R.id.bound_observable, bindableBoolean);
            view.setOnCheckedChangeListener((group, checkedId) -> bindableBoolean.set(checkedId == group.getChildAt(1).getId()));
        }
        Boolean newValue = bindableBoolean.get();
        ((RadioButton) view.getChildAt(newValue ? 1 : 0)).setChecked(true);
    }

    @BindingAdapter("checked")
    public static void bindCheckbox(AppCompatCheckBox view, final BindableBoolean bindableBoolean) {
        if (view.getTag(R.id.bound_observable) != bindableBoolean) {
            view.setTag(R.id.bound_observable, bindableBoolean);
            view.setOnCheckedChangeListener((compoundButton, b) -> {
                bindableBoolean.set(b);
            });
        }
        Boolean newValue = bindableBoolean.get();
        view.setChecked(newValue);
    }

    @BindingAdapter("checked")
    public static void bindCheckbox(CheckBox view, final BindableBoolean bindableBoolean) {
        if (view.getTag(R.id.bound_observable) != bindableBoolean) {
            view.setTag(R.id.bound_observable, bindableBoolean);
            view.setOnCheckedChangeListener((compoundButton, b) -> bindableBoolean.set(b));
        }
        Boolean newValue = bindableBoolean.get();
        view.setChecked(newValue);
    }

    @BindingAdapter({"onClick"})
    public static void bindOnClick(View view, final Runnable runnable) {
        view.setOnClickListener(v -> runnable.run());
    }

    @BindingAdapter({"layout_constraintGuide_percent"})
    public static void bindFloatPercetageOnGuideline(View view, final float percentage) {
        ((Guideline) view).setGuidelinePercent(percentage);
    }

    @BindingAdapter({"backgroundTintResID"})
    public static void bindBackgroundTintResId(View view, @ColorRes final int colorResId) {
        view.setBackgroundTintList(ColorStateList.valueOf(ResourcesCompat.getColor(MainApplication.res, colorResId, null)));
    }


    @BindingAdapter({"visibility"})
    public static void bindViewVisibility(View view, final BindableBoolean bindableBoolean) {
        if (view.getTag(R.id.bound_observable) != bindableBoolean) {
            view.setTag(R.id.bound_observable, bindableBoolean);
        }
        bindableBoolean.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {
            @Override
            public void onPropertyChanged(Observable sender, int propertyId) {
                view.setVisibility(bindableBoolean.get() ? View.VISIBLE : View.GONE);
            }
        });

        view.setVisibility(bindableBoolean.get() ? View.VISIBLE : View.GONE);
    }


    @BindingAdapter("visibility")
    public static void bindView(View view, final ObservableField<Boolean> bindableBoolean) {
        if (view.getTag(R.id.bound_observable) != bindableBoolean) {
            view.setTag(R.id.bound_observable, bindableBoolean);
        }
        bindableBoolean.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {
            @Override
            public void onPropertyChanged(Observable sender, int propertyId) {
                view.setVisibility(bindableBoolean.get() ? View.VISIBLE : View.GONE);
            }
        });

        view.setVisibility(bindableBoolean.get() ? View.VISIBLE : View.GONE);
    }


    @BindingAdapter("android:layout_weight")
    public static void setLayoutWeight(View view, final Float weight) {

        LinearLayoutCompat.LayoutParams layoutParams = (LinearLayoutCompat.LayoutParams) view.getLayoutParams();
        layoutParams.weight = weight;
        view.setLayoutParams(layoutParams);
    }

}