#include "AiOutputPort.h"
#include <cwchar>
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <vector>

AiOutputPort::AiOutputPort()
    : osd_(NULL),
      textItemId_(910001),
      eventTextItemId_(910002),
      subtitleItemId_(910003),
      detectionBaseItemId_(920000),
      lastDetectionItemCount_(0),
      lastEventTickMs_(0),
      videoOverlayEnabled_(true),
      videoWidth_(1280),
      videoHeight_(720)
{
}

void AiOutputPort::SetOsdPort(HWS_PORT osdPort)
{
    osd_ = osdPort;
}

void AiOutputPort::SetTextItemId(int itemId)
{
    textItemId_ = itemId;
}

void AiOutputPort::SetVideoSize(int width, int height)
{
    if (width > 0) videoWidth_ = width;
    if (height > 0) videoHeight_ = height;
}

static std::wstring AiBuildCompactStatusText(const AiDemoResult& result)
{
    // Keep video overlay compact. Detailed JSON/result is already printed in the log window.
    if (!result.ok) {
        std::wstring err = AiUtf8ToWide(result.summaryUtf8);
        if (err.empty()) err = L"AI request failed";
        if (err.size() > 48) err = err.substr(0, 48) + L"...";
        return L"AI: " + err;
    }

    if (!result.detections.empty()) {
        std::wstring text = L"AI: ";
        const size_t maxItems = (std::min)(result.detections.size(), (size_t)3);
        for (size_t i = 0; i < maxItems; ++i) {
            const AiDetection& d = result.detections[i];
            std::wstring label = AiUtf8ToWide(d.labelUtf8);
            if (label.empty()) label = L"object";

            wchar_t part[128] = {};
            swprintf_s(part, L"%s %.2f", label.c_str(), d.confidence);
            if (i > 0) text += L" | ";
            text += part;
        }

        if (result.detections.size() > maxItems) {
            wchar_t more[64] = {};
            swprintf_s(more, L" | +%u", (unsigned)(result.detections.size() - maxItems));
            text += more;
        }

        wchar_t boxes[64] = {};
        swprintf_s(boxes, L"  boxes=%u", (unsigned)result.detections.size());
        text += boxes;
        return text;
    }

    std::wstring summary = AiUtf8ToWide(result.summaryUtf8);
    if (summary.empty()) summary = L"ok";
    if (summary.size() > 42) summary = summary.substr(0, 42) + L"...";
    return L"AI: " + summary;
}

void AiOutputPort::EnsureDefaultTextItem()
{
    if (!osd_) return;
    HWS_OSD_ITEM item = {};
    item.id = textItemId_;
    item.enable = 1;
    item.type = HWS_OSD_ITEM_TEXT;
    item.zOrder = 1000;

    // Small status bar only. Do not cover a large area of the AI preview.
    item.x = 12;
    item.y = 12;
    item.width = 560;
    item.height = 30;
    item.opacity = 0.78f;
    item.color = 0xFFFFFF00;            // ARGB yellow.
    item.backgroundColor = 0x70000000;  // Compact semi-transparent black.
    item.transparentBackground = 0;
    item.fontSize = 18;
    wcscpy_s(item.fontName, L"Microsoft YaHei");
    wcscpy_s(item.text, L"AI: stopped");
    HWS_AddVideoOsdText(osd_, &item);

    HWS_OSD_ITEM eventItem = {};
    eventItem.id = eventTextItemId_;
    eventItem.enable = 1;
    eventItem.type = HWS_OSD_ITEM_TEXT;
    eventItem.zOrder = 1001;
    eventItem.x = 12;
    eventItem.y = 46;
    eventItem.width = 560;
    eventItem.height = 26;
    eventItem.opacity = 0.70f;
    eventItem.color = 0xFFFFD000;
    eventItem.backgroundColor = 0x60000000;
    eventItem.transparentBackground = 0;
    eventItem.fontSize = 16;
    wcscpy_s(eventItem.fontName, L"Microsoft YaHei");
    wcscpy_s(eventItem.text, L"");
    HWS_AddVideoOsdText(osd_, &eventItem);

    HWS_OSD_ITEM subtitle = {};
    subtitle.id = subtitleItemId_;
    subtitle.enable = 1;
    subtitle.type = HWS_OSD_ITEM_TEXT;
    subtitle.zOrder = 1100;
    subtitle.x = 24;
    subtitle.y = (videoHeight_ > 160) ? (videoHeight_ - 122) : 80;
    subtitle.width = (videoWidth_ > 80) ? (videoWidth_ - 48) : 560;
    subtitle.height = 104;
    subtitle.opacity = 0.80f;
    subtitle.color = 0xFFFFFFFF;
    subtitle.backgroundColor = 0x90000000;
    subtitle.transparentBackground = 0;
    subtitle.fontSize = 28;
    wcscpy_s(subtitle.fontName, L"Microsoft YaHei");
    wcscpy_s(subtitle.text, L"");
    HWS_AddVideoOsdText(osd_, &subtitle);
}

void AiOutputPort::SetStatusText(const wchar_t* text)
{
    if (!osd_) return;
    if (!videoOverlayEnabled_ && text && text[0]) return;
    HWS_UpdateVideoOsdText(osd_, textItemId_, text ? text : L"");
}

void AiOutputPort::SetSubtitleText(const wchar_t* text)
{
    if (!osd_) return;
    HWS_UpdateVideoOsdText(osd_, subtitleItemId_, text ? text : L"");
}

void AiOutputPort::SetVideoOverlayEnabled(bool enable)
{
    videoOverlayEnabled_ = enable;
    if (!osd_) return;
    if (!enable) {
        HWS_UpdateVideoOsdText(osd_, textItemId_, L"");
        HWS_UpdateVideoOsdText(osd_, eventTextItemId_, L"");
        ClearDetections();
        lastEventTickMs_ = 0;
    } else {
        HWS_UpdateVideoOsdText(osd_, textItemId_, L"AI: stopped");
    }
}

void AiOutputPort::ClearDetections()
{
    if (!osd_) return;
    for (int i = 0; i < lastDetectionItemCount_; ++i) {
        HWS_RemoveVideoOsdItem(osd_, detectionBaseItemId_ + i);
    }
    lastDetectionItemCount_ = 0;
}

void AiOutputPort::ClearEvents()
{
    if (!osd_) return;
    HWS_UpdateVideoOsdText(osd_, eventTextItemId_, L"");
    lastEventTickMs_ = 0;
}

void AiOutputPort::ApplyDetections(const AiDemoResult& result)
{
    if (!osd_) return;

    ClearDetections();

    const int maxBoxes = (std::min)((int)result.detections.size(), 20);
    int itemIndex = 0;

    for (int i = 0; i < maxBoxes; ++i) {
        const AiDetection& d = result.detections[(size_t)i];
        const int x = (int)(d.x + 0.5);
        const int y = (int)(d.y + 0.5);
        const int w = (int)(d.w + 0.5);
        const int h = (int)(d.h + 0.5);
        if (w <= 1 || h <= 1) continue;

        HWS_OSD_ITEM rect = {};
        rect.id = detectionBaseItemId_ + itemIndex++;
        rect.enable = 1;
        rect.type = HWS_OSD_ITEM_RECT;
        rect.zOrder = 900;
        rect.x = x;
        rect.y = y;
        rect.width = w;
        rect.height = h;
        rect.opacity = 0.90f;
        rect.color = 0xFF00FF00; // ARGB green.
        rect.fontSize = 2;       // Rect border thickness.
        HWS_AddVideoOsdRect(osd_, &rect);

        std::wstring label = AiUtf8ToWide(d.labelUtf8);
        if (label.empty()) label = L"object";
        wchar_t labelText[256] = {};
        swprintf_s(labelText, L"%s %.2f", label.c_str(), d.confidence);

        HWS_OSD_ITEM text = {};
        text.id = detectionBaseItemId_ + itemIndex++;
        text.enable = 1;
        text.type = HWS_OSD_ITEM_TEXT;
        text.zOrder = 901;
        text.x = x;
        text.y = (y >= 22) ? (y - 22) : y;
        text.width = 190;
        text.height = 22;
        text.opacity = 0.82f;
        text.color = 0xFFFFFFFF;
        text.backgroundColor = 0x90008000; // Compact dark green background.
        text.transparentBackground = 0;
        text.fontSize = 16;
        wcscpy_s(text.fontName, L"Microsoft YaHei");
        wcsncpy_s(text.text, labelText, _TRUNCATE);
        HWS_AddVideoOsdText(osd_, &text);
    }

    lastDetectionItemCount_ = itemIndex;
}


static std::wstring AiBuildEventText(const AiDemoResult& result)
{
    if (result.events.empty()) return std::wstring();

    const AiEvent& e = result.events[0];
    std::wstring msg = AiUtf8ToWide(e.messageUtf8);
    if (msg.empty()) {
        msg = AiUtf8ToWide(e.typeUtf8);
        std::wstring label = AiUtf8ToWide(e.labelUtf8);
        if (!label.empty()) msg += L": " + label;
    }
    if (msg.empty()) msg = L"AI event";
    if (msg.size() > 56) msg = msg.substr(0, 56) + L"...";

    std::wstring prefix = L"EVENT: ";
    std::wstring level = AiUtf8ToWide(e.levelUtf8);
    if (!level.empty() && level != L"info") {
        prefix += L"[" + level + L"] ";
    }
    return prefix + msg;
}

void AiOutputPort::ApplyEvents(const AiDemoResult& result)
{
    if (!osd_) return;

    const unsigned long long nowMs = GetTickCount64();
    if (!result.events.empty()) {
        std::wstring eventText = AiBuildEventText(result);
        HWS_UpdateVideoOsdText(osd_, eventTextItemId_, eventText.c_str());
        lastEventTickMs_ = nowMs;
        return;
    }

    // Keep the last event visible briefly. This makes one-shot events readable in preview/recording.
    if (lastEventTickMs_ != 0 && nowMs - lastEventTickMs_ > 2500ULL) {
        HWS_UpdateVideoOsdText(osd_, eventTextItemId_, L"");
        lastEventTickMs_ = 0;
    }
}

bool AiOutputPort::IsAudioResult(const AiDemoResult& result) const
{
    return result.typeUtf8.find("audio.") == 0 ||
           !result.textUtf8.empty() ||
           !result.translationUtf8.empty();
}

void AiOutputPort::ApplyAudioSubtitle(const AiDemoResult& result)
{
    if (!osd_) return;

    std::wstring text = AiUtf8ToWide(result.textUtf8);
    std::wstring trans = AiUtf8ToWide(result.translationUtf8);
    if (!text.empty() && !trans.empty() && text == trans) {
        // Whisper translate task returns English text directly; avoid showing the same line twice.
        text.clear();
    }
    std::wstring subtitle;

    if (!text.empty()) {
        subtitle += L"ASR: ";
        subtitle += text;
    }
    if (!trans.empty()) {
        if (!subtitle.empty()) subtitle += L"\n";
        subtitle += L"TR: ";
        subtitle += trans;
    }
    if (subtitle.empty()) {
        subtitle = AiUtf8ToWide(result.summaryUtf8);
    }
    if (subtitle.size() > 240) {
        subtitle = subtitle.substr(0, 240) + L"...";
    }

    HWS_UpdateVideoOsdText(osd_, subtitleItemId_, subtitle.c_str());
}

void AiOutputPort::ApplyResult(const AiDemoResult& result)
{
    if (!osd_) return;

    if (IsAudioResult(result)) {
        ApplyAudioSubtitle(result);
        return;
    }

    if (!videoOverlayEnabled_) {
        // ASR-only demo mode: keep the video clean and show only subtitles.
        ClearDetections();
        ClearEvents();
        HWS_UpdateVideoOsdText(osd_, textItemId_, L"");
        return;
    }

    std::wstring compact = AiBuildCompactStatusText(result);
    HWS_UpdateVideoOsdText(osd_, textItemId_, compact.c_str());

    ApplyDetections(result);
    ApplyEvents(result);
}
