/*
 * Copyright (C) 2016 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.monkey;

import com.android.tradefed.log.ITestLogger;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.result.FileInputStreamSource;
import com.android.tradefed.result.InputStreamSource;
import com.android.tradefed.result.LogDataType;
import com.android.tradefed.util.CommandResult;
import com.android.tradefed.util.CommandStatus;
import com.android.tradefed.util.FileUtil;
import com.android.tradefed.util.RunUtil;

import java.io.File;
import java.io.IOException;

/**
 * A utility class that encapsulates details of calling post-processing scripts to generate monkey
 * ANR reports.
 */
public class AnrReportGenerator {

    private static final long REPORT_GENERATION_TIMEOUT = 30 * 1000; // 30s

    private File mCachedMonkeyLog = null;
    private File mCachedBugreport = null;

    private final String mReportScriptPath;
    private final String mReportBasePath;
    private final String mReportUrlPrefix;
    private final String mReportPath;
    private final String mDeviceSerial;

    private String mBuildId = null;
    private String mBuildFlavor = null;

    /**
     * Constructs the instance with details of report script and output location information. See
     * matching options on {@link MonkeyBase} for more info.
     */
    public AnrReportGenerator(
            String reportScriptPath,
            String reportBasePath,
            String reportUrlPrefix,
            String reportPath,
            String buildId,
            String buildFlavor,
            String deviceSerial) {
        mReportScriptPath = reportScriptPath;
        mReportBasePath = reportBasePath;
        mReportUrlPrefix = reportUrlPrefix;
        mReportPath = reportPath;
        mBuildId = buildId;
        mBuildFlavor = buildFlavor;
        mDeviceSerial = deviceSerial;

        if (mReportBasePath == null
                || mReportPath == null
                || mReportScriptPath == null
                || mReportUrlPrefix == null) {
            throw new IllegalArgumentException(
                    "ANR post-processing enabled but missing " + "required parameters!");
        }
    }

    /**
     * Return the storage sub path based on build info. The path will not include trailing path
     * separator.
     */
    private String getPerBuildStoragePath() {
        if (mBuildId == null) {
            mBuildId = "-1";
        }
        if (mBuildFlavor == null) {
            mBuildFlavor = "unknown_flavor";
        }
        return String.format("%s/%s", mBuildId, mBuildFlavor);
    }

    /**
     * Sets bugreport information for ANR post-processing script
     *
     * @param bugreportStream
     */
    public void setBugReportInfo(InputStreamSource bugreportStream) throws IOException {
        if (mCachedBugreport != null) {
            CLog.w(
                    "A bugreport for this invocation already existed at %s, overriding anyways",
                    mCachedBugreport.getAbsolutePath());
        }
        mCachedBugreport = FileUtil.createTempFile("monkey-anr-report-bugreport", ".txt");
        FileUtil.writeToFile(bugreportStream.createInputStream(), mCachedBugreport);
    }

    /**
     * Sets monkey log information for ANR post-processing script
     *
     * @param monkeyLogStream
     */
    public void setMonkeyLogInfo(InputStreamSource monkeyLogStream) throws IOException {
        if (mCachedMonkeyLog != null) {
            CLog.w(
                    "A monkey log for this invocation already existed at %s, overriding anyways",
                    mCachedMonkeyLog.getAbsolutePath());
        }
        mCachedMonkeyLog = FileUtil.createTempFile("monkey-anr-report-monkey-log", ".txt");
        FileUtil.writeToFile(monkeyLogStream.createInputStream(), mCachedMonkeyLog);
    }

    public boolean genereateAnrReport(ITestLogger logger) {
        if (mCachedMonkeyLog == null || mCachedBugreport == null) {
            CLog.w("Cannot generate report: bugreport or monkey log not populated yet.");
            return false;
        }
        // generate monkey report and log it
        File reportPath =
                new File(
                        mReportBasePath,
                        String.format("%s/%s", mReportPath, getPerBuildStoragePath()));
        if (reportPath.exists()) {
            if (!reportPath.isDirectory()) {
                CLog.w(
                        "The expected report storage path is not a directory: %s",
                        reportPath.getAbsolutePath());
                return false;
            }
        } else {
            if (!reportPath.mkdirs()) {
                CLog.w(
                        "Failed to create report storage directory: %s",
                        reportPath.getAbsolutePath());
                return false;
            }
        }
        // now we should have the storage path, calculate the HTML report path
        // HTML report file should be named as:
        // monkey-anr-report-<device serial>-<random string>.html
        // under the pre-constructed base report storage path
        File htmlReport = null;
        try {
            htmlReport =
                    FileUtil.createTempFile(
                            String.format("monkey-anr-report-%s-", mDeviceSerial),
                            ".html",
                            reportPath);
        } catch (IOException ioe) {
            CLog.e("Error getting place holder file for HTML report.");
            CLog.e(ioe);
            return false;
        }
        // now ready to call the script
        String htmlReportPath = htmlReport.getAbsolutePath();
        String command[] = {
            mReportScriptPath,
            "--monkey",
            mCachedMonkeyLog.getAbsolutePath(),
            "--html",
            htmlReportPath,
            mCachedBugreport.getAbsolutePath()
        };
        CommandResult cr =
                RunUtil.getDefault().runTimedCmdSilently(REPORT_GENERATION_TIMEOUT, command);
        if (cr.getStatus() == CommandStatus.SUCCESS) {
            // Test log the generated HTML report
            try (InputStreamSource source = new FileInputStreamSource(htmlReport)) {
                logger.testLog("monkey-anr-report", LogDataType.HTML, source);
            }
            // Clean up and declare success!
            FileUtil.deleteFile(htmlReport);
            return true;
        } else {
            CLog.w(cr.getStderr());
            return false;
        }
    }

    public void cleanTempFiles() {
        FileUtil.deleteFile(mCachedBugreport);
        FileUtil.deleteFile(mCachedMonkeyLog);
    }
}
