/*
 * Copyright (C) 2017 Peng fei Pan <[email protected]>
 *
 * 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 me.panpf.adapter.pager;

import android.util.SparseIntArray;

import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.PagerAdapter;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;

/**
 * Combined {@link FragmentPagerAdapter}, supports combining many types of items, supports head and tail
 */
// todo support title
public class AssemblyFragmentPagerAdapter extends FragmentPagerAdapter implements AssemblyFragmentAdapter {

    @NonNull
    private FragmentItemManager itemManager;

    private int notifyNumber = 0;
    @Nullable
    private SparseIntArray notifyNumberPool;

    public AssemblyFragmentPagerAdapter(@NonNull FragmentManager fm, @Behavior int behavior) {
        super(fm, behavior);
        this.itemManager = new FragmentItemManager(this);
    }

    /**
     * @deprecated use {@link #AssemblyFragmentPagerAdapter(FragmentManager, int)} with
     * {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT}
     */
    @Deprecated
    public AssemblyFragmentPagerAdapter(@NonNull FragmentManager fm) {
        super(fm);
        this.itemManager = new FragmentItemManager(this);
    }

    public AssemblyFragmentPagerAdapter(@NonNull FragmentManager fm, @Behavior int behavior, @NonNull List dataList) {
        super(fm, behavior);
        this.itemManager = new FragmentItemManager(this, dataList);
    }

    /**
     * @deprecated use {@link #AssemblyFragmentPagerAdapter(FragmentManager, int, List)} with
     * {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT}
     */
    @Deprecated
    public AssemblyFragmentPagerAdapter(@NonNull FragmentManager fm, @NonNull List dataList) {
        super(fm);
        this.itemManager = new FragmentItemManager(this, dataList);
    }

    public AssemblyFragmentPagerAdapter(@NonNull FragmentManager fm, @Behavior int behavior, @Nullable Object[] dataArray) {
        super(fm, behavior);
        this.itemManager = new FragmentItemManager(this, dataArray);
    }

    /**
     * @deprecated use {@link #AssemblyFragmentPagerAdapter(FragmentManager, int, Object[])} with
     * {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT}
     */
    @Deprecated
    public AssemblyFragmentPagerAdapter(@NonNull FragmentManager fm, @Nullable Object[] dataArray) {
        super(fm);
        this.itemManager = new FragmentItemManager(this, dataArray);
    }


    @Override
    public void addItemFactory(@NonNull AssemblyFragmentItemFactory itemFactory) {
        itemManager.addItemFactory(itemFactory);
    }

    @NonNull
    @Override
    public List<AssemblyFragmentItemFactory> getItemFactoryList() {
        return itemManager.getItemFactoryList();
    }

    @NonNull
    @Override
    public <DATA> FragmentFixedItem<DATA> addHeaderItem(@NonNull FragmentFixedItem<DATA> fixedItem) {
        return itemManager.addHeaderItem(fixedItem);
    }

    @NonNull
    @Override
    public <DATA> FragmentFixedItem<DATA> addHeaderItem(@NonNull AssemblyFragmentItemFactory<DATA> headerFactory, @Nullable DATA data) {
        return itemManager.addHeaderItem(headerFactory, data);
    }

    @NonNull
    @Override
    public <DATA> FragmentFixedItem<DATA> addHeaderItem(@NonNull AssemblyFragmentItemFactory<DATA> headerFactory) {
        return itemManager.addHeaderItem(headerFactory);
    }

    @NonNull
    @Override
    public <DATA> FragmentFixedItem<DATA> getHeaderItemByFactoryClass(@NonNull Class<? extends AssemblyFragmentItemFactory<DATA>> clazz, int number) {
        return itemManager.getHeaderItemManager().getItemByFactoryClass(clazz, number);
    }

    @NonNull
    @Override
    public <DATA> FragmentFixedItem<DATA> getHeaderItemByFactoryClass(@NonNull Class<? extends AssemblyFragmentItemFactory<DATA>> clazz) {
        return itemManager.getHeaderItemManager().getItemByFactoryClass(clazz);
    }

    @NonNull
    @Override
    public FragmentFixedItem getHeaderItem(int positionInHeaderItemList) {
        return itemManager.getHeaderItemManager().getItem(positionInHeaderItemList);
    }

    @Nullable
    @Override
    public Object getHeaderItemData(int positionInHeaderItemList) {
        return itemManager.getHeaderItemManager().getItem(positionInHeaderItemList).getData();
    }

    @Override
    public void setHeaderItemData(int positionInHeaderItemList, @Nullable Object data) {
        itemManager.getHeaderItemManager().setItemData(positionInHeaderItemList, data);
    }

    @Override
    public int getHeaderCount() {
        return itemManager.getHeaderItemManager().getItemCount();
    }

    @Nullable
    @Override
    public Object getHeaderData(int positionInHeaderList) {
        return itemManager.getHeaderItemManager().getItem(positionInHeaderList).getData();
    }

    @NonNull
    @Override
    public <DATA> FragmentFixedItem<DATA> addFooterItem(@NonNull FragmentFixedItem<DATA> fixedItem) {
        return itemManager.addHeaderItem(fixedItem);
    }

    @NonNull
    @Override
    public <DATA> FragmentFixedItem<DATA> addFooterItem(@NonNull AssemblyFragmentItemFactory<DATA> footerFactory, @Nullable DATA data) {
        return itemManager.addFooterItem(footerFactory, data);
    }

    @NonNull
    @Override
    public <DATA> FragmentFixedItem<DATA> addFooterItem(@NonNull AssemblyFragmentItemFactory<DATA> footerFactory) {
        return itemManager.addFooterItem(footerFactory);
    }

    @NonNull
    @Override
    public <DATA> FragmentFixedItem<DATA> getFooterItemByFactoryClass(@NonNull Class<? extends AssemblyFragmentItemFactory<DATA>> clazz, int number) {
        return itemManager.getFooterItemManager().getItemByFactoryClass(clazz, number);
    }

    @NonNull
    @Override
    public <DATA> FragmentFixedItem<DATA> getFooterItemByFactoryClass(@NonNull Class<? extends AssemblyFragmentItemFactory<DATA>> clazz) {
        return itemManager.getFooterItemManager().getItemByFactoryClass(clazz);
    }

    @NonNull
    @Override
    public FragmentFixedItem getFooterItem(int positionInFooterItemList) {
        return itemManager.getFooterItemManager().getItem(positionInFooterItemList);
    }

    @Nullable
    @Override
    public Object getFooterItemData(int positionInFooterItemList) {
        return itemManager.getFooterItemManager().getItem(positionInFooterItemList).getData();
    }

    @Override
    public void setFooterItemData(int positionInFooterItemList, @Nullable Object data) {
        itemManager.getFooterItemManager().setItemData(positionInFooterItemList, data);
    }

    @Override
    public int getFooterCount() {
        return itemManager.getFooterItemManager().getItemCount();
    }

    @Nullable
    @Override
    public Object getFooterData(int positionInFooterList) {
        return itemManager.getFooterItemManager().getItem(positionInFooterList).getData();
    }


    @Nullable
    @Override
    public List getDataList() {
        return itemManager.getDataList();
    }

    @Override
    public void setDataList(@Nullable List dataList) {
        itemManager.setDataList(dataList);
    }

    @Override
    public int getDataCount() {
        return itemManager.getDataCount();
    }

    @Nullable
    @Override
    public Object getData(int positionInDataList) {
        return itemManager.getData(positionInDataList);
    }


    @Override
    public boolean isHeaderItem(int position) {
        return itemManager.isHeaderItem(position);
    }

    @Override
    public boolean isBodyItem(int position) {
        return itemManager.isBodyItem(position);
    }

    @Override
    public boolean isFooterItem(int position) {
        return itemManager.isFooterItem(position);
    }

    @Override
    public int getPositionInPart(int position) {
        return itemManager.getPositionInPart(position);
    }

    @NonNull
    @Override
    public AssemblyFragmentItemFactory getItemFactoryByPosition(int position) {
        return itemManager.getItemFactoryByPosition(position);
    }


    @Override
    public int getCount() {
        return itemManager.getItemCount();
    }

    @NonNull
    @Override
    public Fragment getItem(int position) {
        AssemblyFragmentItemFactory itemFactory = itemManager.getItemFactoryByPosition(position);
        Object itemData = itemManager.getItemDataByPosition(position);
        //noinspection unchecked
        return itemFactory.dispatchCreateFragment(position, itemData);
    }

    public boolean isEnabledPositionNoneOnNotifyDataSetChanged() {
        return notifyNumberPool != null;
    }

    public void setEnabledPositionNoneOnNotifyDataSetChanged(boolean enabled) {
        if (enabled) {
            notifyNumberPool = new SparseIntArray();
            notifyNumber = 0;
        } else {
            notifyNumberPool = null;
        }
    }

    @Override
    public void notifyDataSetChanged() {
        if (notifyNumberPool != null) notifyNumber++;
        super.notifyDataSetChanged();
    }

    @Override
    public int getItemPosition(@NonNull Object object) {
        if (notifyNumberPool != null && notifyNumberPool.get(object.hashCode()) != notifyNumber) {
            notifyNumberPool.put(object.hashCode(), notifyNumber);
            return PagerAdapter.POSITION_NONE;
        }
        return super.getItemPosition(object);
    }

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({BEHAVIOR_SET_USER_VISIBLE_HINT, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT})
    private @interface Behavior {
    }
}