/*
 *    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.utils;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;

import com.h6ah4i.android.widget.advrecyclerview.adapter.AdapterPath;
import com.h6ah4i.android.widget.advrecyclerview.adapter.AdapterPathSegment;
import com.h6ah4i.android.widget.advrecyclerview.adapter.SimpleWrapperAdapter;
import com.h6ah4i.android.widget.advrecyclerview.adapter.UnwrapPositionResult;
import com.h6ah4i.android.widget.advrecyclerview.adapter.WrapperAdapter;

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

public class WrapperAdapterUtils {

    private WrapperAdapterUtils() {
    }

    public static <T> T findWrappedAdapter(RecyclerView.Adapter adapter, Class<T> clazz) {
        if (clazz.isInstance(adapter)) {
            return clazz.cast(adapter);
        } else if (adapter instanceof SimpleWrapperAdapter) {
            final RecyclerView.Adapter wrappedAdapter = ((SimpleWrapperAdapter) adapter).getWrappedAdapter();
            return findWrappedAdapter(wrappedAdapter, clazz);
        } else {
            return null;
        }
    }

    public static <T> T findWrappedAdapter(RecyclerView.Adapter originAdapter, Class<T> clazz, int position) {
        final AdapterPath path = new AdapterPath();
        final int wrappedPosition = unwrapPosition(originAdapter, null, null, position, path);

        if (wrappedPosition == RecyclerView.NO_POSITION) {
            return null;
        }

        for (AdapterPathSegment segment : path.segments()) {
            if (clazz.isInstance(segment.adapter)) {
                return clazz.cast(segment.adapter);
            }
        }

        return null;
    }

    public static RecyclerView.Adapter releaseAll(RecyclerView.Adapter adapter) {
        return releaseCyclically(adapter);
    }

    @SuppressWarnings("unchecked")
    private static RecyclerView.Adapter releaseCyclically(RecyclerView.Adapter adapter) {
        if (!(adapter instanceof WrapperAdapter)) {
            return adapter;
        }

        final WrapperAdapter wrapperAdapter = (WrapperAdapter) adapter;
        final List<RecyclerView.Adapter> wrappedAdapters = new ArrayList<>();

        wrapperAdapter.getWrappedAdapters(wrappedAdapters);

        wrapperAdapter.release();

        for (int i = wrappedAdapters.size() - 1; i >= 0; i--) {
            RecyclerView.Adapter wrappedAdapter = wrappedAdapters.get(i);
            releaseCyclically(wrappedAdapter);
        }

        wrappedAdapters.clear();

        return adapter;
    }

    public static int unwrapPosition(@NonNull RecyclerView.Adapter originAdapter, int position) {
        return unwrapPosition(originAdapter, null, position);
    }

    public static int unwrapPosition(@NonNull RecyclerView.Adapter originAdapter, @Nullable RecyclerView.Adapter targetAdapter, int position) {
        return unwrapPosition(originAdapter, targetAdapter, null, position, null);
    }


    public static int unwrapPosition(@NonNull RecyclerView.Adapter originAdapter, @Nullable RecyclerView.Adapter targetAdapter, Object targetAdapterTag, int position) {
        return unwrapPosition(originAdapter, targetAdapter, targetAdapterTag, position, null);
    }

    public static int unwrapPosition(RecyclerView.Adapter originAdapter, RecyclerView.Adapter targetAdapter, Object targetAdapterTag, int originPosition, @Nullable AdapterPath destPath) {
        RecyclerView.Adapter wrapper = originAdapter;
        int wrappedPosition = originPosition;
        final UnwrapPositionResult tmpResult = new UnwrapPositionResult();
        Object wrappedAdapterTag = null;

        if (destPath != null) {
            destPath.clear();
        }

        if (wrapper == null) {
            return RecyclerView.NO_POSITION;
        }

        if (destPath != null) {
            destPath.append(new AdapterPathSegment(originAdapter, null));
        }

        do {
            if (wrappedPosition == RecyclerView.NO_POSITION) {
                break;
            }

            if (wrapper == targetAdapter) {
                break;
            }

            if (!(wrapper instanceof WrapperAdapter)) {
                if (targetAdapter != null) {
                    wrappedPosition = RecyclerView.NO_POSITION;
                }
                break;
            }

            final WrapperAdapter wrapperParentAdapter = (WrapperAdapter) wrapper;

            tmpResult.clear();
            wrapperParentAdapter.unwrapPosition(tmpResult, wrappedPosition);
            wrappedPosition = tmpResult.position;
            wrappedAdapterTag = tmpResult.tag;

            if (tmpResult.isValid()) {
                if (destPath != null) {
                    destPath.append(tmpResult);
                }
            }

            wrapper = tmpResult.adapter;
        } while (wrapper != null);

        if (targetAdapter != null && wrapper != targetAdapter) {
            wrappedPosition = RecyclerView.NO_POSITION;
        }

        if (targetAdapterTag != null && (wrappedAdapterTag != targetAdapterTag)) {
            wrappedPosition = RecyclerView.NO_POSITION;
        }

        if (wrappedPosition == RecyclerView.NO_POSITION && destPath != null) {
            destPath.clear();
        }

        return wrappedPosition;
    }

    public static int wrapPosition(@NonNull AdapterPath path, @Nullable RecyclerView.Adapter originAdapter, @Nullable RecyclerView.Adapter targetAdapter, int position) {
        final List<AdapterPathSegment> segments = path.segments();
        final int nSegments = segments.size();

        int originSegmentIndex = (originAdapter == null) ? nSegments - 1 : -1;
        int targetSegmentIndex = (targetAdapter == null) ? 0 : -1;

        if (originAdapter != null || targetAdapter != null) {
            for (int i = 0; i < nSegments; i++) {
                AdapterPathSegment segment = segments.get(i);
                if (originAdapter != null && segment.adapter == originAdapter) {
                    originSegmentIndex = i;
                }
                if (targetAdapter != null && segment.adapter == targetAdapter) {
                    targetSegmentIndex = i;
                }
            }
        }

        if (!((originSegmentIndex != -1) && (targetSegmentIndex != -1) && (targetSegmentIndex <= originSegmentIndex))) {
            return RecyclerView.NO_POSITION;
        }

        return wrapPosition(path, originSegmentIndex, targetSegmentIndex, position);
    }

    public static int wrapPosition(@NonNull AdapterPath path, int originSegmentIndex, int targetSegmentIndex, int position) {
        final List<AdapterPathSegment> segments = path.segments();
        int wrappedPosition = position;

        for (int i = originSegmentIndex; i > targetSegmentIndex; i--) {
            AdapterPathSegment segment = segments.get(i);
            AdapterPathSegment parentSegment = segments.get(i - 1);

            final int prevWrappedPosition = wrappedPosition;

            wrappedPosition = ((WrapperAdapter) parentSegment.adapter).wrapPosition(segment, wrappedPosition);

            if (wrappedPosition == RecyclerView.NO_POSITION) {
                break;
            }
        }

        return wrappedPosition;
    }

}

@@ -3889,32 +3889,347 @@
 , null);%0A    %7D%0A%0A
+    public static int unwrapPosition(RecyclerView.Adapter originAdapter, AdapterPathSegment targetAdapterPathSegment, int originPosition, @Nullable AdapterPath destPath) %7B%0A        return unwrapPosition(originAdapter, targetAdapterPathSegment.adapter, targetAdapterPathSegment.tag, originPosition, destPath);%0A    %7D%0A%0A
     public stati

Add variant of unwrapPosition() which takes an adapter path segment for the target adapter