// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.tabmodel;

import org.chromium.chrome.browser.Tab;
import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;

/**
 * This class acts as a controller for determining where tabs should be inserted
 * into a tab strip model. See tab_strip_model_order_controller.cc and
 * tab_strip_model.cc
 */
public class TabModelOrderController {

    private static final int NO_TAB = -1;
    private final TabModelSelector mTabModelSelector;

    public TabModelOrderController(TabModelSelector modelSelector) {
        mTabModelSelector = modelSelector;
    }

    /**
     * Determine the insertion index of the next tab. If it's not the result of
     * a link being pressed, the provided index will be returned.
     *
     * @param type The launch type of the new tab.
     * @param position The provided position.
     * @return Where to insert the tab.
     */
    public int determineInsertionIndex(TabLaunchType type, int position, Tab newTab) {
        if (linkClicked(type)) {
            position = determineInsertionIndex(type, newTab);
        }

        if (willOpenInForeground(type, newTab.isIncognito())) {
            // Forget any existing relationships, we don't want to make things
            // too confusing by having multiple groups active at the same time.
            forgetAllOpeners();
        }

        return position;
    }

    /**
     * Determine the insertion index of the next tab.
     *
     * @param type The launch type of the new tab.
     * @return Where to insert the tab.
     */
    public int determineInsertionIndex(TabLaunchType type, Tab newTab) {
        TabModel currentModel = mTabModelSelector.getCurrentModel();
        Tab currentTab = TabModelUtils.getCurrentTab(currentModel);
        if (currentTab == null) {
            assert (currentModel.getCount() == 0);
            return 0;
        }
        int currentId = currentTab.getId();
        int currentIndex = TabModelUtils.getTabIndexById(currentModel, currentId);

        if (sameModelType(currentModel, newTab)) {
            if (willOpenInForeground(type, newTab.isIncognito())) {
                // If the tab was opened in the foreground, insert it adjacent to
                // the tab that opened that link.
                return currentIndex + 1;
            } else {
                // If the tab was opened in the background, position at the end of
                // it's 'group'.
                int index = getIndexOfLastTabOpenedBy(currentId, currentIndex);
                if (index != NO_TAB) {
                    return index + 1;
                } else {
                    return currentIndex + 1;
                }
            }
        } else {
            // If the tab is opening in the other model type, just put it at the end.
            return mTabModelSelector.getModel(newTab.isIncognito()).getCount();
        }
    }

    /**
     * Returns the index of the last tab in the model opened by the specified
     * opener, starting at startIndex. To clarify, the tabs are traversed in the
     * descending order of their position in the model. This means that the tab
     * furthest in the stack with the given opener id will be returned.
     *
     * @param openerId The opener of interest.
     * @param startIndex The start point of the search.
     * @return The last tab if found, NO_TAB otherwise.
     */
    private int getIndexOfLastTabOpenedBy(int openerId, int startIndex) {
        TabModel currentModel = mTabModelSelector.getCurrentModel();
        int count = currentModel.getCount();
        for (int i = count - 1; i >= startIndex; i--) {
            Tab tab = currentModel.getTabAt(i);
            if (tab.getParentId() == openerId && tab.isGroupedWithParent()) {
                return i;
            }
        }
        return NO_TAB;
    }

    /**
     * Clear the opener attribute on all tabs in the model.
     */
    void forgetAllOpeners() {
        TabModel currentModel = mTabModelSelector.getCurrentModel();
        int count = currentModel.getCount();
        for (int i = 0; i < count; i++) {
            currentModel.getTabAt(i).setGroupedWithParent(false);
        }
    }

    /**
     * Determine if a launch type is the result of linked being clicked.
     */
    static boolean linkClicked(TabLaunchType type) {
        return type == TabLaunchType.FROM_LINK ||
                type == TabLaunchType.FROM_LONGPRESS_FOREGROUND ||
                type == TabLaunchType.FROM_LONGPRESS_BACKGROUND;
    }

    /**
     * Determine if a launch type will result in the tab being opened in the
     * foreground.
     * @param type               The type of opening event.
     * @param isNewTabIncognito  True if the new opened tab is incognito.
     * @return                   True if the tab will be in the foreground
     */
    public boolean willOpenInForeground(TabLaunchType type, boolean isNewTabIncognito) {
        // Restore is handling the active index by itself.
        return (type != TabLaunchType.FROM_LONGPRESS_BACKGROUND &&
                        type != TabLaunchType.FROM_RESTORE)
                || (!mTabModelSelector.isIncognitoSelected() && isNewTabIncognito);
    }

    /**
     * @return {@code true} If both tabs have the same model type, {@code false} otherwise.
     */
    static boolean sameModelType(TabModel model, Tab tab) {
        return model.isIncognito() == tab.isIncognito();
    }

}

@@ -5161,22 +5161,18 @@
     
-return
+if
  (type 
-!
+=
 = Ta
@@ -5192,54 +5192,39 @@
 ROM_
-LONGPRESS_BACKGROUND &&%0A              
+RESTORE) return false;%0A
         
   ty
@@ -5215,25 +5215,30 @@
 se;%0A        
- 
+return
  type != Tab
@@ -5253,24 +5253,36 @@
 pe.FROM_
-RESTORE)
+LONGPRESS_BACKGROUND
 %0A       

Fix incognito tab restore regression