/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/

#if !defined(AZ_RELEASE_BUILD) 
#include "ThermalInfoAndroid.h"
#include <AzCore/std/string/string.h>
#include <AzFramework/StringFunc/StringFunc.h>

#include <cstdio>
#include <sys/types.h>
#include <dirent.h>

ThermalInfoAndroidHandler::ThermalInfoAndroidHandler()
{
    static_assert(AZ_ARRAY_SIZE(m_temperatureFiles) == static_cast<int>(ThermalSensorType::Count), "Thermal count does not match temperature array size");
    ThermalInfoRequestsBus::Handler::BusConnect();
    memset(m_temperatureFiles, 0, sizeof(m_temperatureFiles));

    const int sensorCount = static_cast<int>(ThermalSensorType::Count);
    const char* sensorTypes[sensorCount] = { "cpu", "gpu", "battery" };
    const int maxStringLen = 128;
    char tempString[maxStringLen];
    const char* thermalPath = "/sys/class/thermal";
    // List the elements from the thermal folder to get the thermal_zones available on the device
    DIR* directory = opendir(thermalPath);
    if (directory)
    {
        struct dirent* item;
        const char* thermalPrefix = "thermal_zone";
        // List all items of the directory and find the one that start with thermal_zone (thermal_zone0, thermal_zone1, etc)
        while ((item = readdir(directory)) != nullptr)
        {
            if (strncmp(item->d_name, thermalPrefix, strlen(thermalPrefix)) == 0)
            {
                // Try to deduce the type of sensor. For this we read the "type" file of the thermal zone.
                // This "type" is a string set by the manufacturer, so it can be anything.
                AZStd::string path = AZStd::string::format("%s/%s/type", thermalPath, item->d_name);
                FILE* sensorTypeFile = fopen(path.c_str(), "r");
                if (sensorTypeFile)
                {
                    if (fscanf(sensorTypeFile, "%s", tempString))
                    {
                        for (int i = 0; i < sensorCount; ++i)
                        {
                            if (m_temperatureFiles[i])
                            {
                                continue;
                            }

                            size_t foundPos = AzFramework::StringFunc::Find(tempString, sensorTypes[i]);
                            if (foundPos != AZStd::string::npos)
                            {
                                path = AZStd::string::format("%s/%s/temp", thermalPath, item->d_name);
                                m_temperatureFiles[i] = fopen(path.c_str(), "r");
                                break;
                            }
                        }
                    }
                    fclose(sensorTypeFile);
                }
            }
        }
        closedir(directory);
    }

    int cpuSensorIndex = static_cast<int>(ThermalSensorType::CPU);
    if (!m_temperatureFiles[cpuSensorIndex])
    {
        // If we didn't find the CPU sensor just assume it's the first one.
        AZStd::string path = AZStd::string::format("%s/thermal_zone0/temp", thermalPath);
        m_temperatureFiles[cpuSensorIndex] = fopen(path.c_str(), "r");
    }
}

ThermalInfoAndroidHandler::~ThermalInfoAndroidHandler()
{
    ThermalInfoRequestsBus::Handler::BusDisconnect();
    for (int i = 0; i < static_cast<int>(ThermalSensorType::Count); ++i)
    {
        if (m_temperatureFiles[i])
        {
            fclose(m_temperatureFiles[i]);
        }
    }
}

float ThermalInfoAndroidHandler::GetSensorTemp(ThermalSensorType sensor)
{    
    FILE* tempFile = m_temperatureFiles[static_cast<int>(sensor)];
    if (!tempFile)
    {
        return 0.f;
    }

    fseek(tempFile, 0, SEEK_SET);
    float temperature = 0.f;
    fscanf(tempFile, "%f", &temperature);
    temperature /= 1000.0f;
    return temperature;
}

float ThermalInfoAndroidHandler::GetSensorOverheatingTemp(ThermalSensorType sensor)
{
    const int overheatingTemperatures[static_cast<int>(ThermalSensorType::Count)] = 
    {
        70, // CPU
        70, // GPU
        40  // Battery
    };

    return overheatingTemperatures[static_cast<int>(sensor)];
}

#endif // !defined(AZ_RELEASE_BUILD)