// 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.metrics;

import android.content.Intent;
import android.os.Handler;

import org.chromium.base.ContextUtils;
import org.chromium.base.metrics.RecordHistogram;

/**
 * Keeps track of actions taken on startup.
 */
public class StartupMetrics {
    // Actions we keep track of. Do not change indices, add new actions at the end only
    // and update MAX_INDEX.
    private static final int NO_ACTIVITY = 0;
    private static final int OPENED_NTP = 1;
    private static final int FOCUSED_OMNIBOX = 2;
    private static final int OPENED_BOOKMARKS = 3;
    private static final int OPENED_RECENTS = 4;
    private static final int OPENED_HISTORY = 5;
    private static final int OPENED_TAB_SWITCHER = 6;
    private static final int MAX_INDEX = 7;

    // Keeps track of the actions invoked in the first RECORDING_THRESHOLD_NS.
    private int mFirstActionTaken = NO_ACTIVITY;

    private boolean mIsMainIntent;
    private Handler mHandler;
    // This ensures metrics are recorded only once per updateIntent(...) call.
    private boolean mShouldRecordHistogram;

    // Startup time is measured by two different time sources.
    // {@code mStartTimeNanoMonotonic} is measured from a monotonic time source with nanosecond
    // precision whereas {@code mStartTimeMilli} is measured from the wall clock with millisecond
    // precision. The monotonic time source may not persist across reboots whereas the wall clock
    // is subject to change by the user.
    private long mStartTimeNanoMonotonic;
    private long mStartTimeMilli;

    private static StartupMetrics sInstance;

    // Record only the first 10s.
    private static final long RECORDING_THRESHOLD_NS = 10000000000L;
    private static final int MILLI_SEC_PER_MINUTE = 60000;
    private static final int MINUTES_PER_30DAYS = 43200;
    // Bucket sizes are exponential so we get minute level granularity for first 10 minutes.
    private static final int NUM_BUCKETS = 50;

    public static StartupMetrics getInstance() {
        if (sInstance == null) {
            sInstance = new StartupMetrics();
        }
        return sInstance;
    }

    // Singleton
    private StartupMetrics() {
        mHandler = new Handler();
    }

    /**
     * Starts collecting metrics for the latest intent sent to DocumentActivity or
     * ChromeTabbedActivity. This happens on every intent coming from launcher/external app
     * for DocumentActivity and every time we get onStart() in ChromeTabbedActivity mode.
     */
    public void updateIntent(Intent intent) {
        mIsMainIntent = intent != null && Intent.ACTION_MAIN.equals(intent.getAction());
        mFirstActionTaken = NO_ACTIVITY;
        mStartTimeNanoMonotonic = System.nanoTime();
        mStartTimeMilli = System.currentTimeMillis();
        mShouldRecordHistogram = true;
    }

    private boolean isShortlyAfterChromeStarted() {
        return (System.nanoTime() - mStartTimeNanoMonotonic) <= RECORDING_THRESHOLD_NS;
    }

    private void setFirstAction(int type) {
        if (!isShortlyAfterChromeStarted() || mFirstActionTaken != NO_ACTIVITY) return;
        mFirstActionTaken = type;
    }

    /** Records that the new tab page has been opened. */
    public void recordOpenedNTP() {
        setFirstAction(OPENED_NTP);
    }

    /** Records that the omnibox has been focused. */
    public void recordFocusedOmnibox() {
        setFirstAction(FOCUSED_OMNIBOX);
    }

    /** Records that the bookmarks page has been opened. */
    public void recordOpenedBookmarks() {
        setFirstAction(OPENED_BOOKMARKS);
    }

    /** Records that the recents page has been opened. */
    public void recordOpenedRecents() {
        setFirstAction(OPENED_RECENTS);
    }

    /** Records that the history page has been opened. */
    public void recordOpenedHistory() {
        setFirstAction(OPENED_HISTORY);
    }

    /** Records that the tab switcher has been accessed. */
    public void recordOpenedTabSwitcher() {
        setFirstAction(OPENED_TAB_SWITCHER);
    }

    /** Records the startup data in a histogram. Should only be called after native is loaded. */
    public void recordHistogram(boolean onStop) {
        if (!mShouldRecordHistogram) return;
        if (!isShortlyAfterChromeStarted() || mFirstActionTaken != NO_ACTIVITY || onStop) {
            String histogramName = mIsMainIntent ? "MobileStartup.MainIntentAction" :
                    "MobileStartup.NonMainIntentAction";
            RecordHistogram.recordEnumeratedHistogram(histogramName, mFirstActionTaken, MAX_INDEX);
            mShouldRecordHistogram = false;
            long lastUsedTimeMilli = ContextUtils.getAppSharedPreferences().getLong(
                    UmaSessionStats.LAST_USED_TIME_PREF, 0);
            if (mIsMainIntent && (lastUsedTimeMilli > 0) && (mStartTimeMilli > lastUsedTimeMilli)
                    && (mStartTimeMilli - lastUsedTimeMilli > Integer.MAX_VALUE)) {
                // Measured in minutes and capped at a day with a bucket precision of 6 minutes.
                RecordHistogram.recordCustomCountHistogram("MobileStartup.TimeSinceLastUse",
                        (int) (mStartTimeMilli - lastUsedTimeMilli) / MILLI_SEC_PER_MINUTE, 1,
                        MINUTES_PER_30DAYS, NUM_BUCKETS);
            }
        } else {
            // Call back later to record the histogram after 10s have elapsed.
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    recordHistogram(false);
                }
            }, (RECORDING_THRESHOLD_NS - (System.nanoTime() - mStartTimeNanoMonotonic)) / 1000000);
        }
    }
}