// SPDX-FileCopyrightText: 2020-2026 Sven Breuner and elbencho contributors
// SPDX-License-Identifier: GPL-3.0-only

#ifndef STATISTICS_H_
#define STATISTICS_H_

#include <ftxui/component/component.hpp>
#include <ftxui/component/event.hpp>
#include <ftxui/component/loop.hpp>
#include <ftxui/component/screen_interactive.hpp>
#include <ftxui/dom/elements.hpp>
#include <ftxui/dom/table.hpp>
#include <functional>
#include <vector>

#include "CPUUtil.h"
#include "Common.h"
#include "LiveLatency.h"
#include "ProgArgs.h"
#include "toolkits/TranslatorTk.h"
#include "workers/WorkerManager.h"
#include "workers/WorkersSharedData.h"


class PhaseResults
{
	public:
		uint64_t firstFinishUSec; // stonewall: time to completion of fastest worker
		uint64_t lastFinishUSec; // time to completion of slowest worker

		LiveOps opsTotal; // processed by all workers
		LiveOps opsTotalReadMix; // rwmix read processed by all workers
		LiveOps opsStoneWallTotal; // processed by all workers when stonewall was hit
		LiveOps opsStoneWallTotalReadMix; // rwmix read proc'ed by all workers when stonewall hit

		LiveOps opsPerSec; // total per sec for all workers by last finisher
		LiveOps opsPerSecReadMix; // rwmix read total per sec for all workers by last finisher
		LiveOps opsStoneWallPerSec; // total per sec for all workers by 1st finisher
		LiveOps opsStoneWallPerSecReadMix; // rwmix read total per sec for all workers by 1st fin

		float cpuUtilPercent; // cpu utilization until last finisher
		float cpuUtilStoneWallPercent; // cpu utilization until first finisher

		LatencyHistogram iopsLatHisto; // sum of all histograms
		LatencyHistogram iopsLatHistoReadMix; // rwmix read sum of all histograms
		LatencyHistogram entriesLatHisto; // sum of all histograms
		LatencyHistogram entriesLatHistoReadMix; // rwmix read sum of all histograms
};

/**
 * Phase values for live stats
 */
class LiveResults
{
	public:
        LiveResults(const ProgArgs& progArgs, const WorkerManager& workerManager,
            const WorkersSharedData& workersSharedData)
        {
            this->phaseName = TranslatorTk::benchPhaseToPhaseName(
                workersSharedData.currentBenchPhase, &progArgs);
            this->phaseEntryType = TranslatorTk::benchPhaseToPhaseEntryType(
                workersSharedData.currentBenchPhase, &progArgs);
            this->entryTypeUpperCase = TranslatorTk::benchPhaseToPhaseEntryType(
                workersSharedData.currentBenchPhase, &progArgs, true);

            workerManager.getPhaseNumEntriesAndBytes(
                this->numEntriesPerWorker, this->numBytesPerWorker);
        }

		std::string phaseName; // read, write, mkdirs etc
		std::string phaseEntryType; // files or dirs
		std::string entryTypeUpperCase; // phaseEntryType uppercase
		size_t numEntriesPerWorker; // total number for this phase
		uint64_t numBytesPerWorker; // total number for this phase

		size_t numWorkersDone;
		size_t numRemoteThreadsLeft; // only set in master mode
		size_t percentRemoteCPU; // avg percent cpu util of service hosts (only set in master mode)

		int winHeight; // current height of terminal window (set in fullscreen mode only)
		int winWidth; // current width of terminal window (set in fullscreen mode only)

		LiveOps lastLiveOps = {}; // live ops from last round for per-sec diff
		LiveOps lastLiveOpsReadMix = {}; // live ops from last round for per-sec diff
		LiveOps newLiveOps; // live ops from current round
		LiveOps newLiveOpsReadMix; // live ops from current round
		LiveOps liveOpsPerSec; // live ops per sec from diff of new and last live ops
		LiveOps liveOpsPerSecReadMix; // live ops per sec from diff of new and last live ops
		size_t percentDone; // total percent done based on bytes (if any) or num entries in phase
		size_t percentDoneReadMix; // total percent done based on bytes (if any) or entries in phase

		LiveLatency liveLatency; // avg latency across all workers for current live stats interval
};

/**
 * Rendering state info for ftxui fullscreen live stats GUI.
 */
class LiveStatsUIState
{
    public:
        bool switchToSingleLine = false; // true if user wants to continue without fullscreen
        int selectedRow = 1;
        int selectedColumn = 0;
        std::vector<StringVec> headerStatsTxtTable; // all live stats values as plain text
        std::vector<StringVec> workerStatsTxtTable; // all live stats values as plain text
        ftxui::Element cachedView; // to prevent expensive UI redraw for quick key press check
};


class Statistics
{
	public:
		Statistics(const ProgArgs& progArgs, WorkerManager& workerManager) :
			progArgs(progArgs), workerManager(workerManager),
			workersSharedData(workerManager.getWorkersSharedData() ),
			workerVec(workerManager.getWorkerVec() ) {}
		~Statistics();

		void printLiveCountdown();
		void printLiveStats();

		void printPhaseResultsTableHeader();
		void printPhaseResults();

		void getLiveOps(LiveOps& outLiveOps, LiveOps& outLiveRWMixReadOps,
			LiveLatency& outLiveLatency);
		void getLiveStatsAsPropertyTreeForService(bpt::ptree& outTree);
		void getBenchResultAsPropertyTreeForService(bpt::ptree& outTree);

		void printDryRunInfo();

	private:
		const ProgArgs& progArgs;
		WorkerManager& workerManager;
		WorkersSharedData& workersSharedData;
		WorkerVec& workerVec;
		bool consoleBufferingDisabled{false};
		const std::string phaseResultsFormatStr{"%|-11| %|-17|%|1| %|11| %|11|"}; // note line below
		const std::string phaseResultsLeftFormatStr{"%|-11| %|-17|%|1| "}; // left side format str
		const std::string phaseResultsFooterStr = std::string(3, '-');
		CPUUtil liveCpuUtil; // updated by live stats loop or through http service live stat calls
		int liveCSVFileFD = -1; // fd for live stats csv file

		void disableConsoleBuffering();
		void resetConsoleBuffering();

		void printPhaseStartISODateToStringVec(StringVec& outLabelsVec, StringVec& outResultsVec);
		void printPhaseResultsTableHeaderToStream(std::ostream& outStream);
		bool generatePhaseResults(PhaseResults& phaseResults);
		void printPhaseResultsToStream(const PhaseResults& phaseResults, std::ostream& outStream);
		void printPhaseResultsToStringVec(const PhaseResults& phaseResults, StringVec& outLabelsVec,
			StringVec& outResultsVec);
		void printPhaseResultsLatencyToStream(const LatencyHistogram& latHisto,
			std::string latTypeStr, std::ostream& outStream);
		void printPhaseResultsLatencyToStringVec(const LatencyHistogram& latHisto,
			std::string latTypeStr, StringVec& outLabelsVec, StringVec& outResultsVec);
		void printPhaseResultsAsJSON(const PhaseResults& phaseResults);

		void printLiveCountdownLine(unsigned long long waittimeSec);

		void printSingleLineLiveStatsLine(LiveResults& liveResults);
        void loopSingleLineLiveStats(LiveResults* customLiveResults = NULL,
            std::chrono::steady_clock::time_point* customLastRefreshT = NULL,
            std::chrono::steady_clock::time_point* customNextRefreshT = NULL);

		void printFullScreenLiveStatsGlobalInfo(const LiveResults& liveResults,
			std::vector<StringVec>& outHeaderStatsTxtTable);
		void printFullScreenLiveStatsWorkerTable(const LiveResults& liveResults,
            std::vector<StringVec>& statsTableData);
        std::string printFullScreenLiveStatsFooter();
        std::function<ftxui::Element()> fullscreenGenerateFtxuiRenderer(
            LiveResults& liveResults, LiveStatsUIState& uiState);
        std::function<bool(ftxui::Event event)> fullscreenGenerateFtxuiEventHandler(
            LiveStatsUIState& uiState);
		void loopFullScreenLiveStats();

		void loopNoConsoleLiveStats();

		void updateLiveStatsRemoteInfo(LiveResults& liveResults);
		void updateLiveStatsLiveOps(LiveResults& liveResults, size_t elapsedMS);

		void printDryRunPhaseInfo(BenchPhase benchPhase);
		void printDryRunInfoNetBench();

		void prepLiveCSVFile();
		void printLiveStatsCSV(const LiveResults& liveResults);

        bool checkIfVec2DLeftElemEquals(std::vector<StringVec>& vec, int row, int column,
            const char* value);

	// inliners
	public:
		/**
		 * To be called by HTTP service on start of a benchmark phase.
		 */
		void updateLiveCPUUtil()
		{
			liveCpuUtil.update();
		}

};

#endif /* STATISTICS_H_ */
