//
// Created by sozonov on 18.10.2021.
//

#include "CreatePipes.h"

#include <iostream>
#include <memory>

#include "boost/process/pipe.hpp"


/*! Создание в операционной системе именнованого канала, заданого имени и заданого направления.<br>
 *  Обеспечена кросплатформенность данной функции (Windows/Linux).
 * @param path Назавние именновоного канала.
 * @param mode Направление работы именнованного канала: <br> std::ios_base::in / std::ios_base::out
 * @return Именнованый канал, привязаный к библиотеке boost.
 */
boost::process::pipe build_named_io_pipe( std::filesystem::path path, std::ios_base::openmode mode );

std::tuple< std::unique_ptr< std::istream >,
            std::unique_ptr< std::ostream > > itr::create_pipes( std::filesystem::path input_path,
                                                                           std::filesystem::path output_path,
                                                                           std::ostream & info ) {

    //todo: перед созданием пайпов проверять валидность пути и выводить инфу в поток &info

    info << "Create pipes: " << input_path << ", " << output_path << "..." << std::endl;

    
    auto ostream = std::make_unique<::boost::process::opstream>( std::move( build_named_io_pipe( output_path,
                                                                            std::ios_base::out ) ) );

    auto istream = std::make_unique<::boost::process::ipstream>( std::move( build_named_io_pipe( input_path,
                                                                            std::ios_base::in ) ) );

#if (defined (_WIN32) || defined (_WIN64))
    
    ::boost::winapi::ConnectNamedPipe( istream->pipe().native_source(), nullptr );
    ::boost::winapi::ConnectNamedPipe( ostream->pipe().native_sink(), nullptr );

#endif

    info << "Pipes " << input_path << " and " << output_path << " are connected!" << std::endl;

    return {
        std::move(istream),
        std::move(ostream)
    };
};


boost::process::pipe  build_named_io_pipe( std::filesystem::path path, std::ios_base::openmode mode ) {

#if (defined (_WIN32) || defined (_WIN64))

    if( mode == std::ios_base::in ) {
        boost::winapi::HANDLE_ input_source = boost::winapi::create_named_pipe(
                path.string().c_str(),
                boost::winapi::PIPE_ACCESS_INBOUND_, //write flag
                boost::winapi::PIPE_TYPE_BYTE_ | boost::winapi::PIPE_READMODE_BYTE_ | boost::winapi::PIPE_WAIT_ ,
                4,
                8192,
                8192,
                0,
                nullptr );
        return boost::process::pipe( input_source, boost::winapi::INVALID_HANDLE_VALUE_ );
    }

    if( mode == std::ios_base::out ) {
        boost::winapi::HANDLE_ output_source = boost::winapi::create_named_pipe(
                path.string().c_str(),
                boost::winapi::PIPE_ACCESS_OUTBOUND_, //write flag
                boost::winapi::PIPE_TYPE_BYTE_ | ::boost::winapi::PIPE_READMODE_BYTE_ |
                boost::winapi::PIPE_WAIT_,
                4,
                8192,
                8192,
                0,
                nullptr);
        return boost::process::pipe( boost::winapi::INVALID_HANDLE_VALUE_ , output_source);
    }

    return boost::process::pipe( boost::winapi::INVALID_HANDLE_VALUE_ ,boost::winapi::INVALID_HANDLE_VALUE_ );

#elif (defined (LINUX) || defined (__linux__))

    if( mode == std::ios_base::in ) {
        if( not std::filesystem::exists(path) ) {

            auto fifo = mkfifo(path.c_str(), 0666 );

            if (fifo != 0)
                boost::process::detail::throw_last_error("mkfifo() failed");

            std::cout << "Input fifo created: " << path << std::endl;

        }
        else
        {
            std::cout << "Input fifo exists: " << path << std::endl;
        }

        int read_fd = open(path.c_str(), O_RDONLY );

        if( read_fd == -1 )
            boost::process::detail::throw_last_error();

        return boost::process::pipe( read_fd, -1 );
    }

    if( mode == std::ios_base::out ) {
        if( not std::filesystem::exists(path) ) {

            auto fifo = mkfifo(path.c_str(), 0666 );

            if (fifo != 0)
                boost::process::detail::throw_last_error("mkfifo() failed");
            
            std::cout << "Output fifo created: " << path << std::endl;
        }
        else
        {
            std::cout << "Output fifo exists: " << path << std::endl;
        }

        int  write_fd = open(path.c_str(), O_WRONLY );

        if (write_fd == -1)
            boost::process::detail::throw_last_error();

        return boost::process::pipe( -1, write_fd );
    }

    return boost::process::pipe( -1, -1 );
#endif
};
