// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//--------------------------------------------------------------------------------------
// TelemetryManager.h
//
// Publishing telemetry functionality
//
// Based on GameTelemetry module provided by Advanced Technology Group (ATG)
// Copyright (C) Microsoft Corporation. All rights reserved.
//--------------------------------------------------------------------------------------
#pragma once

#include "CoreMinimal.h"
#include "TelemetryInterfaces.h"
#include "TelemetryBuilder.h"
#include "Containers/CircularQueue.h"
#include "Misc/ConfigCacheIni.h"

// Storage class for telemetry configuration
class SIMPLYGONTELEMETRY_API FTelemetryConfiguration
{
public:
    // URL of the telemetry ingestion server
    FString IngestionUrl;

    // The interval between flushes
    double SendInterval = 180.0;

    // Number of events that can be pending before events are lost
    int32 PendingBufferSize = 128;

public:
    static const FString &GetIniFileName() { return IniFileName; }
    static const FString &GetIniSectionName() { return IniSectionName; }

    static bool GetString(const TCHAR *Key, FString &Value) { return GConfig->GetString(*IniSectionName, Key, Value, IniFileName); }
    static bool GetDouble(const TCHAR *Key, double &Value) { return GConfig->GetDouble(*IniSectionName, Key, Value, IniFileName); }
    static bool GetInt(const TCHAR *Key, int32 &Value) { return GConfig->GetInt(*IniSectionName, Key, Value, IniFileName); }

private:
    static FString IniFileName;
    static FString IniSectionName;
};


class SIMPLYGONTELEMETRY_API FTelemetryManager
{
    // Singleton for common properties
public:
    // Intialization of the singleton fromm the provided configuration
    static void Initialize(const FTelemetryConfiguration &Config);

	//wont initialize any worker and use SimplygonAPI methods instead.
	static void Initialize();

    // Helper function to read configuration values from the ini
    static FTelemetryConfiguration GetConfigFromIni();

    // Set a new client id - Usually set to the platform client id
    // If setting this again after initialization, consider flushing existing buffered telemetry or they may be incorrectly associated with the new id
    void SetClientId(const FString &InClientId);

    // Set a new session id - Usually set when the game would like to differentiate between play sessions (such as changing users)
    // If setting this more than once, consider flushing existing buffered telemetry or they may be incorrectly associated with the new id
    void SetSessionId(const FString &InSessionId);

    // Set a common property which will be included in all telemetry sent
    template<typename T>
    void SetCommonProperty(const FString &Name, const T &Value)
    {
        Instance->CommonProperties.SetProperty(Name, Value);
    }

    // Set a common property which will be included in all telemetry sent
    void SetCommonProperty(const FTelemetryProperty &Property)
    {
        Instance->CommonProperties.SetProperty(Property);
    }

    // Set a common property which will be included in all telemetry sent
    void SetCommonProperty(FTelemetryProperty &&Property)
    {
        Instance->CommonProperties.SetProperty(Property);
    }

    // Set a common property which will be included in all telemetry sent
    const FTelemetryProperties &GetCommonProperties()
    {
        return Instance->CommonProperties.GetProperties();
    }

    // Singleton reference
    // Ensure Initialize is called before this is used
    static FTelemetryManager &Get() { check(Instance.IsValid()) return *Instance; }

    // Get the currently set client id
    static FString GetClientId() { return Instance->CommonProperties["client_id"].GetValue<FString>(); }

    // Get the currently set session id
    static FString GetSessionId() { return Instance->CommonProperties["session_id"].GetValue<FString>(); }

    /**
        Records an event and places it in the buffer to be sent
        @param Name: Event name
        @param Category: Category
        @param PropertiesBuilder: Properties to set in this event
    */
    void Record(const FString &Name, const FString &Category, FTelemetryBuilder &&PropertiesBuilder);


    // Flushes any pending telemetry and shuts down the singleton
    void Shutdown();


	void SetupCommonProperties();

    ~FTelemetryManager() {};

private:
    FTelemetryManager(FTelemetryConfiguration Configuration) : CommonProperties(), Configuration(Configuration) {}
	FTelemetryManager() : CommonProperties(), Configuration() {}

	/**
	 * Returns a unique id string(GUID) per login account of the current machine.
	 * Implemented using persistent storage (i.e HKCU), so
	 * is susceptible to anything that could reset or revert that storage if the ID is created,
	 * which is generally during install or first run of the app.
	 * Based on the LoginId implemention in the Engine. However this is completely controlled by US. 
	 *
	 * Note: This is NOT a user or machine fingerprint, as multiple logins on the same machine will
	 * not share the same ID, and it is not based on the hardware of the user. It is completely random however linkable.
	 *
	 * @return a string containing the LoginID, or empty if not available.
	 */
	FString GetUniqueId();

private:
    // Singleton instance
    static TUniquePtr<FTelemetryManager> Instance;

private:
    // Common properties which are sent with all events
    FTelemetryBuilder CommonProperties;
    FTelemetryConfiguration Configuration;
};