package io.scanbot.sdk.reactnative.components.common;

import android.content.Context;
import android.util.AttributeSet;
import android.view.Choreographer;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;

import com.facebook.react.ReactRootView;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

public class ScanbotComponentRootView extends ReactRootView {

    public interface ILayoutChangeListener {
        void onFrameLayoutChanged(int width, int height, int left, int top, int right, int bottom);
    }

    public interface IOnChildRemovedListener {
        void onChildRemoved(final View child);
    }

    public final Runnable measureAndLayout =
            new Runnable() {
                @Override
                public void run() {
                    measure(
                            MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
                            MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));
                    layout(getLeft(), getTop(), getRight(), getBottom());
                    notifyLayoutChangeListeners(getWidth(), getHeight(), getLeft(), getTop(), getRight(), getBottom());
                }
            };

    private final List<ILayoutChangeListener> layoutChangeListeners = new ArrayList<>();
    private final List<IOnChildRemovedListener> childRemovedListeners = new ArrayList<>();
    private final List<View> lastNotifiedChildViews = new ArrayList<>();
    private final List<Type> reservedComponents = new ArrayList<>();
    private boolean shouldNotifyWhenChildIsRemoved = false;

    private void instantiate() {
        reservedComponents.add(ScanbotComponentRootView.class);
    }

    public ScanbotComponentRootView(Context context) {
        super(context);
        instantiate();
    }

    public ScanbotComponentRootView(Context context, AttributeSet attrs) {
        super(context, attrs);
        instantiate();
    }

    public ScanbotComponentRootView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        instantiate();
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        applyChildrenMeasureWorkaround();
    }

    private void applyChildrenMeasureWorkaround() {
        Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
            @Override
            public void doFrame(long frameTimeNanos) {
                for (int i = 0; i < getChildCount(); i++) {
                    View child = getChildAt(i);
                    if (child instanceof FrameLayout) {
                        child.measure(View.MeasureSpec.makeMeasureSpec(getMeasuredWidth(), View.MeasureSpec.EXACTLY),
                                View.MeasureSpec.makeMeasureSpec(getMeasuredHeight(), View.MeasureSpec.EXACTLY));
                        child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
                    }
                }
                getViewTreeObserver().dispatchOnGlobalLayout();
                shouldNotifyWhenChildIsRemoved = true;
            }
        });
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public void requestLayout() {
        super.requestLayout();
        post(measureAndLayout);
    }

    public void addLayoutChangeListener(ILayoutChangeListener listener) {
        layoutChangeListeners.add(listener);
    }

    public void addOneShotChildRemovedListener(IOnChildRemovedListener listener) {
        if (!childRemovedListeners.contains(listener)) {
            childRemovedListeners.add(listener);
        }
    }

    private void notifyLayoutChangeListeners(int width, int height, int left, int top, int right, int bottom) {
        for(ILayoutChangeListener listener : layoutChangeListeners) {
            listener.onFrameLayoutChanged(width, height, left, top, right, bottom);
        }
    }

    private void notifyChildRemovedListeners(final View childView) {
        for (IOnChildRemovedListener listener : childRemovedListeners) {
            if (!lastNotifiedChildViews.contains(childView)) {
                listener.onChildRemoved(childView);
                lastNotifiedChildViews.add(childView);
            } else {
                lastNotifiedChildViews.clear();
            }
        }

        childRemovedListeners.clear();
    }

    @Override
    public void onViewAdded(View child) {
        super.onViewAdded(child);
        child.bringToFront();
        child.setZ(2.0f);
    }

    /**
     * Called when a child view is removed from this ViewGroup. Overrides should always
     * call super.onViewRemoved.
     *
     * @param child the removed child view
     */
    @Override
    public void onViewRemoved(View child) {
        super.onViewRemoved(child);
        if (shouldNotifyWhenChildIsRemoved) {
            //notifyChildRemovedListeners(child);
        }
    }

    @Override
    public void onAttachedToReactInstance() {
        super.onAttachedToReactInstance();
        shouldNotifyWhenChildIsRemoved = false;
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        shouldNotifyWhenChildIsRemoved = false;
    }
}