// This file is part of the Orbbec Astra SDK [https://orbbec3d.com]
// Copyright (c) 2015 Orbbec 3D
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Be excellent to each other.
#ifndef ASTRA_PLUGIN_STREAM_BIN_HPP
#define ASTRA_PLUGIN_STREAM_BIN_HPP

#include "../capi/astra_types.h"
#include <astra_core/capi/plugins/astra_plugin.h>
#include <astra_core/plugins/PluginServiceProxy.hpp>

namespace astra { namespace plugins {

    template<typename TFrameType>
    class stream_bin
    {
    public:
        stream_bin(PluginServiceProxy& pluginService,
                   astra_stream_t streamHandle,
                   size_t dataSize)
            : streamHandle_(streamHandle),
              pluginService_(pluginService)
        {
            size_t dataWrapperSize = dataSize + sizeof(TFrameType);
            pluginService_.create_stream_bin(streamHandle,
                                             dataWrapperSize,
                                             &binHandle_,
                                             &currentBuffer_);
        }

        ~stream_bin()
        {
            pluginService_.destroy_stream_bin(streamHandle_, &binHandle_, &currentBuffer_);
        }

        bool has_connections()
        {
            bool hasConnections = false;
            pluginService_.bin_has_connections(binHandle_, &hasConnections);

            return hasConnections;
        }

        void cycle()
        {
            pluginService_.cycle_bin_buffers(binHandle_, &currentBuffer_);
        }

        TFrameType* begin_write(size_t frameIndex)
        {
            if (locked_)
                return reinterpret_cast<TFrameType*>(currentBuffer_->data);

            locked_ = true;
            currentBuffer_->frameIndex = frameIndex;
            return reinterpret_cast<TFrameType*>(currentBuffer_->data);
        }

        std::pair<astra_frame_t*, TFrameType*> begin_write_ex(size_t frameIndex)
        {
            if (locked_)
            {
                return std::make_pair(currentBuffer_, reinterpret_cast<TFrameType*>(currentBuffer_->data));
            }

            locked_ = true;
            currentBuffer_->frameIndex = frameIndex;

            return std::make_pair(currentBuffer_, reinterpret_cast<TFrameType*>(currentBuffer_->data));
        }

        void end_write()
        {
            if (!locked_)
                return;

            cycle();
            locked_ = false;
        }

        void link_connection(astra_streamconnection_t connection)
        {
            pluginService_.link_connection_to_bin(connection, binHandle_);

        }

        void unlink_connection(astra_streamconnection_t connection)
        {
            pluginService_.link_connection_to_bin(connection, nullptr);
        }

    private:
        astra_stream_t streamHandle_;
        astra_bin_t binHandle_;
        size_t bufferSize_{0};
        astra_frame_t* currentBuffer_{nullptr};
        PluginServiceProxy& pluginService_;
        bool locked_{false};
    };

}}

#endif /* ASTRA_PLUGIN_STREAM_BIN_HPP */
