/*
 * Copyright (C) 2019 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.
 */
#ifndef _IO_USAGE_H_
#define _IO_USAGE_H_

#include <statstype.h>
#include <chrono>
#include <sstream>
#include <string>

#include <unordered_map>

#define IO_USAGE_BUFFER_SIZE (6 * 30)
#define IO_TOP_MAX 5

namespace android {
namespace pixel {
namespace perfstatsd {

class ProcPidIoStats {
  private:
    std::chrono::system_clock::time_point mCheckTime;
    std::vector<uint32_t> mPrevPids;
    std::vector<uint32_t> mCurrPids;
    std::unordered_map<uint32_t, std::string> mUidNameMapping;
    // functions
    std::vector<uint32_t> getNewPids();

  public:
    void update(bool forceAll);
    bool getNameForUid(uint32_t uid, std::string *name);
};

struct UserIo {
    uint32_t uid;
    uint64_t fgRead;
    uint64_t bgRead;
    uint64_t fgWrite;
    uint64_t bgWrite;
    uint64_t fgFsync;
    uint64_t bgFsync;

    UserIo &operator=(const UserIo &other) {
        uid = other.uid;
        fgRead = other.fgRead;
        bgRead = other.bgRead;
        fgWrite = other.fgWrite;
        bgWrite = other.bgWrite;
        fgFsync = other.fgFsync;
        bgFsync = other.bgFsync;
        return *this;
    }

    UserIo operator-(const UserIo &other) const {
        UserIo r;
        r.uid = uid;
        r.fgRead = fgRead - other.fgRead;
        r.bgRead = bgRead - other.bgRead;
        r.fgWrite = fgWrite - other.fgWrite;
        r.bgWrite = bgWrite - other.bgWrite;
        r.fgFsync = fgFsync - other.fgFsync;
        r.bgFsync = bgFsync - other.bgFsync;
        return r;
    }

    UserIo operator+(const UserIo &other) const {
        UserIo r;
        r.uid = uid;
        r.fgRead = fgRead + other.fgRead;
        r.bgRead = bgRead + other.bgRead;
        r.fgWrite = fgWrite + other.fgWrite;
        r.bgWrite = bgWrite + other.bgWrite;
        r.fgFsync = fgFsync + other.fgFsync;
        r.bgFsync = bgFsync + other.bgFsync;
        return r;
    }

    uint64_t sumWrite() { return fgWrite + bgWrite; }

    uint64_t sumRead() { return fgRead + bgRead; }

    void reset() {
        uid = 0;
        fgRead = 0;
        bgRead = 0;
        fgWrite = 0;
        bgWrite = 0;
        fgFsync = 0;
        bgFsync = 0;
    }
};

class ScopeTimer {
  private:
    bool mDisabled;
    std::string mName;
    std::chrono::system_clock::time_point mStart;

  public:
    ScopeTimer() : ScopeTimer("") {}
    ScopeTimer(std::string name) : mDisabled(false), mName(name) {
        mStart = std::chrono::system_clock::now();
    }
    ~ScopeTimer() {
        if (!mDisabled) {
            std::string msg;
            dump(&msg);
            LOG_TO(SYSTEM, INFO) << msg;
        }
    }
    void setEnabled(bool enabled) { mDisabled = !enabled; }
    void dump(std::string *outAppend);
};

constexpr uint64_t IO_USAGE_DUMP_THRESHOLD = 50L * 1000L * 1000L;  // 50MB
class IoStats {
  private:
    uint64_t mMinSizeOfTotalRead = IO_USAGE_DUMP_THRESHOLD;
    uint64_t mMinSizeOfTotalWrite = IO_USAGE_DUMP_THRESHOLD;
    std::chrono::system_clock::time_point mLast;
    std::chrono::system_clock::time_point mNow;
    std::unordered_map<uint32_t, UserIo> mPrevious;
    UserIo mTotal;
    UserIo mWriteTop[IO_TOP_MAX];
    UserIo mReadTop[IO_TOP_MAX];
    std::vector<uint32_t> mUnknownUidList;
    std::unordered_map<uint32_t, std::string> mUidNameMap;
    ProcPidIoStats mProcIoStats;
    // Functions
    std::unordered_map<uint32_t, UserIo> calcIncrement(
        const std::unordered_map<uint32_t, UserIo> &data);
    void updateTopWrite(UserIo usage);
    void updateTopRead(UserIo usage);
    void updateUnknownUidList();

  public:
    IoStats() {
        mNow = std::chrono::system_clock::now();
        mLast = mNow;
    }
    void calcAll(std::unordered_map<uint32_t, UserIo> &&data);
    void setDumpThresholdSizeForRead(uint64_t size) { mMinSizeOfTotalRead = size; }
    void setDumpThresholdSizeForWrite(uint64_t size) { mMinSizeOfTotalWrite = size; }
    bool dump(std::stringstream *output);
};

class IoUsage : public StatsType {
  private:
    bool mDisabled;
    IoStats mStats;

  public:
    IoUsage() : mDisabled(false) {}
    void refresh(void);
    void setOptions(const std::string &key, const std::string &value);
};

}  // namespace perfstatsd
}  // namespace pixel
}  // namespace android

#endif /*  _IO_USAGE_H_ */