package com.zoonref.viewbehavior;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.coordinatorlayout.widget.CoordinatorLayout;

/**
 * Created by zoonooz on 5/1/2018 AD.
 * Base behavior for percentage driven behaviors.
 */
public class PercentageViewBehavior<V extends View> extends CoordinatorLayout.Behavior<V> {

    static final int UNSPECIFIED_INT = Integer.MAX_VALUE;
    static final float UNSPECIFIED_FLOAT = Float.MAX_VALUE;

    /**
     * Depend on the dependency view height
     */
    public static final int DEPEND_TYPE_HEIGHT = 0;

    /**
     * Depend on the dependency view width
     */
    public static final int DEPEND_TYPE_WIDTH = 1;

    /**
     * Depend on the dependency view x position
     */
    public static final int DEPEND_TYPE_X = 2;

    /**
     * Depend on the dependency view y position
     */
    public static final int DEPEND_TYPE_Y = 3;

    private int mDependType;
    private int mDependViewId;

    private int mDependTarget;

    private int mDependStartX;
    private int mDependStartY;
    private int mDependStartWidth;
    private int mDependStartHeight;

    /**
     * Is the values prepared to be use
     */
    private boolean isPrepared;

    /**
     * Creates a new behavior whose parameters come from the specified context and
     * attributes set.
     *
     * @param context the application environment
     * @param attrs   the set of attributes holding the target and animation parameters
     */
    PercentageViewBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);

        // setting values
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewBehavior);
        mDependViewId = a.getResourceId(R.styleable.ViewBehavior_behavior_dependsOn, 0);
        mDependType = a.getInt(R.styleable.ViewBehavior_behavior_dependType, DEPEND_TYPE_WIDTH);
        mDependTarget = a.getDimensionPixelOffset(R.styleable.ViewBehavior_behavior_dependTarget, UNSPECIFIED_INT);
        a.recycle();
    }

    PercentageViewBehavior(@NonNull Builder builder) {
        mDependViewId = builder.dependsOn;
        mDependType = builder.dependsType;
        mDependTarget = builder.targetValue;
    }

    /**
     * Call this before making any changes to the view to setup values
     *
     * @param parent     coordinator layout as parent
     * @param child      view that will move when dependency changed
     * @param dependency dependency view
     */
    void prepare(CoordinatorLayout parent, V child, View dependency) {
        mDependStartX = (int) dependency.getX();
        mDependStartY = (int) dependency.getY();
        mDependStartWidth = dependency.getWidth();
        mDependStartHeight = dependency.getHeight();

        isPrepared = true;
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency) {
        // depend on the view that has the same id
        return dependency.getId() == mDependViewId;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) {
        // first time, prepare values before continue
        if (!isPrepared) {
            prepare(parent, child, dependency);
        }
        updateView(child, dependency);
        return false;
    }

    @Override
    public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
        boolean bool = super.onLayoutChild(parent, child, layoutDirection);
        if (isPrepared) {
            updateView(child, parent.getDependencies(child).get(0));
        }
        return bool;
    }

    /**
     * Update the child view from the dependency states
     *
     * @param child      child view
     * @param dependency dependency view
     */
    void updateView(V child, View dependency) {
        float percent = 0;
        float start = 0;
        float current = 0;
        float end = UNSPECIFIED_INT;
        switch (mDependType) {
            case DEPEND_TYPE_WIDTH:
                start = mDependStartWidth;
                current = dependency.getWidth();
                end = mDependTarget;
                break;
            case DEPEND_TYPE_HEIGHT:
                start = mDependStartHeight;
                current = dependency.getHeight();
                end = mDependTarget;
                break;
            case DEPEND_TYPE_X:
                start = mDependStartX;
                current = dependency.getX();
                end = mDependTarget;
                break;
            case DEPEND_TYPE_Y:
                start = mDependStartY;
                current = dependency.getY();
                end = mDependTarget;
                break;
        }

        // need to define target value according to the depend type, if not then skip
        if (end != UNSPECIFIED_INT) {
            percent = Math.abs(current - start) / Math.abs(end - start);
        }
        updateViewWithPercent(child, percent > 1 ? 1 : percent);
    }

    /**
     * Update the child view from the progress of changed dependency view
     *
     * @param child   child view
     * @param percent progress of dependency changed 0.0f to 1.0f
     */
    void updateViewWithPercent(V child, float percent) {
        if (child instanceof PercentageChildView) {
            ((PercentageChildView) child).onPercentageBehaviorChange(this, percent);
        }
    }

    /**
     * Builder class
     */
    static abstract class Builder<T extends Builder> {

        private int dependsOn;
        private int dependsType = UNSPECIFIED_INT;
        private int targetValue = UNSPECIFIED_INT;

        abstract T getThis();

        T dependsOn(@IdRes int dependsOn, int dependsType) {
            this.dependsOn = dependsOn;
            this.dependsType = dependsType;
            return getThis();
        }

        T targetValue(int targetValue) {
            this.targetValue = targetValue;
            return getThis();
        }
    }
}
