/**
 * Paste picture data into guest clipboard.
 *
 * @param   pPasteboard    Guest PasteBoard reference.
 * @param   pData          Data to be pasted.
 * @param   cbDataSize     The size of *pData.
 *
 * @returns IPRT status code.
 */
static int vbclClipboardGuestPastePicture(PasteboardRef pPasteboard, void *pData, uint32_t cbDataSize)
{
    int     rc;
    void   *pBmp;
    size_t  cbBmpSize;

    AssertReturn(pData, VERR_INVALID_PARAMETER);
    /* Skip zero-sized buffer */
    AssertReturn(cbDataSize > 0, VINF_SUCCESS);

    rc = vboxClipboardDibToBmp(pData, cbDataSize, &pBmp, &cbBmpSize);
    AssertReturn(RT_SUCCESS(rc), rc);

    rc = vbclClipboardGuestPasteData(pPasteboard, (UInt8 *)pBmp, cbBmpSize, kUTTypeBMP, true);
    RTMemFree(pBmp);

    return rc;
}
void VBoxClipboardService::MessageReceived(BMessage *message)
{
    uint32_t formats = 0;
    message->PrintToStream();
    switch (message->what)
    {
        case VBOX_GUEST_CLIPBOARD_HOST_MSG_FORMATS:
        {
            int rc;
            uint32_t cb;
            void *pv;
            bool commit = false;

            if (message->FindInt32("Formats", (int32 *)&formats) != B_OK)
                break;

            if (!formats)
                break;

            if (!be_clipboard->Lock())
                break;

            be_clipboard->Clear();
            BMessage *clip = be_clipboard->Data();
            if (!clip)
            {
                be_clipboard->Unlock();
                break;
            }

            if (formats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
            {
                pv = _VBoxReadHostClipboard(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, &cb);
                if (pv)
                {
                    char *text;
                    rc = RTUtf16ToUtf8((PCRTUTF16)pv, &text);
                    if (RT_SUCCESS(rc))
                    {
                        BString str(text);
                        /** @todo user vboxClipboardUtf16WinToLin() */
                        // convert Windows CRLF to LF
                        str.ReplaceAll("\r\n", "\n");
                        // don't include the \0
                        clip->AddData("text/plain", B_MIME_TYPE, str.String(), str.Length());
                        RTStrFree(text);
                        commit = true;
                    }
                    free(pv);
                }
            }

            if (formats & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
            {
                pv = _VBoxReadHostClipboard(VBOX_SHARED_CLIPBOARD_FMT_BITMAP, &cb);
                if (pv)
                {
                    void  *pBmp  = NULL;
                    size_t cbBmp = 0;
                    rc = vboxClipboardDibToBmp(pv, cb, &pBmp, &cbBmp);
                    if (RT_SUCCESS(rc))
                    {
                        BMemoryIO mio(pBmp, cbBmp);
                        BBitmap *bitmap = BTranslationUtils::GetBitmap(&mio);
                        if (bitmap)
                        {
                            BMessage bitmapArchive;

                            /** @todo r=ramshankar: split this into functions with error checking as
                             *        neccessary. */
                            if (   bitmap->IsValid()
                                && bitmap->Archive(&bitmapArchive) == B_OK
                                && clip->AddMessage("image/bitmap", &bitmapArchive) == B_OK)
                            {
                                commit = true;
                            }
                            delete bitmap;
                        }
                        RTMemFree(pBmp);
                    }
                    free(pv);
                }
            }

            /*
             * Make sure we don't bounce this data back to the host, it's impolite. It can also
             * be used as a hint to applications probably.
             */
            clip->AddBool("FromVirtualBoxHost", true);
            if (commit)
                be_clipboard->Commit();
            be_clipboard->Unlock();
            break;
        }

        case VBOX_GUEST_CLIPBOARD_HOST_MSG_READ_DATA:
        {
            int rc;

            if (message->FindInt32("Formats", (int32 *)&formats) != B_OK)
                break;

            if (!formats)
                break;
            if (!be_clipboard->Lock())
                break;

            BMessage *clip = be_clipboard->Data();
            if (!clip)
            {
                be_clipboard->Unlock();
                break;
            }
            clip->PrintToStream();

            if (formats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
            {
                const char *text;
                int32 textLen;
                if (clip->FindData("text/plain", B_MIME_TYPE, (const void **)&text, &textLen) == B_OK)
                {
                    // usually doesn't include the \0 so be safe
                    BString str(text, textLen);
                    // convert from LF to Windows CRLF
                    str.ReplaceAll("\n", "\r\n");
                    PRTUTF16 pwsz;
                    rc = RTStrToUtf16(str.String(), &pwsz);
                    if (RT_SUCCESS(rc))
                    {
                        uint32_t cb = (RTUtf16Len(pwsz) + 1) * sizeof(RTUTF16);

                        rc = VbglR3ClipboardWriteData(fClientId, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, pwsz, cb);
                        //printf("VbglR3ClipboardWriteData: %d\n", rc);
                        RTUtf16Free(pwsz);
                    }
                }
            }
            else if (formats & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
            {
                BMessage archivedBitmap;
                if (clip->FindMessage("image/bitmap", &archivedBitmap) == B_OK ||
                    clip->FindMessage("image/x-be-bitmap", &archivedBitmap) == B_OK)
                {
                    BBitmap *bitmap = new(std::nothrow) BBitmap(&archivedBitmap);
                    if (bitmap)
                    {
                        // Don't delete bitmap, BBitmapStream will.
                        BBitmapStream stream(bitmap);
                        BTranslatorRoster *roster = BTranslatorRoster::Default();
                        if (roster && bitmap->IsValid())
                        {
                            BMallocIO bmpStream;
                            if (roster->Translate(&stream, NULL, NULL, &bmpStream, B_BMP_FORMAT) == B_OK)
                            {
                                const void *pDib;
                                size_t cbDibSize;
                                /* Strip out the BM header */
                                rc = vboxClipboardBmpGetDib(bmpStream.Buffer(), bmpStream.BufferLength(), &pDib, &cbDibSize);
                                if (RT_SUCCESS(rc))
                                {
                                    rc = VbglR3ClipboardWriteData(fClientId, VBOX_SHARED_CLIPBOARD_FMT_BITMAP, (void *)pDib,
                                                                  cbDibSize);
                                }
                            }
                        }
                    }
                }
            }

            be_clipboard->Unlock();
            break;
        }

        case B_CLIPBOARD_CHANGED:
        {
            printf("B_CLIPBOARD_CHANGED\n");
            const void *data;
            int32 dataLen;
            if (!be_clipboard->Lock())
                break;

            BMessage *clip = be_clipboard->Data();
            if (!clip)
            {
                be_clipboard->Unlock();
                break;
            }

            bool fromVBox;
            if (clip->FindBool("FromVirtualBoxHost", &fromVBox) == B_OK && fromVBox)
            {
                // It already comes from the host, discard.
                be_clipboard->Unlock();
                break;
            }

            if (clip->FindData("text/plain", B_MIME_TYPE, &data, &dataLen) == B_OK)
                formats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;

            if (   clip->HasMessage("image/bitmap")
                || clip->HasMessage("image/x-be-bitmap"))
            {
                formats |= VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
            }

            be_clipboard->Unlock();

            VbglR3ClipboardReportFormats(fClientId, formats);
            break;
        }

        case B_QUIT_REQUESTED:
            fExiting = true;
            break;

        default:
            BHandler::MessageReceived(message);
    }
}
/**
 * Write clipboard content to the host clipboard from the internal clipboard
 * structure.
 *
 * @param   pPasteboardRef Reference to the global pasteboard.
 * @param   pv             The source buffer.
 * @param   cb             The size of the source buffer.
 * @param   fFormats       The format type which should be written.
 *
 * @returns IPRT status code.
 */
int writeToPasteboard(PasteboardRef pPasteboard, void *pv, uint32_t cb, uint32_t fFormat)
{
    Log(("writeToPasteboard: fFormat = %02X\n", fFormat));

    /* Clear the pasteboard */
    if (PasteboardClear(pPasteboard))
        return VERR_NOT_SUPPORTED;

    /* Make sure all is in sync */
    PasteboardSynchronize(pPasteboard);

    int rc = VERR_NOT_SUPPORTED;
    /* Handle the unicode text */
    if (fFormat & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
    {
        PRTUTF16 pwszSrcText = static_cast <PRTUTF16>(pv);
        size_t cwSrc = cb / 2;
        size_t cwDest = 0;
        /* How long will the converted text be? */
        rc = vboxClipboardUtf16GetLinSize(pwszSrcText, cwSrc, &cwDest);
        if (RT_FAILURE(rc))
        {
            Log(("writeToPasteboard: clipboard conversion failed.  vboxClipboardUtf16GetLinSize returned %Rrc.  Abandoning.\n", rc));
            AssertRCReturn(rc, rc);
        }
        /* Empty clipboard? Not critical */
        if (cwDest == 0)
        {
            Log(("writeToPasteboard: received empty clipboard data from the guest, returning false.\n"));
            return VINF_SUCCESS;
        }
        /* Allocate the necessary memory */
        PRTUTF16 pwszDestText = static_cast <PRTUTF16>(RTMemAlloc(cwDest * 2));
        if (pwszDestText == NULL)
        {
            Log(("writeToPasteboard: failed to allocate %d bytes\n", cwDest * 2));
            return VERR_NO_MEMORY;
        }
        /* Convert the EOL */
        rc = vboxClipboardUtf16WinToLin(pwszSrcText, cwSrc, pwszDestText, cwDest);
        if (RT_FAILURE(rc))
        {
            Log(("writeToPasteboard: clipboard conversion failed.  vboxClipboardUtf16WinToLin() returned %Rrc.  Abandoning.\n", rc));
            RTMemFree(pwszDestText);
            AssertRCReturn(rc, rc);
        }

        CFDataRef textData = NULL;
        /* Item id is 1. Nothing special here. */
        PasteboardItemID itemId = (PasteboardItemID)1;
        /* Create a CData object which we could pass to the pasteboard */
        if ((textData = CFDataCreate(kCFAllocatorDefault,
                                     reinterpret_cast<UInt8*>(pwszDestText), cwDest * 2)))
        {
            /* Put the Utf-16 version to the pasteboard */
            PasteboardPutItemFlavor(pPasteboard, itemId,
                                    kUTTypeUTF16PlainText,
                                    textData, 0);
        }
        /* Create a Utf-8 version */
        char *pszDestText;
        rc = RTUtf16ToUtf8(pwszDestText, &pszDestText);
        if (RT_SUCCESS(rc))
        {
            /* Create a CData object which we could pass to the pasteboard */
            if ((textData = CFDataCreate(kCFAllocatorDefault,
                                         reinterpret_cast<UInt8*>(pszDestText), strlen(pszDestText))))
            {
                /* Put the Utf-8 version to the pasteboard */
                PasteboardPutItemFlavor(pPasteboard, itemId,
                                        kUTTypeUTF8PlainText,
                                        textData, 0);
            }
            RTStrFree(pszDestText);
        }

        RTMemFree(pwszDestText);
        rc = VINF_SUCCESS;
    }
    /* Handle the bitmap */
    else if (fFormat & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
    {
        /* Create a full BMP from it */
        void *pBmp;
        size_t cbBmpSize;
        CFDataRef bmpData = NULL;
        /* Item id is 1. Nothing special here. */
        PasteboardItemID itemId = (PasteboardItemID)1;

        rc = vboxClipboardDibToBmp(pv, cb, &pBmp, &cbBmpSize);
        if (RT_SUCCESS(rc))
        {
            /* Create a CData object which we could pass to the pasteboard */
            if ((bmpData = CFDataCreate(kCFAllocatorDefault,
                                         reinterpret_cast<UInt8*>(pBmp), cbBmpSize)))
            {
                /* Put the Utf-8 version to the pasteboard */
                PasteboardPutItemFlavor(pPasteboard, itemId,
                                        kUTTypeBMP,
                                        bmpData, 0);
            }
            RTMemFree(pBmp);
        }
        rc = VINF_SUCCESS;
    }
    else
        rc = VERR_NOT_IMPLEMENTED;

    Log(("writeToPasteboard: rc = %02X\n", rc));
    return rc;
}