/*
 * MIT License
 *
 * Copyright (c) 2018 Gwangseong Choi
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package kstarchoi.lib.recyclerview.builder.typefree;

import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author Gwangseong Choi
 * @since 2018-09-22
 */

final class ViewAdapterImpl extends RecyclerView.Adapter<ViewHolderImpl> implements ViewAdapter {

    private final ArrayList<Object> dataList = new ArrayList<>();
    private final ViewBindHelper viewBindHelper;

    ViewAdapterImpl(ViewBindHelper viewBindHelper) {
        this.viewBindHelper = viewBindHelper;
    }


    private Object getData(int index) {
        return dataList.get(index);
    }

    @Override
    public int getItemViewType(int position) {
        return viewBindHelper.getViewType(getData(position));
    }

    @Override
    public ViewHolderImpl onCreateViewHolder(ViewGroup parent, int viewType) {
        int layoutRes = viewBindHelper.getLayoutRes(viewType);

        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View view = inflater.inflate(layoutRes, parent, false);
        return new ViewHolderImpl(view);
    }

    @Override
    public void onBindViewHolder(ViewHolderImpl holder, int position) {
        Object data = getData(position);
        ViewBinderImpl<?> viewBinderImpl = viewBindHelper.getViewBinderImpl(data);
        viewBinderImpl.bind(holder, data, position);
    }

    @Override
    public int getItemCount() {
        return dataList.size();
    }


    // =============================================================================================
    // ViewAdapter interface
    // =============================================================================================

    @Override
    public void setDataList(@NonNull List<?> dataList) {
        AssertionHelper.notNull("dataList", dataList);

        this.dataList.clear();
        this.dataList.addAll(dataList);
        notifyDataSetChanged();
    }

    @Override
    public void insertData(@NonNull Object data, Object... dataArray) {
        insertDataTo(dataList.size(), data, dataArray);
    }

    @Override
    public void insertDataTo(@IntRange(from = 0) int index,
                             @NonNull Object data, Object... dataArray) {
        AssertionHelper.interior("index", index, 0, dataList.size());
        AssertionHelper.notNull("data", data);
        AssertionHelper.notContains("null", null, dataArray);

        viewBindHelper.checkBoundDataType(data, dataArray);

        for (int i = dataArray.length - 1; i >= 0; i--) {
            dataList.add(index, dataArray[i]);
        }
        dataList.add(index, data);
        notifyItemRangeInserted(index, 1 + dataArray.length);
    }

    @Override
    public void insertDataAll(@NonNull List<?> dataList) {
        insertDataAllTo(this.dataList.size(), dataList);
    }

    @Override
    public void insertDataAllTo(@IntRange(from = 0) int index, @NonNull List<?> dataList) {
        AssertionHelper.interior("index", index, 0, this.dataList.size());
        AssertionHelper.notNull("dataList", dataList);
        AssertionHelper.notContains("null", null, dataList);

        viewBindHelper.checkBoundDataType(dataList);

        this.dataList.addAll(index, dataList);
        notifyItemRangeInserted(index, dataList.size());
    }

    @Override
    public void removeData(@NonNull Object data, Object... dataArray) {
        ArrayList<Integer> dataIndexList = new ArrayList<>();
        dataIndexList.add(dataList.indexOf(data));
        for (Object aData : dataArray) {
            dataIndexList.add(dataList.indexOf(aData));
        }

        removeData(dataIndexList);
    }

    @Override
    public void removeDataAll(@NonNull List<?> dataList) {
        AssertionHelper.notNull("dataList", dataList);

        ArrayList<Integer> dataIndexList = new ArrayList<>();
        for (Object data : dataList) {
            dataIndexList.add(this.dataList.indexOf(data));
        }

        removeData(dataIndexList);
    }

    @Override
    public void removeDataAt(@IntRange(from = 0) int index, int... indexArray) {
        int dataCount = dataList.size();
        AssertionHelper.interior("index", index, 0, dataCount);
        AssertionHelper.interior("index", indexArray, 0, dataCount);

        ArrayList<Integer> dataIndexList = new ArrayList<>();
        dataIndexList.add(index);
        for (int aIndex : indexArray) {
            dataIndexList.add(aIndex);
        }

        removeData(dataIndexList);
    }

    private void removeData(List<Integer> dataIndexList) {
        Collections.sort(dataIndexList);

        for (int i = dataIndexList.size() - 1; i >= 0; i--) {
            int dataIndex = dataIndexList.get(i);
            if (dataIndex < 0) {
                break;
            }

            dataList.remove(dataIndex);
            notifyItemRemoved(dataIndex);
        }
    }

    @Override
    public void removeDataFrom(@IntRange(from = 0) int index, @IntRange(from = 1) int dataCount) {
        int adapterDataCount = dataList.size();
        int lastIndex = index + dataCount - 1;
        AssertionHelper.interior("index", index, 0, adapterDataCount);
        AssertionHelper.greaterThanOrEqualTo("dataCount", dataCount, 1);
        AssertionHelper.interior("lastIndex", lastIndex, 0, adapterDataCount);

        for (int i = lastIndex; i >= index; i--) {
            dataList.remove(i);
        }
        notifyItemRangeRemoved(index, dataCount);
    }
}

@@ -5414,25 +5414,29 @@
     int 
-dataCount
+lastDataIndex
  = dataL
@@ -5437,32 +5437,36 @@
  dataList.size()
+ - 1
 ;%0A        Assert
@@ -5503,25 +5503,29 @@
 dex, 0, 
-dataCount
+lastDataIndex
 );%0A     
@@ -5576,25 +5576,29 @@
 ray, 0, 
-dataCount
+lastDataIndex
 );%0A%0A    
@@ -6329,32 +6329,29 @@
     int 
-adapterDataCount
+lastDataIndex
  = dataL
@@ -6356,24 +6356,28 @@
 aList.size()
+ - 1
 ;%0A        in
@@ -6465,32 +6465,29 @@
 dex, 0, 
-adapterDataCount
+lastDataIndex
 );%0A     
@@ -6618,24 +6618,21 @@
  0, 
-adapterDataCount
+lastDataIndex
 );%0A%0A

Resolve wrong assertion issue for last data index