// Copyright (c) 2016 Klemens D. Morgenstern
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)


#ifndef BOOST_PROCESS_DETAIL_WINDOWS_BASIC_CMD_HPP_
#define BOOST_PROCESS_DETAIL_WINDOWS_BASIC_CMD_HPP_

#include <boost/algorithm/string/trim.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/process/v1/shell.hpp>
#include <boost/process/v1/detail/windows/handler.hpp>

#include <vector>
#include <string>
#include <iterator>


namespace boost
{
namespace process
{
BOOST_PROCESS_V1_INLINE namespace v1
{
namespace detail
{
namespace windows
{

inline std::string build_args(const std::string & exe, std::vector<std::string> && data)
{
    std::string st = exe;

    //put in quotes if it has spaces or double quotes
    if(!exe.empty())
    {
        auto it = st.find_first_of(" \"");

        if(it != st.npos)//contains spaces.
        {
            // double existing quotes
            boost::replace_all(st, "\"", "\"\"");

            // surround with quotes
            st.insert(st.begin(), '"');
            st += '"';
        }
    }

    for (auto & arg : data)
    {
        if(!arg.empty())
        {
            auto it = arg.find_first_of(" \"");//contains space or double quotes?
            if(it != arg.npos)//yes
            {
                // double existing quotes
                boost::replace_all(arg, "\"", "\"\"");

                // surround with quotes
                arg.insert(arg.begin(), '"');
                arg += '"';
            }
        }
        else
        {
            arg = "\"\"";
        }

        if (!st.empty())//first one does not need a preceding space
            st += ' ';

        st += arg;
    }
    return st;
}

inline std::wstring build_args(const std::wstring & exe, std::vector<std::wstring> && data)
{
    std::wstring st = exe;

    //put in quotes if it has spaces or double quotes
    if(!exe.empty())
    {
        auto it = st.find_first_of(L" \"");

        if(it != st.npos)//contains spaces or double quotes.
        {
            // double existing quotes
            boost::replace_all(st, L"\"", L"\"\"");

            // surround with quotes
            st.insert(st.begin(), L'"');
            st += L'"';
        }
    }

    for(auto & arg : data)
    {
        if(!arg.empty())
        {
            auto it = arg.find_first_of(L" \"");//contains space or double quotes?
            if(it != arg.npos)//yes
            {
                // double existing quotes
                boost::replace_all(arg, L"\"", L"\"\"");

                // surround with quotes
                arg.insert(arg.begin(), L'"');
                arg += '"';
            }
        }
        else
        {
            arg = L"\"\"";
        }

        if (!st.empty())//first one does not need a preceding space
            st += L' ';

        st += arg;
    }
    return st;
}

template<typename Char>
struct exe_cmd_init : handler_base_ext
{
    using value_type  = Char;
    using string_type = std::basic_string<value_type>;

    static const char*    c_arg(char)    { return "/c";}
    static const wchar_t* c_arg(wchar_t) { return L"/c";}

    exe_cmd_init(const string_type & exe, bool cmd_only = false)
                : exe(exe), args({}), cmd_only(cmd_only) {};
    exe_cmd_init(string_type && exe, bool cmd_only = false)
                : exe(std::move(exe)), args({}), cmd_only(cmd_only) {};

    exe_cmd_init(string_type && exe, std::vector<string_type> && args)
            : exe(std::move(exe)), args(build_args(this->exe, std::move(args))), cmd_only(false) {};
    template <class Executor>
    void on_setup(Executor& exec) const
    {

        if (cmd_only && args.empty())
            exec.cmd_line = exe.c_str();
        else
        {
            exec.exe = exe.c_str();
            exec.cmd_line = args.c_str();
        }
    }
    static exe_cmd_init<Char> exe_args(string_type && exe, std::vector<string_type> && args)
    {
        return exe_cmd_init<Char>(std::move(exe), std::move(args));
    }
    static exe_cmd_init<Char> cmd(string_type&& cmd)
    {
        return exe_cmd_init<Char>(std::move(cmd), true);
    }
    static exe_cmd_init<Char> exe_args_shell(string_type && exe, std::vector<string_type> && args)
    {
        std::vector<string_type> args_ = {c_arg(Char())};
        if (!exe.empty())
            args_.emplace_back(std::move(exe));
        args_.insert(args_.end(), std::make_move_iterator(args.begin()), std::make_move_iterator(args.end()));
        string_type sh = get_shell(Char());

        return exe_cmd_init<Char>(std::move(sh), std::move(args_));
    }

#ifdef BOOST_PROCESS_USE_STD_FS
    static std:: string get_shell(char)    {return shell(). string(); }
    static std::wstring get_shell(wchar_t) {return shell().wstring(); }
#else
    static std:: string get_shell(char)    {return shell(). string(codecvt()); }
    static std::wstring get_shell(wchar_t) {return shell().wstring(codecvt());}
#endif

    static exe_cmd_init<Char> cmd_shell(string_type&& cmd)
    {
        std::vector<string_type> args = {c_arg(Char()), std::move(cmd)};
        string_type sh = get_shell(Char());

        return exe_cmd_init<Char>(
                std::move(sh),
                std::move(args));
    }
private:
    string_type exe;
    string_type args;
    bool cmd_only;
};

}



}
}
}
}


#endif /* INCLUDE_BOOST_PROCESS_WINDOWS_ARGS_HPP_ */
