/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * 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.android.server.wifi;

import static com.android.server.wifi.proto.WifiStatsLog.WIFI_THREAD_TASK_EXECUTED;

import android.annotation.NonNull;
import android.os.Message;
import android.os.Trace;
import android.util.LocalLog;

import com.android.internal.util.State;
import com.android.server.wifi.proto.WifiStatsLog;

/**
 * RunnerState class is a wrapper based on State class to monitor and track the State enter/exit
 * and message handler execution for taking longer time than the expected threshold.
 * User must extend the RunnerState class instead of State, and provide the implementation of:
 * { @link RunnerState#enterImpl() } { @link RunnerState#exitImpl() }
 * { @link RunnerState#processMessageImpl() }
 * { @link RunnerState#getMessageLogRec() }
 *
 */
public abstract class RunnerState extends State {
    private static final String TAG = "RunnerState";
    private static final int METRICS_THRESHOLD_MILLIS = 100;

    /** Message.what value when entering */
    public static final int STATE_ENTER_CMD = -3;

    /** Message.what value when exiting */
    public static final int STATE_EXIT_CMD = -4;

    private final int mRunningTimeThresholdInMilliseconds;
    // TODO: b/246623192 Add Wifi metric for Runner state overruns.
    private final LocalLog mLocalLog;

    /**
     * The Runner state Constructor
     * @param threshold the running time threshold in milliseconds
     */
    public RunnerState(int threshold, @NonNull LocalLog localLog) {
        mRunningTimeThresholdInMilliseconds = threshold;
        mLocalLog = localLog;
    }

    @Override
    public boolean processMessage(Message message) {
        long startTime = System.currentTimeMillis();

        String signatureToLog = getMessageLogRec(message);
        if (signatureToLog == null) {
            signatureToLog = getMessageLogRec(message.what);
        }
        Trace.traceBegin(Trace.TRACE_TAG_NETWORK, signatureToLog);
        boolean ret = processMessageImpl(message);
        Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
        long runTime = System.currentTimeMillis() - startTime;
        if (runTime > mRunningTimeThresholdInMilliseconds) {
            mLocalLog.log(signatureToLog + " was running for " + runTime + " ms");
        }
        if (runTime > METRICS_THRESHOLD_MILLIS) {
            WifiStatsLog.write(WIFI_THREAD_TASK_EXECUTED, (int) runTime, 0, signatureToLog);
        }
        return ret;
    }

    @Override
    public void enter() {
        long startTime = System.currentTimeMillis();
        String signatureToLog = getMessageLogRec(STATE_ENTER_CMD);
        Trace.traceBegin(Trace.TRACE_TAG_NETWORK, signatureToLog);
        enterImpl();
        Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
        long runTime = System.currentTimeMillis() - startTime;
        if (runTime > mRunningTimeThresholdInMilliseconds) {
            mLocalLog.log(signatureToLog + " was running for " + runTime + " ms");
        }
        if (runTime > METRICS_THRESHOLD_MILLIS) {
            WifiStatsLog.write(WIFI_THREAD_TASK_EXECUTED, (int) runTime, 0, signatureToLog);
        }
    }

    @Override
    public void exit() {
        long startTime = System.currentTimeMillis();
        String signatureToLog = getMessageLogRec(STATE_EXIT_CMD);
        Trace.traceBegin(Trace.TRACE_TAG_NETWORK, signatureToLog);
        exitImpl();
        Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
        long runTime = System.currentTimeMillis() - startTime;
        if (runTime > mRunningTimeThresholdInMilliseconds) {
            mLocalLog.log(signatureToLog + " was running for " + runTime + " ms");
        }
        if (runTime > METRICS_THRESHOLD_MILLIS) {
            WifiStatsLog.write(WIFI_THREAD_TASK_EXECUTED, (int) runTime, 0, signatureToLog);
        }
    }

    /**
     * Implement this method for State enter process, instead of enter()
     */
    public abstract void enterImpl();

    /**
     * Implement this method for State exit process, instead of exit()
     */
    public abstract void exitImpl();

    /**
     * Implement this method for process message, instead of processMessage()
     */
    public abstract boolean processMessageImpl(Message msg);

    /**
     * Implement this to translate a message `what` into a readable String
     * @param what message 'what' field
     * @return Readable string
     */
    public String getMessageLogRec(int what) {
        return null;
    };

    /**
     * Implement this to translate a message into a readable String
     * @return Readable string
     */
    public String getMessageLogRec(Message message) {
        return null;
    };
}
