// videoio to XAML bridge for OpenCV

// Copyright (c) Microsoft Open Technologies, Inc.
// All rights reserved.
//
// (3 - clause BSD License)
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that
// the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
// following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
// following disclaimer in the documentation and/or other materials provided with the distribution.
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or
// promote products derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#pragma once

// this header is included in the XAML App, so it cannot include any
// OpenCV headers, or a static assert will be raised

#include <ppl.h>
#include <ppltasks.h>
#include <concrt.h>
#include <agile.h>
#include <opencv2\core.hpp>

#include <mutex>
#include <memory>
#include <atomic>
#include <functional>


// Class VideoioBridge (singleton) is needed because the interface for
// VideoCapture_WinRT in cap_winrt_capture.hpp is fixed by OpenCV.
class VideoioBridge
{
public:

    static VideoioBridge& getInstance();

    // call after initialization
    void    setReporter(Concurrency::progress_reporter<int> pr) { reporter = pr; }

    // to be called from cvMain via cap_winrt on bg thread - non-blocking (async)
    void    requestForUIthreadAsync(int action);

    // TODO: modify in window.cpp: void cv::imshow( const String& winname, InputArray _img )
    void    imshow(/*cv::InputArray matToShow*/);   // shows Mat in the cvImage element
    void    swapInputBuffers();
    void    allocateOutputBuffers();
    void    swapOutputBuffers();
    void    updateFrameContainer();
    bool    openCamera();
    void    allocateBuffers(int width, int height);

    int     getDeviceIndex();
    void    setDeviceIndex(int index);
    int     getWidth();
    void    setWidth(int width);
    int     getHeight();
    void    setHeight(int height);

    std::atomic<bool>           bIsFrameNew;
    std::mutex                  inputBufferMutex;   // input is double buffered
    unsigned char *             frontInputPtr;      // OpenCV reads this
    unsigned char *             backInputPtr;       // Video grabber writes this
    std::atomic<unsigned long>  frameCounter;
    unsigned long               currentFrame;

    std::mutex                  outputBufferMutex;  // output is double buffered
    Windows::UI::Xaml::Media::Imaging::WriteableBitmap^ frontOutputBuffer;  // OpenCV write this
    Windows::UI::Xaml::Media::Imaging::WriteableBitmap^ backOutputBuffer;   // XAML reads this
    Windows::UI::Xaml::Controls::Image ^cvImage;

private:

    VideoioBridge() {
        deviceIndex = 0;
        width = 640;
        height = 480;
        deviceReady = false;
        bIsFrameNew = false;
        currentFrame = 0;
        frameCounter = 0;
    };

    // singleton
    VideoioBridge(VideoioBridge const &);
    void operator=(const VideoioBridge &);

    std::atomic<bool>   deviceReady;
    Concurrency::progress_reporter<int> reporter;

    // Mats are wrapped with singleton class, we do not support more than one
    // capture device simultaneously with the design at this time
    //
    // nb. inputBufferMutex was not able to guarantee that OpenCV Mats were
    // ready to accept data in the UI thread (memory access exceptions were thrown
    // even though buffer address was good).
    // Therefore allocation of Mats is also done on the UI thread before the video
    // device is initialized.
    cv::Mat frontInputMat;
    cv::Mat backInputMat;

    int deviceIndex, width, height;
};