/*
 * Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */
 
#pragma once
#include <switch.h>
#include <inttypes.h>
#include <string.h>
// Battery charging flags
typedef enum {
    BatteryFlag_NoHub  = BIT(0),  // Hub is disconnected
    BatteryFlag_Rail   = BIT(8),  // At least one Joy-con is charging from rail
    BatteryFlag_SPDSRC = BIT(12), // OTG
    BatteryFlag_ACC    = BIT(16)  // Accessory
} BatteryChargeFlags;

// Power Delivery Controller State (BM92T series)
typedef enum {
    PDState_NewPDO      = 1, // Received new Power Data Object
    PDState_NoPD        = 2, // No Power Delivery source is detected
    PDState_AcceptedRDO = 3  // Received and accepted Request Data Object
} BatteryPDControllerState;

// Charger type detection
typedef enum {
    ChargerType_None         = 0,
    ChargerType_PD           = 1,
    ChargerType_TypeC_1500mA = 2,
    ChargerType_TypeC_3000mA = 3,
    ChargerType_DCP          = 4, // Dedicated Charging Port
    ChargerType_CDP          = 5, // Charging Downstream Port
    ChargerType_SDP          = 6, // Standard Downstream Port
    ChargerType_Apple_500mA  = 7,
    ChargerType_Apple_1000mA = 8,
    ChargerType_Apple_2000mA = 9
} BatteryChargerType;

// Power role (USB Power Delivery)
typedef enum {
    PowerRole_Sink   = 1, // Device is receiving power
    PowerRole_Source = 2  // Device is providing power
} BatteryPowerRole;

// Complete battery charge information structure
typedef struct {
    int32_t InputCurrentLimit;       // Input (Sink) current limit in mA
    int32_t VBUSCurrentLimit;        // Output (Source/VBUS/OTG) current limit in mA
    int32_t ChargeCurrentLimit;      // Battery charging current limit in mA
    int32_t ChargeVoltageLimit;      // Battery charging voltage limit in mV
    int32_t unk_x10;                 // Unknown field (possibly enum)
    int32_t unk_x14;                 // Unknown field (possibly flags)
    BatteryPDControllerState PDControllerState; // PD Controller State
    int32_t BatteryTemperature;      // Battery temperature in milli-Celsius
    int32_t RawBatteryCharge;        // Battery charge in per cent-mille (100% = 100000)
    int32_t VoltageAvg;              // Average voltage in mV
    int32_t BatteryAge;              // Battery health (capacity full/design) in pcm
    BatteryPowerRole PowerRole;      // Current power role
    BatteryChargerType ChargerType;  // Type of charger connected
    int32_t ChargerVoltageLimit;     // Charger voltage limit in mV
    int32_t ChargerCurrentLimit;     // Charger current limit in mA
    BatteryChargeFlags Flags;        // Various status flags
} BatteryChargeInfo;

// Helper macro to check if battery charging is enabled
#define IS_BATTERY_CHARGING_ENABLED(info) (((info)->unk_x14 >> 8) & 1)

// Initialize the battery info driver
Result batteryInfoInitialize(void);

// Cleanup the battery info driver
void batteryInfoExit(void);

// Get complete battery charge information
Result batteryInfoGetChargeInfo(BatteryChargeInfo *out);

// Get battery charge percentage (0-100)
Result batteryInfoGetChargePercentage(u32 *out);

// Check if enough power is being supplied
Result batteryInfoIsEnoughPowerSupplied(bool *out);

// Battery charge control functions
Result batteryInfoEnableCharging(void);
Result batteryInfoDisableCharging(void);
Result batteryInfoEnableFastCharging(void);
Result batteryInfoDisableFastCharging(void);

// Helper functions to get human-readable strings
const char* batteryInfoGetChargerTypeString(BatteryChargerType type);
const char* batteryInfoGetPowerRoleString(BatteryPowerRole role);
const char* batteryInfoGetPDStateString(BatteryPDControllerState state);

// Convenience functions for common values
static inline int batteryInfoGetTemperatureMiliCelsius(BatteryChargeInfo *info) {
    return info->BatteryTemperature;
}

static inline float batteryInfoGetChargePercent(BatteryChargeInfo *info) {
    return (float)info->RawBatteryCharge / 1000.0f;
}

static inline float batteryInfoGetBatteryHealthPercent(BatteryChargeInfo *info) {
    return (float)info->BatteryAge / 1000.0f;
}

static inline bool batteryInfoIsCharging(BatteryChargeInfo *info) {
    return IS_BATTERY_CHARGING_ENABLED(info);
}

// String lookup tables
static const char* s_chargerTypeStrings[] = {
    "None",
    "Power Delivery",
    "USB-C @ 1.5A",
    "USB-C @ 3.0A",
    "USB-DCP",
    "USB-CDP",
    "USB-SDP",
    "Apple @ 0.5A",
    "Apple @ 1.0A",
    "Apple @ 2.0A",
};

static const char* s_powerRoleStrings[] = {
    "Unknown",
    "Sink",
    "Source",
};

static const char* s_pdStateStrings[] = {
    "Unknown",
    "New PDO Received",
    "No PD Source",
    "RDO Accepted"
};

// Internal PSM service handle
static Service g_psmService = {0};
static bool g_batteryInfoInitialized = false;

// Internal PSM command implementations
static Result psmGetBatteryChargeInfoFields(BatteryChargeInfo *out) {
    if (!g_batteryInfoInitialized)
        return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
    
    return serviceDispatchOut(&g_psmService, 17, *out);
}

static Result psmEnableBatteryCharging_internal(void) {
    if (!g_batteryInfoInitialized)
        return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
    
    return serviceDispatch(&g_psmService, 2);
}

static Result psmDisableBatteryCharging_internal(void) {
    if (!g_batteryInfoInitialized)
        return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
    
    return serviceDispatch(&g_psmService, 3);
}

static Result psmEnableFastBatteryCharging_internal(void) {
    if (!g_batteryInfoInitialized)
        return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
    
    return serviceDispatch(&g_psmService, 10);
}

static Result psmDisableFastBatteryCharging_internal(void) {
    if (!g_batteryInfoInitialized)
        return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
    
    return serviceDispatch(&g_psmService, 11);
}

// Public API implementations
Result batteryInfoInitialize(void) {
    if (g_batteryInfoInitialized)
        return 0;
    
    Result rc = psmInitialize();
    if (R_SUCCEEDED(rc)) {
        memcpy(&g_psmService, psmGetServiceSession(), sizeof(Service));
        g_batteryInfoInitialized = true;
    }
    
    return rc;
}

void batteryInfoExit(void) {
    if (g_batteryInfoInitialized) {
        psmExit();
        memset(&g_psmService, 0, sizeof(Service));
        g_batteryInfoInitialized = false;
    }
}

Result batteryInfoGetChargeInfo(BatteryChargeInfo *out) {
    if (!out)
        return MAKERESULT(Module_Libnx, LibnxError_BadInput);
    
    return psmGetBatteryChargeInfoFields(out);
}

Result batteryInfoGetChargePercentage(u32 *out) {
    if (!g_batteryInfoInitialized)
        return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
    
    return psmGetBatteryChargePercentage(out);
}

Result batteryInfoIsEnoughPowerSupplied(bool *out) {
    if (!g_batteryInfoInitialized)
        return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
    
    return psmIsEnoughPowerSupplied(out);
}

Result batteryInfoEnableCharging(void) {
    return psmEnableBatteryCharging_internal();
}

Result batteryInfoDisableCharging(void) {
    return psmDisableBatteryCharging_internal();
}

Result batteryInfoEnableFastCharging(void) {
    return psmEnableFastBatteryCharging_internal();
}

Result batteryInfoDisableFastCharging(void) {
    return psmDisableFastBatteryCharging_internal();
}

const char* batteryInfoGetChargerTypeString(BatteryChargerType type) {
    if (type < 0 || type > ChargerType_Apple_2000mA)
        return "Unknown";
    
    return s_chargerTypeStrings[type];
}

const char* batteryInfoGetPowerRoleString(BatteryPowerRole role) {
    if (role < PowerRole_Sink || role > PowerRole_Source)
        return s_powerRoleStrings[0];
    
    return s_powerRoleStrings[role];
}

const char* batteryInfoGetPDStateString(BatteryPDControllerState state) {
    if (state < PDState_NewPDO || state > PDState_AcceptedRDO)
        return s_pdStateStrings[0];
    
    return s_pdStateStrings[state];
}