#include "uvpipe.h"
#include "uveventloop.h"
#include <utility>

namespace myapp::async {

namespace {

static std::unique_ptr<uv_pipe> create_pipe(uvw::Loop& loop)
{
    return std::make_unique<uv_pipe>(
        loop.resource<uvw::PipeHandle>());
}

} /* namespace myapp::async::{anonymous} */

std::unique_ptr<pipe> pipe::create(std::shared_ptr<event_loop> loop)
{
    return create_pipe(std::static_pointer_cast<uv_event_loop>(loop)->handle());
}

uv_pipe::uv_pipe(std::shared_ptr<uvw::PipeHandle> handle)
  : pipe_(std::move(handle))
{
}

uv_pipe::~uv_pipe()
{
    stop_read();
    close();
}

uvw::PipeHandle& uv_pipe::handle() noexcept
{
    return *pipe_;
}

uvw::PipeHandle const& uv_pipe::handle() const noexcept
{
    return *pipe_;
}

std::unique_ptr<pipe> uv_pipe::accept()
{
    auto newpipe = create_pipe(pipe_->loop());
    pipe_->accept(newpipe->handle());
    return newpipe;
}

void uv_pipe::bind(std::string name)
{
    pipe_->bind(std::move(name));
}

void uv_pipe::connect(std::string name, std::function<void()> callback)
{
    pipe_->once<uvw::ConnectEvent>([on_connected = std::move(callback)](uvw::ConnectEvent const&, uvw::PipeHandle&) {
        on_connected();
    });

    pipe_->connect(std::move(name));
}

void uv_pipe::listen(int backlog, std::function<void()> callback)
{
    pipe_->on<uvw::ListenEvent>([on_listen = std::move(callback)](uvw::ListenEvent const&, uvw::PipeHandle&) {
        on_listen();
    });

    pipe_->listen(backlog);
}

void uv_pipe::open(int fd)
{
    pipe_->open(static_cast<uv_file>(fd));
}

void uv_pipe::close()
{
    pipe_->close();
}

void uv_pipe::write(std::string data, std::function<void()> callback)
{
    auto buffer = std::make_unique<char[]>(data.size());
    std::copy(begin(data), end(data), buffer.get());

    pipe_->once<uvw::WriteEvent>([on_wrote = std::move(callback)](uvw::WriteEvent const&, uvw::PipeHandle&) {
        on_wrote();
    });

    pipe_->write(std::move(buffer), data.size());
}

void uv_pipe::shutdown(std::function<void()> callback)
{
    pipe_->once<uvw::ShutdownEvent>([on_shutdown = std::move(callback)](uvw::ShutdownEvent const&, uvw::PipeHandle&) {
        on_shutdown();
    });

    pipe_->shutdown();
}

void uv_pipe::read(std::function<void(std::string)> data, std::function<void()> end)
{
    pipe_->on<uvw::DataEvent>([on_data = std::move(data)](uvw::DataEvent const& e, uvw::PipeHandle&) {
        on_data({ e.data.get(), e.length });
    });
    pipe_->once<uvw::EndEvent>([on_end = std::move(end)](uvw::EndEvent const&, uvw::PipeHandle&) {
        on_end();
    });

    pipe_->read();
}

void uv_pipe::stop_read()
{
    pipe_->stop();
}

void uv_pipe::on_error(std::function<void( std::function<void()> )> callback)
{
    pipe_->on<uvw::ErrorEvent>([on_error = std::move(callback)](uvw::ErrorEvent const& e, uvw::PipeHandle&) {
        on_error([&e] { throw std::runtime_error(e.what()); });
    });
}

} /* namespace myapp::async */
