/* $Id: display_vrdp.cpp $ */

/** @file
 * Presenter API: CrFbDisplayVrdp class implementation -- display content over VRDP.
 */

/*
 * Copyright (C) 2014-2019 Oracle Corporation
 *
 * This file is part of VirtualBox Open Source Edition (OSE), as
 * available from http://www.virtualbox.org. This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License (GPL) as published by the Free Software
 * Foundation, in version 2 as it comes in the "COPYING" file of the
 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 */

#include "server_presenter.h"


CrFbDisplayVrdp::CrFbDisplayVrdp()
{
    memset(&mPos, 0, sizeof (mPos));
}


int CrFbDisplayVrdp::EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
{
    int rc = CrFbDisplayBase::EntryCreated(pFb, hEntry);
    if (!RT_SUCCESS(rc))
    {
        WARN(("EntryAdded failed rc %d", rc));
        return rc;
    }

    Assert(!CrFbDDataEntryGet(hEntry, slotGet()));
    rc = vrdpCreate(pFb, hEntry);
    if (!RT_SUCCESS(rc))
    {
        WARN(("vrdpCreate failed rc %d", rc));
        return rc;
    }

    return VINF_SUCCESS;
}


int CrFbDisplayVrdp::EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry)
{
    int rc = CrFbDisplayBase::EntryReplaced(pFb, hNewEntry, hReplacedEntry);
    if (!RT_SUCCESS(rc))
    {
        WARN(("err"));
        return rc;
    }

    const VBOXVR_SCR_COMPOSITOR_ENTRY* pReplacedEntry = CrFbEntryGetCompositorEntry(hReplacedEntry);
    CR_TEXDATA *pReplacedTex = CrVrScrCompositorEntryTexGet(pReplacedEntry);
    const VBOXVR_SCR_COMPOSITOR_ENTRY* pNewEntry = CrFbEntryGetCompositorEntry(hNewEntry);
    CR_TEXDATA *pNewTex = CrVrScrCompositorEntryTexGet(pNewEntry);

    CrTdBltDataInvalidateNe(pReplacedTex);

    rc = CrTdBltEnter(pNewTex);
    if (RT_SUCCESS(rc))
    {
        rc = vrdpFrame(hNewEntry);
        CrTdBltLeave(pNewTex);
    }
    else
        WARN(("CrTdBltEnter failed %d", rc));

    return rc;
}


int CrFbDisplayVrdp::EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
{
    int rc = CrFbDisplayBase::EntryTexChanged(pFb, hEntry);
    if (!RT_SUCCESS(rc))
    {
        WARN(("err"));
        return rc;
    }

    const VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry = CrFbEntryGetCompositorEntry(hEntry);
    CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(pEntry);

    rc = CrTdBltEnter(pTex);
    if (RT_SUCCESS(rc))
    {
        rc = vrdpFrame(hEntry);
        CrTdBltLeave(pTex);
    }
    else
        WARN(("CrTdBltEnter failed %d", rc));

    return rc;
}


int CrFbDisplayVrdp::EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
{
    int rc = CrFbDisplayBase::EntryRemoved(pFb, hEntry);
    if (!RT_SUCCESS(rc))
    {
        WARN(("err"));
        return rc;
    }

    const VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry = CrFbEntryGetCompositorEntry(hEntry);
    CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(pEntry);
    CrTdBltDataInvalidateNe(pTex);

    return vrdpRegions(pFb, hEntry);
}


int CrFbDisplayVrdp::EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
{
    int rc = CrFbDisplayBase::EntryDestroyed(pFb, hEntry);
    if (!RT_SUCCESS(rc))
    {
        WARN(("err"));
        return rc;
    }

    vrdpDestroy(hEntry);
    return VINF_SUCCESS;
}


int CrFbDisplayVrdp::EntryPosChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
{
    int rc = CrFbDisplayBase::EntryPosChanged(pFb, hEntry);
    if (!RT_SUCCESS(rc))
    {
        WARN(("err"));
        return rc;
    }

    vrdpGeometry(hEntry);

    return VINF_SUCCESS;
}


int CrFbDisplayVrdp::RegionsChanged(struct CR_FRAMEBUFFER *pFb)
{
    int rc = CrFbDisplayBase::RegionsChanged(pFb);
    if (!RT_SUCCESS(rc))
    {
        WARN(("err"));
        return rc;
    }

    return vrdpRegionsAll(pFb);
}


int CrFbDisplayVrdp::FramebufferChanged(struct CR_FRAMEBUFFER *pFb)
{
    int rc = CrFbDisplayBase::FramebufferChanged(pFb);
    if (!RT_SUCCESS(rc))
    {
        WARN(("err"));
        return rc;
    }

    syncPos();

    rc = vrdpSyncEntryAll(pFb);
    if (!RT_SUCCESS(rc))
    {
        WARN(("err"));
        return rc;
    }

    return vrdpRegionsAll(pFb);
}


void CrFbDisplayVrdp::syncPos()
{
    const struct VBVAINFOSCREEN* pScreenInfo = CrFbGetScreenInfo(getFramebuffer());
    mPos.x = pScreenInfo->i32OriginX;
    mPos.y = pScreenInfo->i32OriginY;
}

int CrFbDisplayVrdp::fbCleanup()
{
    int rc = fbCleanupRemoveAllEntries();
    if (!RT_SUCCESS(rc))
    {
        WARN(("err"));
        return rc;
    }

    return CrFbDisplayBase::fbCleanup();
}


int CrFbDisplayVrdp::fbSync()
{
    syncPos();

    int rc = fbSynchAddAllEntries();
    if (!RT_SUCCESS(rc))
    {
        WARN(("err"));
        return rc;
    }

    return CrFbDisplayBase::fbSync();
}


void CrFbDisplayVrdp::vrdpDestroy(HCR_FRAMEBUFFER_ENTRY hEntry)
{
    void *pVrdp = CrFbDDataEntryGet(hEntry, slotGet());
    cr_server.outputRedirect.CROREnd(pVrdp);
}


void CrFbDisplayVrdp::vrdpGeometry(HCR_FRAMEBUFFER_ENTRY hEntry)
{
    void *pVrdp = CrFbDDataEntryGet(hEntry, slotGet());
    const VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry = CrFbEntryGetCompositorEntry(hEntry);

    cr_server.outputRedirect.CRORGeometry(
        pVrdp,
        mPos.x + CrVrScrCompositorEntryRectGet(pEntry)->xLeft,
        mPos.y + CrVrScrCompositorEntryRectGet(pEntry)->yTop,
        CrVrScrCompositorEntryTexGet(pEntry)->Tex.width,
        CrVrScrCompositorEntryTexGet(pEntry)->Tex.height);
}


int CrFbDisplayVrdp::vrdpRegions(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
{
    void *pVrdp = CrFbDDataEntryGet(hEntry, slotGet());
    const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(pFb);
    const VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry = CrFbEntryGetCompositorEntry(hEntry);
    uint32_t cRects;
    const RTRECT *pRects;

    int rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRects, &pRects, NULL, NULL);
    if (!RT_SUCCESS(rc))
    {
        WARN(("CrVrScrCompositorEntryRegionsGet failed, rc %d", rc));
        return rc;
    }

    cr_server.outputRedirect.CRORVisibleRegion(pVrdp, cRects, pRects);
    return VINF_SUCCESS;
}


int CrFbDisplayVrdp::vrdpFrame(HCR_FRAMEBUFFER_ENTRY hEntry)
{
    void *pVrdp = CrFbDDataEntryGet(hEntry, slotGet());
    const VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry = CrFbEntryGetCompositorEntry(hEntry);
    CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(pEntry);
    const CR_BLITTER_IMG *pImg;
    CrTdBltDataInvalidateNe(pTex);

    int rc = CrTdBltDataAcquire(pTex, GL_BGRA, !!(CrVrScrCompositorEntryFlagsGet(pEntry) & CRBLT_F_INVERT_SRC_YCOORDS), &pImg);
    if (!RT_SUCCESS(rc))
    {
        WARN(("CrTdBltDataAcquire failed rc %d", rc));
        return rc;
    }

    cr_server.outputRedirect.CRORFrame(pVrdp, pImg->pvData, pImg->cbData);
    CrTdBltDataRelease(pTex);
    return VINF_SUCCESS;
}


int CrFbDisplayVrdp::vrdpRegionsAll(struct CR_FRAMEBUFFER *pFb)
{
    const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(pFb);
    VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter;
    CrVrScrCompositorConstIterInit(pCompositor, &Iter);
    const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry;
    while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL)
    {
        HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry);
        vrdpRegions(pFb, hEntry);
    }

    return VINF_SUCCESS;
}


int CrFbDisplayVrdp::vrdpSynchEntry(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry)
{
    vrdpGeometry(hEntry);

    return vrdpRegions(pFb, hEntry);;
}


int CrFbDisplayVrdp::vrdpSyncEntryAll(struct CR_FRAMEBUFFER *pFb)
{
    const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(pFb);
    VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter;
    CrVrScrCompositorConstIterInit(pCompositor, &Iter);
    const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry;
    while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL)
    {
        HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry);
        int rc = vrdpSynchEntry(pFb, hEntry);
        if (!RT_SUCCESS(rc))
        {
            WARN(("vrdpSynchEntry failed rc %d", rc));
            return rc;
        }
    }

    return VINF_SUCCESS;
}


int CrFbDisplayVrdp::vrdpCreate(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry)
{
    void *pVrdp;

    /* Query supported formats. */
    uint32_t cbFormats = 4096;
    char *pachFormats = (char *)crAlloc(cbFormats);

    if (!pachFormats)
    {
        WARN(("crAlloc failed"));
        return VERR_NO_MEMORY;
    }

    int rc = cr_server.outputRedirect.CRORContextProperty(cr_server.outputRedirect.pvContext,
                                                          0 /* H3DOR_PROP_FORMATS */, /// @todo from a header
                                                          pachFormats, cbFormats, &cbFormats);
    if (RT_SUCCESS(rc))
    {
        if (RTStrStr(pachFormats, "H3DOR_FMT_RGBA_TOPDOWN"))
        {
            cr_server.outputRedirect.CRORBegin(
                cr_server.outputRedirect.pvContext,
                &pVrdp,
                "H3DOR_FMT_RGBA_TOPDOWN"); /// @todo from a header

            if (pVrdp)
            {
                rc = CrFbDDataEntryPut(hEntry, slotGet(), pVrdp);
                if (RT_SUCCESS(rc))
                {
                    vrdpGeometry(hEntry);
                    vrdpRegions(hFb, hEntry);
                    //vrdpFrame(hEntry);
                    return VINF_SUCCESS;
                }
                else
                    WARN(("CrFbDDataEntryPut failed rc %d", rc));

                cr_server.outputRedirect.CROREnd(pVrdp);
            }
            else
            {
                WARN(("CRORBegin failed"));
                rc = VERR_GENERAL_FAILURE;
            }
        }
    }
    else
        WARN(("CRORContextProperty failed rc %d", rc));

    crFree(pachFormats);

    return rc;
}

