/*
   HARK Library
   Copyright (C) 2021 Honda Research Institute Japan Inc.
 */

#include "hark-lib/datastream.h"
#include "hark-lib/error.h"

namespace hark
{
    ForwardingDataProxy::~ForwardingDataProxy()
    {
    }

    void
    ForwardingDataProxy::clear()
    {
        // do nothing
    }

    size_t
    ForwardingDataProxy::count() const
    {
        if(this->_up_stream){
            return this->_up_stream->count();
        }else{
            throw RuntimeError("ForwardingDataProxy: Tried to retrieve count from data-proxy without upstream");
        }
    }

    data
    ForwardingDataProxy::last()
    {
        return this->relative(0);
    }

    data
    ForwardingDataProxy::relative(int offset)
    {
        if(this->_up_stream){
            return this->_up_stream->relative(offset);
        }else{
            throw RuntimeError("ForwardingDataProxy: Tried to retrieve data from data-proxy without upstream");
        }
    }

    data
    ForwardingDataProxy::at(size_t count)
    {
        if(this->_up_stream){
            return this->_up_stream->at(count);
        }else{
            throw RuntimeError("ForwardingDataProxy: Tried to retrieve data from data-proxy without upstream");
        }
    }

    bool
    ForwardingDataProxy::is_available_at(size_t count) const
    {
        if(this->_up_stream){
            return this->_up_stream->is_available_at(count);
        }else{
            return false;
        }
    }

    data
    ForwardingDataProxy::pop()
    {
        throw RuntimeError("ForwardingDataProxy: Tried to pop data from data-proxy without queue");
    }

    std::vector<data>
    ForwardingDataProxy::window(int offset_from, int offset_to)
    {
        if(this->_up_stream){
            return this->_up_stream->window(offset_from, offset_to);
        }else{
            throw RuntimeError("ForwardingDataProxy: Tried to retrieve data from data-proxy without upstream");
        }
    }

    void
    ForwardingDataProxy::push(const data &packet)
    {
        for(auto ds: this->_down_streams){
            ds->push(packet);
        }
    }

    void
    ForwardingDataProxy::set_static(const data &value)
    {
        throw RuntimeError("ForwardingDataProxy: Static value not supported.");
    }

    bool
    ForwardingDataProxy::is_static() const
    {
        return false;
    }

    bool
    ForwardingDataProxy::is_synchronous() const
    {
        return false;
    }

    bool
    ForwardingDataProxy::is_source_ready() const
    {
        return false;
    }

    bool
    ForwardingDataProxy::is_sink_ready() const
    {
        for(auto ds: this->_down_streams){
            if(!ds->is_sink_ready()){
                return false;
            }
        }
        return true;
    }

    void
    ForwardingDataProxy::set_look_window(size_t lookBack, size_t lookAhead, size_t look_back_required, size_t look_ahead_required)
    {
        throw RuntimeError("ForwardingDataProxy::set_window: Window operation not supported");
    }

    size_t
    ForwardingDataProxy::get_delay() const
    {
        if(this->_up_stream){
            return this->_up_stream->get_delay();
        }else{
            throw RuntimeError("ForwardingDataProxy: Tried to retrieve delay from data-proxy without upstream");
        }
    }

    void
    ForwardingDataProxy::set_delay(size_t delay)
    {
        for(auto ds: this->_down_streams){
            ds->set_delay(delay);
        }
    }

    void
    ForwardingDataProxy::set_extended_window(size_t extended_window)
    {
        throw RuntimeError("ForwardingDataProxy::set_extended_window: Window operation not supported");
    }

    void
    ForwardingDataProxy::notify_send_data()
    {
        if(this->DataSink::_parent){
            // std::cout << "ForwardingDataProxy::notify_send_data: 1\n";
            this->DataSink::_parent->notify_queue_vacancy();
            // {
            //     std::stringstream ss;
            //     ss << "ForwardingDataProxy::notify_send_data: 2, up-stream=" << this->_up_stream << std::endl;
            //     std::cout << ss.str();
            // }
            if(this->_up_stream){
                // std::cout << "ForwardingDataProxy::notify_send_data: 3\n";
                this->_up_stream->notify_send_data();
            }
        }else if(this->_up_stream){
            this->_up_stream->notify_send_data();
        }else{
            throw RuntimeError("ForwardingDataProxy: Tried to notify without upstream");
        }
    }

    void
    ForwardingDataProxy::notify_receive_data()
    {
        if(this->DataSource::_parent){
            this->DataSource::_parent->notify_queue_vacancy();
        }
        for(auto ds: this->_down_streams){
            ds->notify_receive_data();
        }
    }
}

/* end of file */
