content: /*
   * Copyright (C) 2015 AlexMofer
   *
   * 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 io.github.alexmofer.android.support.widget;
  
  import android.content.res.Configuration;
  import android.view.View;
  import android.view.ViewGroup;
  
  import androidx.annotation.NonNull;
  import androidx.annotation.Nullable;
  import androidx.viewpager.widget.PagerAdapter;
  
  import java.util.ArrayList;
  import java.util.Collection;
  
  /**
   * 视图集PagerAdapter
   * Created by Alex on 2016/3/16.
   */
  @SuppressWarnings("all")
  public class ViewsPagerAdapter extends PagerAdapter {
  
      private final ArrayList<ViewHolder> mItems = new ArrayList<>();
      private final ArrayList<ViewHolder> mItemsInLayout = new ArrayList<>();
      private final ArrayList<ViewHolder> mItemsShouldCheck = new ArrayList<>();
      private ViewHolder mPrimaryItem;
  
      public ViewsPagerAdapter() {
      }
  
      public ViewsPagerAdapter(@NonNull Collection<? extends View> views,
                               @Nullable Collection<? extends CharSequence> titles) {
          addAll(views, titles);
      }
  
      public ViewsPagerAdapter(@NonNull Collection<? extends View> views) {
          this(views, null);
      }
  
      @Override
      public int getCount() {
          return mItems.size();
      }
  
      @Override
      public void startUpdate(@NonNull ViewGroup container) {
          // do nothing
      }
  
      @NonNull
      @Override
      public Object instantiateItem(@NonNull ViewGroup container, int position) {
          final ViewHolder holder = mItems.get(position);
          holder.mPosition = position;
          holder.mNeedRebind = false;
          holder.mPositionChanged = false;
          holder.mRemoved = false;
          final View view = holder.itemView;
          onBindView(view, position);
          container.addView(view);
          mItemsInLayout.add(holder);
          return holder;
      }
  
      @Override
      public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
          if ((!(object instanceof ViewHolder)))
              return;
          final ViewHolder holder = (ViewHolder) object;
          final View view = holder.itemView;
          container.removeView(view);
          mItemsInLayout.remove(holder);
          mItemsShouldCheck.add(holder);
          onUnbindView(view, position);
      }
  
      @Override
      public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
          if ((!(object instanceof ViewHolder)))
              return;
          if (mPrimaryItem == object)
              return;
          final View old = getPrimaryItem();
          mPrimaryItem = (ViewHolder) object;
          onPrimaryItemChanged(old, mPrimaryItem.itemView);
      }
  
      @Override
      public void finishUpdate(@NonNull ViewGroup container) {
          for (ViewHolder holder : mItemsShouldCheck) {
              if (holder.mRemoved)
                  holder.recycle();
          }
          mItemsShouldCheck.clear();
      }
  
      @Override
      public final void startUpdate(@NonNull View container) {
          startUpdate((ViewGroup) container);
      }
  
      @NonNull
      @Override
      public final Object instantiateItem(@NonNull View container, int position) {
          return instantiateItem((ViewGroup) container, position);
      }
  
      @Override
      public final void destroyItem(@NonNull View container, int position, @NonNull Object object) {
          destroyItem((ViewGroup) container, position, object);
      }
  
      @Override
      public final void setPrimaryItem(@NonNull View container, int position, @NonNull Object object) {
          setPrimaryItem((ViewGroup) container, position, object);
      }
  
      @Override
      public final void finishUpdate(@NonNull View container) {
          finishUpdate((ViewGroup) container);
      }
  
      @Override
      public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
          return object instanceof ViewHolder && view == ((ViewHolder) object).itemView;
      }
  
      @Override
      public int getItemPosition(@NonNull Object object) {
          if ((!(object instanceof ViewHolder)))
              return POSITION_NONE;
          final ViewHolder holder = (ViewHolder) object;
          if (holder.mRemoved)
              return POSITION_NONE;
          if (holder.mNeedRebind)
              return POSITION_NONE;
          return holder.mPositionChanged ? holder.mPosition : POSITION_UNCHANGED;
      }
  
      @Nullable
      @Override
      public CharSequence getPageTitle(int position) {
          return (position < 0 || position >= mItems.size()) ? null : mItems.get(position).mTitle;
      }
  
      /**
       * 绑定子View
       *
       * @param view     子View
       * @param position 位置
       */
      public void onBindView(@NonNull View view, int position) {
      }
  
      /**
       * 解绑子View
       *
       * @param view     子View
       * @param position 位置
       */
      public void onUnbindView(@NonNull View view, int position) {
      }
  
      /**
       * 主子项已发生改变
       *
       * @param oldItem 旧的子项
       * @param newItem 新的子项
       */
      protected void onPrimaryItemChanged(@Nullable View oldItem, @NonNull View newItem) {
      }
  
      /**
       * 获取主子项
       *
       * @return 主子项
       */
      @Nullable
      public View getPrimaryItem() {
          return mPrimaryItem == null ? null : mPrimaryItem.itemView;
      }
  
      /**
       * 通知子项改变
       *
       * @param position 位置
       */
      public final void notifyItemChanged(int position) {
          notifyItemRangeChanged(position, 1);
      }
  
      /**
       * 通知区间内子项改变
       *
       * @param positionStart 开始位置
       * @param itemCount     总数
       */
      public final void notifyItemRangeChanged(int positionStart, int itemCount) {
          if (itemCount <= 0)
              return;
          for (int i = 0; i < itemCount; i++) {
              final ViewHolder holder = mItems.get(positionStart + i);
              holder.mNeedRebind = true;
          }
          notifyDataSetChanged();
      }
  
      /**
       * 设置子视图与标题
       *
       * @param position 位置
       * @param view     子视图
       * @param title    标题
       */
      public void set(int position, @NonNull View view, @Nullable CharSequence title) {
          if (position < 0 || position >= mItems.size())
              return;
          final ViewHolder old = mItems.get(position);
          old.mRemoved = true;
          mItemsShouldCheck.add(old);
          final ViewHolder holder = ViewHolderFactory.getInstance().get();
          holder.set(view, title);
          holder.mPosition = position;
          mItems.set(position, holder);
          notifyDataSetChanged();
      }
  
      /**
       * 设置子视图
       *
       * @param position 位置
       * @param view     子视图
       */
      public void setView(int position, @NonNull View view) {
          set(position, view, getPageTitle(position));
      }
  
      /**
       * 设置标题
       * 修改标题不会触发刷新
       *
       * @param position 位置
       * @param title    标题
       */
      public void setTitle(int position, @Nullable CharSequence title) {
          if (position < 0 || position >= mItems.size())
              return;
          mItems.get(position).mTitle = title;
      }
  
      /**
       * 添加视图
       *
       * @param view  视图
       * @param title 标题
       */
      public void add(@NonNull View view, @Nullable CharSequence title) {
          final ViewHolder holder = ViewHolderFactory.getInstance().get();
          holder.set(view, title);
          holder.mPosition = mItems.size();
          mItems.add(holder);
          notifyDataSetChanged();
      }
  
      /**
       * 添加视图
       *
       * @param view 视图
       */
      public void addView(View view) {
          add(view, null);
      }
  
      /**
       * 在指定位置添加视图
       *
       * @param position 位置
       * @param view     视图
       * @param title    标题
       */
      public void add(int position, View view, CharSequence title) {
          final ViewHolder holder = ViewHolderFactory.getInstance().get();
          holder.set(view, title);
          holder.mPosition = position;
          mItems.add(position, holder);
          final int count = getCount();
          for (int i = position; i < count; i++) {
              final ViewHolder item = mItems.get(i);
              item.mPosition = i;
              if (mItemsInLayout.contains(item))
                  item.mPositionChanged = true;
          }
          notifyDataSetChanged();
      }
  
      /**
       * 添加视图
       *
       * @param position 位置
       * @param view     视图
       */
      public void addView(int position, View view) {
          add(position, view, null);
      }
  
      /**
       * 移除子项
       *
       * @param position 位置
       */
      public void remove(int position) {
          final ViewHolder removed = mItems.remove(position);
          removed.mRemoved = true;
          mItemsShouldCheck.add(removed);
          final int count = getCount();
          for (int i = position; i < count; i++) {
              final ViewHolder item = mItems.get(i);
              item.mPosition = i;
              if (mItemsInLayout.contains(item))
                  item.mPositionChanged = true;
          }
          notifyDataSetChanged();
      }
  
      /**
       * 移除子项
       *
       * @param view 子项
       * @return 是否成功
       */
      public boolean remove(@NonNull View view) {
          final int count = mItems.size();
          for (int i = 0; i < count; i++) {
              final ViewHolder holder = mItems.get(i);
              if (holder.itemView == view) {
                  remove(i);
                  return true;
              }
          }
          return false;
      }
  
      /**
       * 添加所有
       *
       * @param views  子项视图
       * @param titles 子项标题
       */
      public void addAll(@NonNull Collection<? extends View> views,
                         @Nullable Collection<? extends CharSequence> titles) {
          if (views.isEmpty())
              return;
          int position = mItems.size();
          int index = -1;
          boolean changed = false;
          final CharSequence[] ts = titles == null ? null : (CharSequence[]) titles.toArray();
          for (View view : views) {
              index++;
              if (view == null)
                  continue;
              changed = true;
              final CharSequence title = ts == null ? null : (index >= ts.length ? null : ts[index]);
              final ViewHolder holder = ViewHolderFactory.getInstance().get();
              holder.set(view, title);
              holder.mPosition = position;
              mItems.add(holder);
              position++;
          }
          if (changed)
              notifyDataSetChanged();
      }
  
      /**
       * 添加所有
       *
       * @param views 子项视图
       */
      public void addAllViews(@NonNull Collection<? extends View> views) {
          addAll(views, null);
      }
  
      /**
       * 添加所有
       *
       * @param views 子项视图
       */
      public void addAllViews(View... views) {
          if (views == null || views.length <= 0)
              return;
          int position = mItems.size();
          boolean changed = false;
          for (View view : views) {
              if (view == null)
                  continue;
              changed = true;
              final ViewHolder holder = ViewHolderFactory.getInstance().get();
              holder.set(view, null);
              holder.mPosition = position;
              mItems.add(holder);
              position++;
          }
          if (changed)
              notifyDataSetChanged();
      }
  
      /**
       * 清空
       */
      public void clear() {
          if (mItems.isEmpty())
              return;
          for (ViewHolder holder : mItems) {
              holder.mRemoved = true;
          }
          mItemsShouldCheck.addAll(mItems);
          mItems.clear();
          notifyDataSetChanged();
      }
  
      /**
       * 移动子项
       *
       * @param fromPosition 原始位置
       * @param toPosition   新位置
       */
      public void move(int fromPosition, int toPosition) {
          if (fromPosition == toPosition)
              return;
          final ViewHolder formHolder = mItems.remove(fromPosition);
          mItems.add(toPosition, formHolder);
          final int count = mItems.size();
          for (int i = 0; i < count; i++) {
              final ViewHolder holder = mItems.get(i);
              if (holder.mPosition == i)
                  continue;
              holder.mPosition = i;
              if (mItemsInLayout.contains(holder))
                  holder.mPositionChanged = true;
          }
          notifyDataSetChanged();
      }
  
      /**
       * 配置变更
       *
       * @param newConfig 新配置
       */
      public void onConfigurationChanged(@NonNull Configuration newConfig) {
          if (mItems.isEmpty())
              return;
          for (ViewHolder holder : mItems) {
              if (!mItemsInLayout.contains(holder))
                  holder.itemView.dispatchConfigurationChanged(newConfig);
          }
      }
  
      private static final class ViewHolder {
          View itemView;
          CharSequence mTitle;
          boolean mNeedRebind = false;
          boolean mPositionChanged = false;
          int mPosition = 0;
          boolean mRemoved = false;
  
          void recycle() {
              if (itemView == null)
                  return;
              itemView = null;
              mTitle = null;
              mNeedRebind = false;
              mPositionChanged = false;
              mPosition = -1;
              mRemoved = false;
              ViewHolderFactory.getInstance().put(this);
          }
  
          void set(@NonNull View view, @Nullable CharSequence title) {
              itemView = view;
              mTitle = title;
              mNeedRebind = false;
              mPositionChanged = false;
              mPosition = 0;
              mRemoved = false;
          }
      }
  
      private static final class ViewHolderFactory {
  
          private static ViewHolderFactory mInstance;
          private final ArrayList<ViewHolder> mRecycledViewHolders = new ArrayList<>();
  
          private ViewHolderFactory() {
              //no instance
          }
  
          static ViewHolderFactory getInstance() {
              if (mInstance == null)
                  mInstance = new ViewHolderFactory();
              return mInstance;
          }
  
          @NonNull
          ViewHolder get() {
              return mRecycledViewHolders.isEmpty() ? new ViewHolder() :
                      mRecycledViewHolders.remove(mRecycledViewHolders.size() - 1);
          }
  
          void put(@NonNull ViewHolder holder) {
              mRecycledViewHolders.add(holder);
          }
      }
  }