package org.solovyev.android.calculator.keyboard;

import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING;
import static android.view.HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING;
import static android.view.HapticFeedbackConstants.KEYBOARD_TAP;
import static org.solovyev.android.calculator.App.cast;
import static org.solovyev.android.calculator.Preferences.Gui.Mode.simple;

import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import org.solovyev.android.Check;
import org.solovyev.android.calculator.ActivityLauncher;
import org.solovyev.android.calculator.App;
import org.solovyev.android.calculator.Calculator;
import org.solovyev.android.calculator.Editor;
import org.solovyev.android.calculator.Keyboard;
import org.solovyev.android.calculator.Preferences;
import org.solovyev.android.calculator.buttons.CppButton;
import org.solovyev.android.calculator.memory.Memory;
import org.solovyev.android.views.Adjuster;
import org.solovyev.android.views.dragbutton.DirectionDragButton;
import org.solovyev.android.views.dragbutton.DirectionDragImageButton;
import org.solovyev.android.views.dragbutton.DirectionDragListener;
import org.solovyev.android.views.dragbutton.DirectionDragView;
import org.solovyev.android.views.dragbutton.Drag;
import org.solovyev.android.views.dragbutton.DragDirection;
import org.solovyev.android.views.dragbutton.DragEvent;
import org.solovyev.android.views.dragbutton.DragView;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.Nonnull;
import javax.inject.Inject;

import dagger.Lazy;

public abstract class BaseKeyboardUi implements SharedPreferences.OnSharedPreferenceChangeListener, View.OnClickListener {

    public static float getTextScale(@NonNull Context context) {
        return App.isTablet(context) ? 0.4f : 0.5f;
    }

    public static final float IMAGE_SCALE = 0.5f;
    public static final float IMAGE_SCALE_ERASE = 0.4f;

    @NonNull
    private final List<DragView> dragButtons = new ArrayList<>();
    @NonNull
    protected final DirectionDragListener listener;
    @Inject
    SharedPreferences preferences;
    @Inject
    Keyboard keyboard;
    @Inject
    Editor editor;
    @Inject
    Calculator calculator;
    @Inject
    ActivityLauncher launcher;
    @Inject
    Lazy<Memory> memory;
    protected int orientation = ORIENTATION_PORTRAIT;
    private int textSize;
    private Preferences.Gui.Mode mode;
    private final float textScale;

    public BaseKeyboardUi(@NonNull Application application) {
        listener = new DirectionDragListener(application) {
            @Override
            protected boolean onDrag(@NonNull View view, @NonNull DragEvent event, @NonNull DragDirection direction) {
                if (!Drag.hasDirectionText(view, direction)) {
                    return false;
                }
                final DirectionDragView dragView = (DirectionDragView) view;
                final String text = dragView.getText(direction).getValue();
                if (TextUtils.isEmpty(text)) {
                    // hasDirectionText should return false for empty text
                    Check.shouldNotHappen();
                    return false;
                }
                keyboard.buttonPressed(text);
                return true;
            }
        };
        textScale = getTextScale(application);
    }

    public static void adjustButton(@NonNull View button) {
        if (button instanceof TextView) {
            Adjuster.adjustText((TextView) button, getTextScale(button.getContext()));
        } else if (button instanceof DirectionDragImageButton) {
            Adjuster.adjustText(((DirectionDragImageButton) button), AdjusterHelper.instance, getTextScale(button.getContext()), 0);
            Adjuster.adjustImage((ImageView) button, IMAGE_SCALE);
        } else if (button instanceof ImageView) {
            Adjuster.adjustImage((ImageView) button, IMAGE_SCALE);
        }
    }

    @Override
    public void onClick(View v) {
        final CppButton button = CppButton.getById(v.getId());
        if (button == null) {
            Check.shouldNotHappen();
            return;
        }
        onClick(v, button.action);
    }

    public void onCreateView(@Nonnull Activity activity, @Nonnull View view) {
        cast(activity.getApplication()).getComponent().inject(this);
        preferences.registerOnSharedPreferenceChangeListener(this);

        orientation = App.getScreenOrientation(activity);
        mode = Preferences.Gui.mode.getPreferenceNoError(preferences);
        textSize = calculateTextSize(activity);
    }

    protected final void prepareButton(@Nullable View button) {
        if (button == null) {
            return;
        }
        // we call android.view.View.performHapticFeedback(int, int) from #onClick
        button.setHapticFeedbackEnabled(false);
        button.setOnClickListener(this);
    }

    protected final void prepareButton(@Nullable DirectionDragButton button) {
        if (button == null) {
            return;
        }
        dragButtons.add(button);
        button.setVibrateOnDrag(keyboard.isVibrateOnKeypress());
        button.setHighContrast(keyboard.isHighContrast());
        prepareButton((View) button);
        button.setOnDragListener(listener);
        button.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
        Adjuster.adjustText(button, textScale);
    }

    protected final void hideText(@Nullable DirectionDragView button, @Nonnull DragDirection... directions) {
        if (button == null) {
            return;
        }
        for (DragDirection direction : directions) {
            hideText(button, direction);
        }
    }

    protected final void hideText(@Nullable DirectionDragView button, @Nonnull DragDirection direction) {
        if (button == null) {
            return;
        }
        button.getText(direction).setVisible(false);
    }

    public void onDestroyView() {
        dragButtons.clear();
        preferences.unregisterOnSharedPreferenceChangeListener(this);
    }

    public static int calculateTextSize(@Nonnull Activity activity) {
        final boolean portrait = App.getScreenOrientation(activity) == ORIENTATION_PORTRAIT;
        final DisplayMetrics metrics = new DisplayMetrics();
        activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
        final int buttonsCount = portrait ? 5 : 4;
        final int buttonsWeight = portrait ? (2 + 1 + buttonsCount) : (2 + buttonsCount);
        final int buttonSize = metrics.heightPixels / buttonsWeight;
        return 5 * buttonSize / 12;
    }

    @Override
    public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
        if (Preferences.Gui.vibrateOnKeypress.isSameKey(key)) {
            final boolean vibrate = Preferences.Gui.vibrateOnKeypress.getPreference(preferences);
            for (DragView dragButton : dragButtons) {
                dragButton.setVibrateOnDrag(vibrate);
            }
        } else if (Preferences.Gui.highContrast.isSameKey(key)) {
            final boolean highContrast = Preferences.Gui.highContrast.getPreference(preferences);
            for (DragView dragButton : dragButtons) {
                dragButton.setHighContrast(highContrast);
            }
        }
    }

    protected boolean isSimpleMode() {
        return mode == simple;
    }

    protected final void onClick(@Nonnull View v, @Nonnull String s) {
        if (!keyboard.buttonPressed(s)) {
            return;
        }
        if (!keyboard.isVibrateOnKeypress()) {
            return;
        }
        v.performHapticFeedback(KEYBOARD_TAP, FLAG_IGNORE_GLOBAL_SETTING | FLAG_IGNORE_VIEW_SETTING);
    }

    private static class AdjusterHelper implements Adjuster.Helper<DirectionDragImageButton> {

        public static final AdjusterHelper instance = new AdjusterHelper();

        @Override
        public void apply(@NonNull DirectionDragImageButton view, float textSize) {
            view.setTextSize(textSize);
        }

        @Override
        public float getTextSize(@NonNull DirectionDragImageButton view) {
            return view.getTextSize();
        }
    }
}
