// Copyright 2015 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.customtabs;

import android.app.Application;
import android.os.IBinder;
import android.os.SystemClock;
import android.support.customtabs.CustomTabsCallback;

import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.browser.tab.EmptyTabObserver;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabObserver;
import org.chromium.components.security_state.ConnectionSecurityLevel;
import org.chromium.content_public.browser.LoadUrlParams;

import java.util.concurrent.TimeUnit;

/**
 * A {@link TabObserver} that also handles custom tabs specific logging and messaging.
 */
class CustomTabObserver extends EmptyTabObserver {
    private CustomTabsConnection mCustomTabsConnection;
    private IBinder mSession;
    private long mIntentReceivedTimestamp;
    private long mPageLoadStartedTimestamp;

    private static final int STATE_RESET = 0;
    private static final int STATE_WAITING_LOAD_START = 1;
    private static final int STATE_WAITING_LOAD_FINISH = 2;
    private int mCurrentState;

    public CustomTabObserver(Application application, IBinder session) {
        mCustomTabsConnection = CustomTabsConnection.getInstance(application);
        mSession = session;
        resetPageLoadTracking();
    }

    /**
     * Tracks the next page load, with timestamp as the origin of time.
     */
    public void trackNextPageLoadFromTimestamp(long timestamp) {
        mIntentReceivedTimestamp = timestamp;
        mCurrentState = STATE_WAITING_LOAD_START;
    }

    @Override
    public void onLoadUrl(Tab tab, LoadUrlParams params, int loadType) {
        mCustomTabsConnection.registerLaunch(mSession, params.getUrl());
    }

    @Override
    public void onPageLoadStarted(Tab tab, String url) {
        if (mCurrentState == STATE_WAITING_LOAD_START) {
            mPageLoadStartedTimestamp = SystemClock.elapsedRealtime();
            mCurrentState = STATE_WAITING_LOAD_FINISH;
        } else if (mCurrentState == STATE_WAITING_LOAD_FINISH) {
            mCustomTabsConnection.notifyNavigationEvent(
                    mSession, CustomTabsCallback.NAVIGATION_ABORTED);
            mPageLoadStartedTimestamp = SystemClock.elapsedRealtime();
        }
        mCustomTabsConnection.notifyNavigationEvent(
                mSession, CustomTabsCallback.NAVIGATION_STARTED);
    }

    @Override
    public void onShown(Tab tab) {
        mCustomTabsConnection.notifyNavigationEvent(
                mSession, CustomTabsCallback.TAB_SHOWN);
    }

    @Override
    public void onPageLoadFinished(Tab tab) {
        long pageLoadFinishedTimestamp = SystemClock.elapsedRealtime();
        mCustomTabsConnection.notifyNavigationEvent(
                mSession, CustomTabsCallback.NAVIGATION_FINISHED);
        // Both histograms (commit and PLT) are reported here, to make sure
        // that they are always recorded together, and that we only record
        // commits for successful navigations.
        if (mCurrentState == STATE_WAITING_LOAD_FINISH && mIntentReceivedTimestamp > 0) {
            long timeToPageLoadStartedMs = mPageLoadStartedTimestamp - mIntentReceivedTimestamp;
            long timeToPageLoadFinishedMs =
                    pageLoadFinishedTimestamp - mIntentReceivedTimestamp;
            // Same bounds and bucket count as "Startup.FirstCommitNavigationTime"
            RecordHistogram.recordCustomTimesHistogram(
                    "CustomTabs.IntentToFirstCommitNavigationTime", timeToPageLoadStartedMs,
                    1, TimeUnit.MINUTES.toMillis(1), TimeUnit.MILLISECONDS, 225);
            // Same bounds and bucket count as PLT histograms.
            RecordHistogram.recordCustomTimesHistogram("CustomTabs.IntentToPageLoadedTime",
                    timeToPageLoadFinishedMs, 10, TimeUnit.MINUTES.toMillis(10),
                    TimeUnit.MILLISECONDS, 100);
        }
        resetPageLoadTracking();
    }

    @Override
    public void onDidAttachInterstitialPage(Tab tab) {
        if (tab.getSecurityLevel() != ConnectionSecurityLevel.SECURITY_ERROR) return;
        resetPageLoadTracking();
        mCustomTabsConnection.notifyNavigationEvent(
                mSession, CustomTabsCallback.NAVIGATION_FAILED);
    }

    @Override
    public void onPageLoadFailed(Tab tab, int errorCode) {
        resetPageLoadTracking();
        mCustomTabsConnection.notifyNavigationEvent(
                mSession, CustomTabsCallback.NAVIGATION_FAILED);
    }

    private void resetPageLoadTracking() {
        mCurrentState = STATE_RESET;
        mIntentReceivedTimestamp = -1;
    }
}
