#include "displayserver.h"
#include "common/settings.h"
#include "common/processing.h"
#include "linux/displayserver_linux.h"

#include <math.h>

static bool checkHdrStatus(FFDisplayResult* display)
{
    FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();

    if (ffSettingsGetAndroidProperty("ro.surface_flinger.has_HDR_display", &buffer))
    {
        if (ffStrbufIgnCaseEqualS(&buffer, "true"))
        {
            display->hdrStatus = FF_DISPLAY_HDR_STATUS_SUPPORTED;

            if (ffSettingsGetAndroidProperty("persist.sys.hdr_mode", &buffer) &&
                ffStrbufToUInt(&buffer, 0) > 0)
                display->hdrStatus = FF_DISPLAY_HDR_STATUS_ENABLED;

            return true;
        }
        else
        {
            display->hdrStatus = FF_DISPLAY_HDR_STATUS_UNSUPPORTED;
            return true;
        }
    }

    display->hdrStatus = FF_DISPLAY_HDR_STATUS_UNKNOWN;
    return false;
}

static void detectWithDumpsys(FFDisplayServerResult* ds)
{
    FF_STRBUF_AUTO_DESTROY buf = ffStrbufCreate();
    if (ffProcessAppendStdOut(&buf, (char* []) {
        "/system/bin/dumpsys",
        "display",
        NULL,
    }) != NULL || buf.length == 0)
        return; // Only works in `adb shell`, or when rooted

    uint32_t index = 0;
    while ((index = ffStrbufNextIndexS(&buf, index, "DisplayDeviceInfo")) < buf.length)
    {
        index += strlen("DisplayDeviceInfo");
        uint32_t nextIndex = ffStrbufNextIndexC(&buf, index, '\n');
        buf.chars[nextIndex] = '\0';
        const char* info = buf.chars + index;

        // {"Builtin display": uniqueId="local:4630947134992368259", 1440 x 3200, modeId 2, defaultModeId 1, supportedModes [{id=1, width=1440, height=3200, fps=60.000004, alternativeRefreshRates=[24.000002, 30.000002, 40.0, 120.00001, 120.00001, 120.00001, 120.00001, 120.00001]},
        FF_STRBUF_AUTO_DESTROY name = ffStrbufCreateA(64);
        unsigned width = 0, height = 0, modeId = 0;
        double refreshRate = 0;
        // {"Builtin display": uniqueId="local:4630947134992368259", 1440 x 3200, modeId 2
        int res = sscanf(info, "{\"%63[^\"]\":%*s%u x %u, modeId%u", name.chars, &width, &height, &modeId);
        if (res >= 3)
        {
            if (res == 4)
            {
                ++info; // skip first '{'
                while ((info = strchr(info, '{')))
                {
                    ++info;

                    unsigned id;
                    double fps;
                    // id=1, width=1440, height=3200, fps=60.000004,
                    if (sscanf(info, "id=%u, %*s%*s fps=%lf", &id, &fps) >= 2)
                    {
                        if (id == modeId)
                        {
                            refreshRate = fps;
                            break;
                        }
                    }
                    else
                        break;
                }
            }

            ffStrbufRecalculateLength(&name);
            FFDisplayResult* display = ffdsAppendDisplay(ds,
                (uint32_t)width, (uint32_t)height,
                refreshRate,
                0, 0,
                0, 0,
                0,
                0,
                &name,
                FF_DISPLAY_TYPE_UNKNOWN,
                false,
                0,
                0,
                0,
                "dumpsys"
            );
            if (display) display->hdrStatus = checkHdrStatus(display);
        }

        index = nextIndex + 1;
    }
}

static bool detectWithGetprop(FFDisplayServerResult* ds)
{
    // Only for MiUI
    FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();

    if (ffSettingsGetAndroidProperty("persist.sys.miui_resolution", &buffer) &&
        ffStrbufContainC(&buffer, ','))
    {
        // 1440,3200,560 => width,height,densityDpi
        uint32_t width = (uint32_t) ffStrbufToUInt(&buffer, 0);
        ffStrbufSubstrAfterFirstC(&buffer, ',');
        uint32_t height = (uint32_t) ffStrbufToUInt(&buffer, 0);
        ffStrbufSubstrAfterFirstC(&buffer, ',');
        double scaleFactor = (double) ffStrbufToUInt(&buffer, 0) / 160.;
        FFDisplayResult* display = ffdsAppendDisplay(ds,
            width, height,
            0,
            (uint32_t) (width / scaleFactor + .5), (uint32_t) (height / scaleFactor + .5),
            0, 0,
            0,
            0,
            NULL,
            FF_DISPLAY_TYPE_BUILTIN,
            false,
            0,
            0,
            0,
            "getprop"
        );
        if (display) display->hdrStatus = checkHdrStatus(display);
        return !!display;
    }

    return false;
}

static bool detectDE(FFDisplayServerResult* ds)
{
    if (ffSettingsGetAndroidProperty("ro.vivo.os.build.display.id", &ds->dePrettyName)) // OriginOS 6
    {
        ffStrbufAppendC(&ds->dePrettyName, ' ');
        ffSettingsGetAndroidProperty("ro.vivo.product.version", &ds->dePrettyName); // PD2505D_xxx
        return true;
    }
    if (ffSettingsGetAndroidProperty("ro.build.version.magic", &ds->dePrettyName) ||
        ffSettingsGetAndroidProperty("ro.build.version.emui", &ds->dePrettyName))
    {
        ffStrbufReplaceAllC(&ds->dePrettyName, '_', ' ');
        return true;
    }
    if (ffSettingsGetAndroidProperty("ro.mi.os.version.name", &ds->dePrettyName))
    {
        // MiUI like
        ffStrbufClear(&ds->dePrettyName);
        ffSettingsGetAndroidProperty("ro.build.version.incremental", &ds->dePrettyName); // Detail version number
        if (ffStrbufStartsWithS(&ds->dePrettyName, "OS"))
        {
            ds->dePrettyName.chars[0] = 'S';
            ds->dePrettyName.chars[1] = ' ';
            ffStrbufPrependS(&ds->dePrettyName, "HyperO");
        }
        else if (ffStrbufStartsWithS(&ds->dePrettyName, "V"))
        {
            ds->dePrettyName.chars[0] = ' ';
            ffStrbufPrependS(&ds->dePrettyName, "MiUI");
        }
        else
            ffStrbufSetStatic(&ds->dePrettyName, "MiUI");
        return true;
    }
    if (ffSettingsGetAndroidProperty("ro.build.version.oplusrom", &ds->dePrettyName))
    {
        if (ffStrbufStartsWithS(&ds->dePrettyName, "V"))
            ffStrbufSubstrAfter(&ds->dePrettyName, 0);
        ffStrbufPrependS(&ds->dePrettyName, "ColorOS");
        return true;
    }
    if (ffSettingsGetAndroidProperty("ro.oxygen.version", &ds->dePrettyName))
    {
        ffStrbufPrependS(&ds->dePrettyName, "OxygenOS");
        return true;
    }
    if (ffSettingsGetAndroidProperty("ro.build.display.id", &ds->dePrettyName) && ffStrbufStartsWithS(&ds->dePrettyName, "RedMagicOS"))
    {
        ffStrbufInsertNC(&ds->dePrettyName, strlen("RedMagicOS"), 1, ' ');
        return true;
    }

    return false;
}

void ffConnectDisplayServerImpl(FFDisplayServerResult* ds)
{
    const char* error = ffdsConnectXcbRandr(ds);
    if (error)
        error = ffdsConnectXrandr(ds);
    if (!error)
    {
        ffdsDetectWMDE(ds);
        return;
    }

    // https://source.android.com/docs/core/graphics/surfaceflinger-windowmanager
    ffStrbufSetStatic(&ds->wmProcessName, "system_server");
    ffStrbufSetStatic(&ds->wmPrettyName, "WindowManager"); // A system service managed by system_server
    ffStrbufSetStatic(&ds->wmProtocolName, FF_WM_PROTOCOL_SURFACEFLINGER);

    if (!detectWithGetprop(ds))
        detectWithDumpsys(ds);

    detectDE(ds);
}
