package com.hikapro.backpack.presenter.adapters.helper.items;

import android.graphics.Canvas;
import android.graphics.Rect;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.SparseArray;
import android.view.View;
import android.widget.LinearLayout;

/**
 * Created by tariel on 01/05/16.
 */
public class StickyHeaderDecoration extends RecyclerView.ItemDecoration {

    private final StickyHeaderAdapter adapter;
    private final ItemVisibilityAdapter visibilityAdapter;
    private final SparseArray<Rect> headerRects = new SparseArray<>();
    private final HeaderProvider headerProvider;
    private final HeaderPositionCalculator headerPositionCalculator;
    private final HeaderRenderer renderer;
    private final DimensionCalculator dimensionCalculator;

    private final Rect rect = new Rect();


    public StickyHeaderDecoration(StickyHeaderAdapter adapter) {
        this(adapter, new DimensionCalculator(), null);
    }

    private StickyHeaderDecoration(StickyHeaderAdapter adapter,
                                   DimensionCalculator dimensionCalculator, ItemVisibilityAdapter visibilityAdapter) {
        this(adapter, dimensionCalculator, new HeaderRenderer(), new HeaderViewCache(adapter), visibilityAdapter);
    }

    private StickyHeaderDecoration(StickyHeaderAdapter adapter, DimensionCalculator dimensionCalculator,
                                   HeaderRenderer headerRenderer, HeaderProvider headerProvider,
                                   ItemVisibilityAdapter visibilityAdapter) {
        this(adapter, headerRenderer, dimensionCalculator, headerProvider,
                new HeaderPositionCalculator(adapter, headerProvider,
                        dimensionCalculator), visibilityAdapter);
    }

    private StickyHeaderDecoration(StickyHeaderAdapter adapter, HeaderRenderer headerRenderer,
                                   DimensionCalculator dimensionCalculator, HeaderProvider headerProvider,
                                   HeaderPositionCalculator headerPositionCalculator, ItemVisibilityAdapter visibilityAdapter) {
        this.adapter = adapter;
        this.headerProvider = headerProvider;
        this.renderer = headerRenderer;
        this.dimensionCalculator = dimensionCalculator;
        this.headerPositionCalculator = headerPositionCalculator;
        this.visibilityAdapter = visibilityAdapter;
    }


    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);

        int itemPosition = parent.getChildAdapterPosition(view);
        if (itemPosition == RecyclerView.NO_POSITION) {
            return;
        }
        if (headerPositionCalculator.hasNewHeader(itemPosition)) {
            View header = getHeaderView(parent, itemPosition);
            setItemOffsetsForHeader(outRect, header, LinearLayout.VERTICAL);
        }
    }

    /**
     * Sets the offsets for the first item in a section to make room for the header view
     *
     * @param itemOffsets rectangle to define offsets for the item
     * @param header      view used to calculate offset for the item
     * @param orientation used to calculate offset for the item
     */
    private void setItemOffsetsForHeader(Rect itemOffsets, View header, int orientation) {
        dimensionCalculator.initMargins(rect, header);
        if (orientation == LinearLayoutManager.VERTICAL) {
            itemOffsets.top = header.getHeight() + rect.top + rect.bottom;
        } else {
            itemOffsets.left = header.getWidth() + rect.left +
            rect.right;
        }
    }

    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDrawOver(c, parent, state);

        final int childCount = parent.getChildCount();
        if (childCount <= 0 || adapter.getItemCount() <= 0) {
            return;
        }

        for (int i = 0; i < childCount; i++) {
            View itemView = parent.getChildAt(i);
            int position = parent.getChildAdapterPosition(itemView);
            if (position == RecyclerView.NO_POSITION) {
                continue;
            }

            boolean hasStickyHeader = headerPositionCalculator.hasStickyHeader(itemView, LinearLayout.VERTICAL, position);
            if (hasStickyHeader || headerPositionCalculator.hasNewHeader(position)) {
                View header = headerProvider.getHeader(parent, position);
                //re-use existing Rect, if any.
                Rect headerOffset = headerRects.get(position);
                if (headerOffset == null) {
                    headerOffset = new Rect();
                    headerRects.put(position, headerOffset);
                }
                headerPositionCalculator.initHeaderBounds(headerOffset, parent, header, itemView, hasStickyHeader);
                renderer.drawHeader(parent, c, header, headerOffset);
            }
        }
    }

    /**
     * Gets the position of the header under the specified (x, y) coordinates.
     *
     * @param x x-coordinate
     * @param y y-coordinate
     * @return position of header, or -1 if not found
     */
    public int findHeaderPositionUnder(int x, int y) {
        for (int i = 0; i < headerRects.size(); i++) {
            Rect rect = headerRects.get(headerRects.keyAt(i));
            if (rect.contains(x, y)) {
                int position = headerRects.keyAt(i);
                if (visibilityAdapter == null || visibilityAdapter.isPositionVisible(position)) {
                    return position;
                }
            }
        }
        return -1;
    }

    /**
     * Gets the header view for the associated position.  If it doesn't exist yet, it will be
     * created, measured, and laid out.
     *
     * @param parent the recyclerview
     * @param position the position to get the header view for
     * @return Header view
     */
    public View getHeaderView(RecyclerView parent, int position) {
        return headerProvider.getHeader(parent, position);
    }

    /**
     * Invalidates cached headers.  This does not invalidate the recyclerview, you should do that manually after
     * calling this method.
     */
    public void invalidateHeaders() {
        headerProvider.invalidate();
        headerRects.clear();
    }
}
