/*
 * Copyright (C) 2009 The Android Open Source Project
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifndef ANDROID_NATIVETEST_SYSTEM_EXTRAS_TESTS_SDCARD_STOPWATCH_H_
#define ANDROID_NATIVETEST_SYSTEM_EXTRAS_TESTS_SDCARD_STOPWATCH_H_

#include <stdlib.h>
#include <stdio.h>
#include <time.h>

namespace android_test {

// StopWatch class to collect execution statistics.
//
// Once the watch has been created, start and stop can be called to
// capture an event duration.
//
// On completion, use 'sprint' to retrieve the data.
//
// If StopWatch::setPrintRawMode(true) has been called, the raw
// samples also are printed.
// The print method is thread safe to avoid mixing the result of
// watches on different threads. For processes, use different files
// that you concat after the run.
//
// If the time measure is associated with some volume of data, use
// setMbytes, the print method will compute the average throughput
// based on that value.
//
// To capture the time accurately and since the runs are not too long,
// we collect the raw start and stop time in an array that get
// processed once all the measurements have been done.
//
// Typical Usage:
// ==============
//
//  StopWatch watch("my name", 20);
//
//  for (int i = 0; i < 20; ++i) {
//    watch.start();
//    doMyStuff();
//    watch.stop();
//  }
//  char buffer[4096];
//  char *str = buffer;
//  size_t size = sizeof(buffer);
//  watch.sprint(&str, &size);
//

class StopWatch {
  public:
    // Time of the snapshot and its nature (start of the interval or end of it).
    struct Measurement {
        struct timespec mTime;
        bool mIsStart;
    };
    static const size_t kUseDefaultCapacity = 20;

    // Create a stop watch. Default capacity == 2 * interval_nb
    // @param name To be used when the results are displayed. No
    //             spaces, use _ instead.
    // @param capacity Hint about the number of sampless that will be
    //                 measured (1 sample == 1 start + 1 stop). Used
    //                 to size the internal storage, when the capacity
    //                 is reached, it is doubled.
    StopWatch(const char *name, size_t capacity = kUseDefaultCapacity);
    ~StopWatch();

    // A StopWatch instance measures time intervals. Use setDataSize
    // if some volume of data is processed during these intervals, to
    // get the average throughput (in kbytes/s) printed.
    void setDataSize(size_t size_in_bytes) { mSizeKbytes = size_in_bytes / 1000; }

    // Starts and stops the timer. The time between the 2 calls is an
    // interval whose duration will be reported in sprint.
    void start();
    void stop();

    // Print a summary of the measurement and optionaly the raw data.
    // The summary is commented out using a leading '#'.  The raw data
    // is a pair (time, duration). The 1st sample is always at time
    // '0.0'.
    // @param str[inout] On entry points to the begining of a buffer
    // where to write the data. On exit points pass the last byte
    // written.
    // @param size[inout] On entry points to the size of the buffer
    // pointed by *str. On exit *size is the amount of free space left
    // in the buffer. If there was not enough space the data is truncated
    // and a warning is printed.
    void sprint(char **str, size_t *size);

    // @return true if at least one interval was timed.
    bool used() const { return mUsed; }

    // Affects all the timers. Instructs all the timers to print the
    // raw data as well as the summary.
    static void setPrintRawMode(bool printRaw);

  private:
    void checkCapacity();
    double timespecToDouble(const struct timespec& time);
    void printAverageMinMax(char **str, size_t *size);
    void printThroughput(char **str, size_t *size);
    // Allocate mDeltas and fill it in. Search for the min and max.
    void processSamples();

    char *const mName;  // Name of the test.
    struct timespec mStart;
    size_t mNum; // # of intervals == # of start() calls.
    struct Measurement *mData;
    size_t mDataLen;
    size_t mCapacity;
    int mSizeKbytes;

    bool mAlreadyPrinted;
    bool mPrintRaw;

    double mDuration;
    double mDeviation;
    double mMinDuration;
    size_t mMinIdx;
    double mMaxDuration;
    size_t mMaxIdx;
    double *mDeltas;

    bool mUsed;
};

}  // namespace android_test

#endif  // ANDROID_NATIVETEST_SYSTEM_EXTRAS_TESTS_SDCARD_STOPWATCH_H_