/*
 *    Copyright (C) 2015 Haruki Hasegawa
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.h6ah4i.android.widget.advrecyclerview.expandable;

import android.view.ViewGroup;

import com.h6ah4i.android.widget.advrecyclerview.adapter.ItemIdComposer;
import com.h6ah4i.android.widget.advrecyclerview.adapter.ItemViewTypeComposer;

import java.util.List;

import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

@SuppressWarnings("ALL")
public interface ExpandableItemAdapter<GVH extends RecyclerView.ViewHolder, CVH extends RecyclerView.ViewHolder> {

    /**
     * Gets the number of groups.
     *
     * @return the number of groups
     */
    int getGroupCount();

    /**
     * Gets the number of children in a specified group.
     *
     * @param groupPosition the position of the group for which the children count should be returned
     * @return the number of children
     */
    int getChildCount(int groupPosition);

    /**
     * <p>Gets the ID for the group at the given position. This group ID must be unique across groups.</p>
     * <p>The combined ID (see {@link RecyclerViewExpandableItemManager#getCombinedGroupId(long)})
     * must be unique across ALL items (groups and all children).</p>
     *
     * @param groupPosition the position of the group for which the ID is wanted
     * @return the ID associated with the group
     */
    @IntRange(from = ItemIdComposer.MIN_GROUP_ID, to = ItemIdComposer.MAX_GROUP_ID)
    long getGroupId(int groupPosition);

    /**
     * <p>Gets the ID for the given child within the given group.</p>
     * <p>This ID must be unique across all children within the group.
     * The combined ID (see {@link RecyclerViewExpandableItemManager#getCombinedChildId(long, long)})
     * must be unique across ALL items (groups and all children).</p>
     *
     * @param groupPosition the position of the group that contains the child
     * @param childPosition the position of the child within the group for which the ID is wanted
     * @return the ID associated with the child
     */
    @IntRange(from = ItemIdComposer.MIN_CHILD_ID, to = ItemIdComposer.MAX_CHILD_ID)
    long getChildId(int groupPosition, int childPosition);

    /**
     * Gets the view type of the specified group.
     *
     * @param groupPosition the position of the group for which the view type is wanted
     * @return integer value identifying the type of the view needed to represent the group item at position. Type codes need positive number but not be contiguous.
     */
    @IntRange(from = ItemViewTypeComposer.MIN_WRAPPED_VIEW_TYPE, to = ItemViewTypeComposer.MAX_WRAPPED_VIEW_TYPE)
    int getGroupItemViewType(int groupPosition);

    /**
     * Gets the view type of the specified child.
     *
     * @param groupPosition the position of the group that contains the child
     * @param childPosition the position of the child within the group for which the view type is wanted
     * @return integer value identifying the type of the view needed to represent the group item at position. Type codes need positive number but not be contiguous.
     */
    @IntRange(from = ItemViewTypeComposer.MIN_WRAPPED_VIEW_TYPE, to = ItemViewTypeComposer.MAX_WRAPPED_VIEW_TYPE)
    int getChildItemViewType(int groupPosition, int childPosition);

    /**
     * Called when RecyclerView needs a new {@link GVH} of the given type to represent a group item.
     *
     * @param parent   The ViewGroup into which the new View will be added after it is bound to an adapter position
     * @param viewType The view type of the new View
     * @return A new group ViewHolder that holds a View of the given view type
     */
    @NonNull
    GVH onCreateGroupViewHolder(ViewGroup parent, @IntRange(from = ItemViewTypeComposer.MIN_WRAPPED_VIEW_TYPE, to = ItemViewTypeComposer.MAX_WRAPPED_VIEW_TYPE) int viewType);

    /**
     * Called when RecyclerView needs a new {@link CVH} of the given type to represent a child item.
     *
     * @param parent   The ViewGroup into which the new View will be added after it is bound to an adapter position
     * @param viewType The view type of the new View
     * @return A new child ViewHolder that holds a View of the given view type
     */
    @NonNull
    CVH onCreateChildViewHolder(ViewGroup parent, @IntRange(from = ItemViewTypeComposer.MIN_WRAPPED_VIEW_TYPE, to = ItemViewTypeComposer.MAX_WRAPPED_VIEW_TYPE) int viewType);

    /**
     * Called by RecyclerView to display the group data at the specified position.
     * This method should update the contents of the {@link androidx.recyclerview.widget.RecyclerView.ViewHolder#itemView}
     * to reflect the item at the given position.
     *
     * @param holder        The ViewHolder which should be updated to represent the contents of the item at the given position in the data set
     * @param groupPosition The position of the group item within the adapter's data set
     * @param viewType      The view type code
     */
    void onBindGroupViewHolder(@NonNull GVH holder, int groupPosition, @IntRange(from = ItemViewTypeComposer.MIN_WRAPPED_VIEW_TYPE, to = ItemViewTypeComposer.MAX_WRAPPED_VIEW_TYPE) int viewType);

    /**
     * Called by RecyclerView to display the group data at the specified position.
     * This method should update the contents of the {@link androidx.recyclerview.widget.RecyclerView.ViewHolder#itemView}
     * to reflect the item at the given position.
     *
     * @param holder        The ViewHolder which should be updated to represent the contents of the item at the given position in the data set
     * @param groupPosition The position of the group item within the adapter's data set
     * @param viewType      The view type code
     * @param payloads      A non-null list of merged payloads. Can be empty list if requires full update.
     */
    void onBindGroupViewHolder(@NonNull GVH holder, int groupPosition, @IntRange(from = ItemViewTypeComposer.MIN_WRAPPED_VIEW_TYPE, to = ItemViewTypeComposer.MAX_WRAPPED_VIEW_TYPE) int viewType, List<Object> payloads);

    /**
     * Called by RecyclerView to display the child data at the specified position.
     * This method should update the contents of the {@link androidx.recyclerview.widget.RecyclerView.ViewHolder#itemView}
     * to reflect the item at the given position.
     *
     * @param holder        The ViewHolder which should be updated to represent the contents of the item at the given position in the data set
     * @param groupPosition The position of the group item within the adapter's data set
     * @param childPosition The position of the child item within the group
     * @param viewType      The view type code
     * @param payloads      A non-null list of merged payloads. Can be empty list if requires full update.
     */
    void onBindChildViewHolder(@NonNull CVH holder, int groupPosition, int childPosition, @IntRange(from = ItemViewTypeComposer.MIN_WRAPPED_VIEW_TYPE, to = ItemViewTypeComposer.MAX_WRAPPED_VIEW_TYPE) int viewType, List<Object> payloads);

    /**
     * Called by RecyclerView to display the child data at the specified position.
     * This method should update the contents of the {@link androidx.recyclerview.widget.RecyclerView.ViewHolder#itemView}
     * to reflect the item at the given position.
     *
     * @param holder        The ViewHolder which should be updated to represent the contents of the item at the given position in the data set
     * @param groupPosition The position of the group item within the adapter's data set
     * @param childPosition The position of the child item within the group
     * @param viewType      The view type code
     */
    void onBindChildViewHolder(@NonNull CVH holder, int groupPosition, int childPosition, @IntRange(from = ItemViewTypeComposer.MIN_WRAPPED_VIEW_TYPE, to = ItemViewTypeComposer.MAX_WRAPPED_VIEW_TYPE) int viewType);

    /**
     * <p>Called when a user attempt to expand/collapse a group item by tapping.</p>
     * <p>Tips: If you want to set your own click event listener to group items, make this method always return false.
     * It will disable auto expanding/collapsing when a group item is clicked.</p>
     *
     * @param holder        The ViewHolder which is associated to group item user is attempt to expand/collapse
     * @param groupPosition Group position
     * @param x             Touched X position. Relative from the itemView's top-left
     * @param y             Touched Y position. Relative from the itemView's top-left
     * @param expand        true: expand, false: collapse
     * @return Whether to perform expand/collapse operation.
     */
    boolean onCheckCanExpandOrCollapseGroup(@NonNull GVH holder, int groupPosition, int x, int y, boolean expand);

    /**
     * Called when a group attempt to expand by user operation or by
     * {@link com.h6ah4i.android.widget.advrecyclerview.expandable.RecyclerViewExpandableItemManager#expandGroup(int)} method.
     *
     * @param groupPosition The position of the group item within the adapter's data set
     * @param fromUser      Whether the expand request is issued by a user operation
     * @return Whether the group can be expanded. If returns false, the group keeps collapsed.
     */
    boolean onHookGroupExpand(int groupPosition, boolean fromUser);

    /**
     * Called when a group attempt to expand by user operation or by
     * {@link com.h6ah4i.android.widget.advrecyclerview.expandable.RecyclerViewExpandableItemManager#expandGroup(int)} method.
     *
     * @param groupPosition The position of the group item within the adapter's data set
     * @param fromUser      Whether the expand request is issued by a user operation
     * @param payload       Optional parameter, use null to identify a "full" update the group item
     * @return Whether the group can be expanded. If returns false, the group keeps collapsed.
     */
    boolean onHookGroupExpand(int groupPosition, boolean fromUser, Object payload);

    /**
     * Called when a group attempt to expand by user operation or by
     * {@link com.h6ah4i.android.widget.advrecyclerview.expandable.RecyclerViewExpandableItemManager#collapseGroup(int)} method.
     *
     * @param groupPosition The position of the group item within the adapter's data set
     * @param fromUser      Whether the collapse request is issued by a user operation
     * @return Whether the group can be collapsed. If returns false, the group keeps expanded.
     */
    boolean onHookGroupCollapse(int groupPosition, boolean fromUser);

    /**
     * Called when a group attempt to expand by user operation or by
     * {@link com.h6ah4i.android.widget.advrecyclerview.expandable.RecyclerViewExpandableItemManager#collapseGroup(int)} method.
     *
     * @param groupPosition The position of the group item within the adapter's data set
     * @param fromUser      Whether the collapse request is issued by a user operation
     * @param payload       Optional parameter, use null to identify a "full" update the group item
     * @return Whether the group can be collapsed. If returns false, the group keeps expanded.
     */
    boolean onHookGroupCollapse(int groupPosition, boolean fromUser, Object payload);

    /**
     * Gets initial expanded state of the group item. This method is called when initially creating
     * a wrapper adapter and also when the data set is changed.
     *
     * @param groupPosition The position of the group
     * @return True for expanded, otherwise false.
     */
    boolean getInitialGroupExpandedState(int groupPosition);
}
