/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * 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.
 */
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <thread>
#include "include/sp_utils.h"
#include "include/ByTrace.h"
#include "include/Capture.h"
#include "include/FPS.h"
#include "include/sp_log.h"
namespace OHOS {
namespace SmartPerf {
std::map<std::string, std::string> FPS::ItemData()
{
    std::map<std::string, std::string> result;
    FpsInfo fpsInfoResult;
    if (surfaceViewName.length() > 0) {
        fpsInfoResult = GetDiffLayersFpsInfo(surfaceViewName);
    } else {
        fpsInfoResult = GetFpsInfo();
    }
    result["fps"] = std::to_string(fpsInfoResult.fps);
    LOGI("result.fps====: %s", std::to_string(fpsInfoResult.fps).c_str());
    std::string jitterStr = "";
    std::string split = "";
    for (size_t i = 0; i < fpsInfoResult.jitters.size(); i++) {
        if (i > 0) {
            split = ";;";
        }
        jitterStr += split + std::to_string(fpsInfoResult.jitters[i]);
    }
    result["fpsJitters"] = jitterStr;
    LOGI("result.jitters====: %s", jitterStr.c_str());
    if (isCatchTrace > 0) {
        ByTrace::GetInstance().CheckFpsJitters(fpsInfoResult.jitters, fpsInfoResult.fps);
    }
    if (isCapture > 0) {
        Capture::GetInstance().TriggerGetCatch(SPUtils::GetCurTime());
    }
    return result;
}

void FPS::SetTraceCatch()
{
    isCatchTrace = 1;
}

void FPS::SetCaptureOn()
{
    isCapture = 1;
}

void FPS::SetPackageName(std::string pName)
{
    pkgName = std::move(pName);
}
void FPS::SetLayerName(std::string sName)
{
    surfaceViewName = std::move(sName);
}
FpsInfo FPS::GetDiffLayersFpsInfo(std::string sName)
{
    FpsInfo surfaceFramefpsInfo = GetSurfaceFrame(sName);
    return surfaceFramefpsInfo;
}
std::string FPS::GetSurface()
{
    std::string cmdResult;
    std::string cmdString1 = "hidumper -s 10 -a sur";
    std::string cmdString2 = "face | grep sur";
    std::string cmdString3 = "face";
    SPUtils::LoadCmd(cmdString1 + cmdString2 + cmdString3, cmdResult);
    size_t position1 = cmdResult.find("[");
    size_t position2 = cmdResult.find("]");
    LOGI("cmdResult==: %s", (cmdResult.substr(position1 + 1, position2 - position1 - 1)).c_str());
    return cmdResult.substr(position1 + 1, position2 - position1 - 1);
}
static void PrintFpsInfo(FpsInfo &fpsInfo, const std::string tag, const std::string type)
{
    LOGI("Print fps: %s", std::to_string(fpsInfo.fps).c_str());
    LOGI("Print tag: %s", tag.c_str());
    LOGI("Print type: %s", type.c_str());
    std::string jitterStr = "";
    std::string split = "";
    for (size_t i = 0; i < fpsInfo.jitters.size(); i++) {
        if (i > 0) {
            split = ";;";
        }
        jitterStr += split + std::to_string(fpsInfo.jitters[i]);
    }
    LOGI("Print jitterStr: %s", jitterStr.c_str());
}
std::string FPS::CutLayerName(std::string layerName)
{
    std::string subLayerName;
    size_t twenty = 20;
    if (layerName.size() > twenty) {
        subLayerName = layerName.substr(0, twenty);
    } else {
        subLayerName = layerName;
    }
    return subLayerName;
}

FpsInfo FPS::GetFpsInfoMax()
{
    int fpsValue = 0;
    if (fpsInfo.fps > uniteFpsInfo.fps) {
        fpsInfoMax = fpsInfo;
    } else {
        fpsInfoMax = uniteFpsInfo;
    }
    if (fpsInfoMax.fps < fpsValue) {
        fpsInfoMax.fps = fpsValue;
    }
    if (fpsInfoMax == prevFlagFpsInfo) {
        LOGI("fpsInfoMax == prevFlagFpsInfo");
        if (cntFpsInfo == lastFlagFpsInfo) {
            LOGI("cntFpsInfo == lastFlagFpsInfo");
            fpsInfoMax.fps = 0;
            fpsInfoMax.Clear();
            return fpsInfoMax;
        } else {
            LOGI("cntFpsInfo != lastFlagFpsInfo");
            if ((cntFpsInfo.jitters.size() > 0 && lastFlagFpsInfo.jitters.size() > 0) &&
                cntFpsInfo.jitters[0] == lastFlagFpsInfo.jitters[0]) {
                LOGI("cntFpsInfo.jitters.size: %s", std::to_string(cntFpsInfo.jitters.size()).c_str());
                LOGI("lastFlagFpsInfo.jitters.size: %s", std::to_string(lastFlagFpsInfo.jitters.size()).c_str());
                LOGI("cntFpsInfo.jitters[0]: %s", std::to_string(cntFpsInfo.jitters[0]).c_str());
                LOGI("lastFlagFpsInfo.jitters[0]: %s", std::to_string(lastFlagFpsInfo.jitters[0]).c_str());
                cntFpsInfo.fps = 0;
                cntFpsInfo.Clear();
            } else {
                LOGI("6666666666666666666");
                lastFlagFpsInfo = cntFpsInfo;
            }
            return cntFpsInfo;
        }
    } else {
        LOGI("fpsInfoMax != prevFlagFpsInfo");
        if ((fpsInfoMax.jitters.size() > 0 && lastFlagFpsInfo.jitters.size() > 0) &&
            fpsInfoMax.jitters[0] == lastFlagFpsInfo.jitters[0]) {
            LOGI("fpsInfoMax == lastFlagFpsInfo");
            lastFlagFpsInfo = cntFpsInfo;
            return cntFpsInfo;
        } else {
            LOGI("fpsInfoMax != lastFlagFpsInfo");
            prevFlagFpsInfo = fpsInfoMax;
            return fpsInfoMax;
        }
    }
}

FpsInfo FPS::GetFpsInfo()
{
    fpsInfoMax.fps = 0;
    if (pkgName.empty()) {
        return fpsInfoMax;
    }
    std::vector<std::string> sps;
    SPUtils::StrSplit(this->pkgName, ".", sps);
    std::string layerName = std::string(sps[sps.size() - 1].c_str());
    LOGI("layerName===: %s", layerName.c_str());
    std::string uniteLayer = "DisplayNode";
    uniteLayer = GetSurface();
    LOGI("uniteLayer===: %s", uniteLayer.c_str());
    std::string line = GetLayer(layerName);
    LOGI("line===: %s", line.c_str());
    std::vector<std::string> params;
    SPUtils::StrSplit(line, ":", params);
    std::string pkgZOrd = params[1];
    LOGI("pkgZOrd===: %s", pkgZOrd.c_str());
    std::string zOrd = "-1";
    std::string focusSurface = params[0];
    LOGI("focusSurface===: %s", focusSurface.c_str());
    std::string subStrLayerName = CutLayerName(layerName);
    LOGI("subStrLayerName===: %s", subStrLayerName.c_str());
    if ((focusSurface.find(subStrLayerName) != std::string::npos) && (strcmp(pkgZOrd.c_str(), zOrd.c_str()) != 0)) {
        uniteFpsInfo = GetSurfaceFrame(uniteLayer);
        PrintFpsInfo(uniteFpsInfo, uniteLayer, "one");
    }
    fpsInfo = GetSurfaceFrame(focusSurface);
    PrintFpsInfo(fpsInfo, focusSurface, "two");
    return GetFpsInfoMax();
}
FpsInfo FPS::GetFpsInfoResult(FpsInfo &fpsInfo, long long &lastLineTime)
{
    const int maxZeroNum = 266;
    if (zeroNum >= maxZeroNum) {
        LOGI("zeroNum====: %s", std::to_string(zeroNum).c_str());
        while (!(fpsInfo.timeStampQ.empty())) {
            fpsInfo.timeStampQ.pop();
        }
        fpsInfo.fps = 0;
        fpsInfo.jitters.clear();
        LOGI("fpsInfo.fps0: %s", std::to_string(fpsInfo.fps).c_str());
        return fpsInfo;
    }
    const int minPrintLine = 5;
    if (cnt < minPrintLine) {
        fpsInfo.fps = fpsInfo.preFps;
        LOGI("fpsInfo.fps1: %s", std::to_string(fpsInfo.fps).c_str());
        return fpsInfo;
    }
    if (!fpsInfo.timeStampQ.empty() && fpsInfo.timeStampQ.back() == lastLineTime) {
        fpsInfo.fps = fpsGb;
        if (fpsGb == 0) {
            fpsInfo.jitters.clear();
        }
        LOGI("fpsInfo.fps2: %s", std::to_string(fpsInfo.fps).c_str());
        LOGI("lastLineTime: %s", std::to_string(lastLineTime).c_str());
        return fpsInfo;
    }
    if (fpsGb > 0) {
        fpsInfo.fps = fpsGb;
        fpsInfo.preFps = fpsGb;
        LOGI("fpsInfo.fps3: %s", std::to_string(fpsInfo.fps).c_str());
        LOGI("fpsInfo.preFps3: %s", std::to_string(fpsInfo.preFps).c_str());
        return fpsInfo;
    } else if (refresh && !jump) {
        fpsInfo.fps = fpsInfo.preFps;
        LOGI("fpsInfo.fps4: %s", std::to_string(fpsInfo.fps).c_str());
        return fpsInfo;
    } else {
        fpsInfo.fps = 0;
        fpsInfo.jitters.clear();
        LOGI("fpsInfo.fps5: %s", std::to_string(fpsInfo.fps).c_str());
        return fpsInfo;
    }
}

void FPS::GetLastFpsInfo(FpsInfo &fpsInfo)
{
    int total = 266;
    if (cnt == total && fpsGb != 0) {
        LOGI("cnt == total && fpsGb != 0");
        lastReadyTime = frameReadyTime;
        int fpsTmp = 0;
        cntFpsInfo.jitters.clear();
        while (!(fpsInfo.timeStampQ).empty()) {
            fpsTmp++;
            long long currFrame = (fpsInfo.timeStampQ.front());
            if (lastFrame != -1) {
                long long jitter = currFrame - lastFrame;
                cntFpsInfo.jitters.push_back(jitter);
            } else {
                long long jitter = currFrame - currFrame / mod * mod;
                cntFpsInfo.jitters.push_back(jitter);
            }
            lastFrame = currFrame;
            (fpsInfo.timeStampQ).pop();
        }
        cntFpsInfo.fps = fpsTmp;
        LOGI("cntFpsInfo.fps====: %s", std::to_string(cntFpsInfo.fps).c_str());
    }
}

void FPS::GetPrevFpsInfo(FpsInfo &fpsInfo)
{
    refresh = true;
    long long tFrameReadyTime = frameReadyTime / mod;
    long long tLastReadyTime = lastReadyTime / mod;
    lastFrame = -1;
    if (tFrameReadyTime == tLastReadyTime) {
        (fpsInfo.timeStampQ).push(frameReadyTime);
    } else if (tFrameReadyTime == tLastReadyTime + 1) {
        jump = true;
        lastReadyTime = frameReadyTime;
        int fpsTmp = 0;
        fpsInfo.jitters.clear();
        while (!(fpsInfo.timeStampQ).empty()) {
            fpsTmp++;
            long long currFrame = (fpsInfo.timeStampQ.front());
            if (lastFrame != -1) {
                long long jitter = currFrame - lastFrame;
                fpsInfo.jitters.push_back(jitter);
            } else {
                long long jitter = currFrame - currFrame / mod * mod;
                fpsInfo.jitters.push_back(jitter);
            }
            lastFrame = currFrame;
            (fpsInfo.timeStampQ).pop();
        }
        fpsGb = fpsTmp;
        LOGI("fpsGb====: %s", std::to_string(fpsGb).c_str());
        (fpsInfo.timeStampQ).push(frameReadyTime);
        fpsInfo.lastFrameReadyTime = lastFrame;
    } else if (tFrameReadyTime > tLastReadyTime + 1) {
        jump = true;
        lastReadyTime = frameReadyTime;
        while (!(fpsInfo.timeStampQ).empty()) {
            (fpsInfo.timeStampQ).pop();
        }
        (fpsInfo.timeStampQ).push(frameReadyTime);
    }
}
void FPS::InitParams(FpsInfo &fpsInfo, long long &lastLineTime)
{
    lastReadyTime = -1;
    fpsGb = 0;
    if (!(fpsInfo.timeStampQ).empty()) {
        lastReadyTime = (fpsInfo.timeStampQ).back();
        lastLineTime = (fpsInfo.timeStampQ).back();
    }
    jump = false;
    refresh = false;
    cnt = 0;
    zeroNum = 0;
}

FpsInfo FPS::GetSurfaceFrame(std::string name)
{
    if (name == "") {
        return FpsInfo();
    }
    static std::map<std::string, FpsInfo> fpsMap;
    if (fpsMap.count(name) == 0) {
        FpsInfo tmp;
        tmp.fps = 0;
        tmp.preFps = 0;
        fpsMap[name] = tmp;
    }
    fpsInfo = fpsMap[name];
    fpsInfo.fps = 0;
    FILE *fp;
    static char tmp[1024];
    std::string cmd = "hidumper -s 10 -a \"fps " + name + "\"";
    LOGI("cmd=====: %s", cmd.c_str());
    fp = popen(cmd.c_str(), "r");
    if (fp == nullptr) {
        return fpsInfo;
    }
    static long long lastLineTime;
    InitParams(fpsInfo, lastLineTime);
    LOGI("dump time: start!");
    while (fgets(tmp, sizeof(tmp), fp) != nullptr) {
        std::string str(tmp);
        LOGD("dump time: %s", str.c_str());
        frameReadyTime = 0;
        std::stringstream sstream;
        sstream << tmp;
        sstream >> frameReadyTime;
        cnt++;
        if (frameReadyTime == 0) {
            zeroNum++;
            continue;
        }
        if (lastReadyTime >= frameReadyTime) {
            lastReadyTime = -1;
            continue;
        }
        GetPrevFpsInfo(fpsInfo);
        GetLastFpsInfo(fpsInfo);
    }
    pclose(fp);
    return GetFpsInfoResult(fpsInfo, lastLineTime);
}

std::string FPS::GetLayer(std::string pkgSurface)
{
    std::vector<DumpEntity> dumpEntityList;
    std::string curFocusId = "-1";
    const std::string cmd = "hidumper -s WindowManagerService -a -a";
    FILE *fd = popen(cmd.c_str(), "r");
    if (fd != nullptr) {
        int lineNum = 0;
        char buf[1024] = {'\0'};
        const int paramFourteen = 14;
        const int paramTwentyFour = 24;
        const int paramThree = 3;
        const int windowNameIndex = 0;
        const int windowIdIndex = 3;
        const int focusNameIndex = 2;
        while ((fgets(buf, sizeof(buf), fd)) != nullptr) {
            std::string line = buf;
            LOGE("hidumperline: %s", line.c_str());
            if (line[0] == '-' || line[0] == ' ') {
                continue;
            }
            std::vector<std::string> params;
            SPUtils::StrSplit(line, " ", params);
            if (params[windowNameIndex].find("WindowName") != std::string::npos &&
                params[windowIdIndex].find("WinId") != std::string::npos) {
                continue;
            }
            if (params.size() > paramFourteen) {
                DumpEntity dumpEntity { params[0], params[1], params[2], params[3], params[7]};
                dumpEntityList.push_back(dumpEntity);
            }
            if (params.size() == paramFourteen || params.size() == paramTwentyFour) {
                DumpEntity dumpEntity { params[0], params[2], params[2], params[3], params[6]};
                dumpEntityList.push_back(dumpEntity);
            }
            if (params.size() == paramThree) {
                curFocusId = params[focusNameIndex];
                break;
            }
            lineNum++;
        }
        pclose(fd);
    }

    std::string focusWindowName = "NA";
    std::string pkgZOrd = "-1";
    std::string spSurfacePrefix = "sp_";
    std::string floatWindow = "floatWindow";
    int curId = std::stoi(curFocusId);
    LOGE("getLayerCurId====: %s", std::to_string(curId).c_str());
    for (size_t i = 0; i < dumpEntityList.size(); i++) {
        DumpEntity dumpItem = dumpEntityList[i];
        int curWinId = std::stoi(dumpItem.windId);
        if (curId == curWinId) {
            LOGE("curId == curWinId");
            if ((dumpItem.windowName.find(spSurfacePrefix) != std::string::npos ||
                dumpItem.windowName.find(floatWindow) != std::string::npos) && dumpItem.zOrd != "-1") {
                continue;
            }
            focusWindowName = dumpItem.windowName;
            LOGE("focusWindowName: %s", focusWindowName.c_str());
            LOGE("dumpItem.windowName: %s", dumpItem.windowName.c_str());
        }
        if (dumpItem.windowName.find(pkgSurface) != std::string::npos && dumpItem.zOrd != "-1") {
            focusWindowName = dumpItem.windowName;
            pkgZOrd = dumpItem.zOrd;
            LOGE("pkgZOrd: %s", pkgZOrd.c_str());
            LOGE("dumpItem.zOrd: %s", dumpItem.zOrd.c_str());
            LOGE("focusWindowName2: %s", focusWindowName.c_str());
            LOGE("dumpItem.windowName2: %s", dumpItem.windowName.c_str());
        }
    }
    return focusWindowName + ":" + pkgZOrd;
}
}
}