// file      : libbutl/curl.cxx -*- C++ -*-
// license   : MIT; see accompanying LICENSE file

#include <libbutl/curl.hxx>

#include <cassert>
#include <utility>   // move()
#include <exception> // invalid_argument

#include <libbutl/utility.hxx>

using namespace std;

namespace butl
{
  process::pipe curl::
  map_in (nullfd_t, method_proto mp, io_data& d)
  {
    switch (mp)
    {
    case ftp_put:
      throw invalid_argument ("no input specified for PUT method");
    case http_post:
      throw invalid_argument ("no input specified for POST method");
    case ftp_get:
    case http_get:
      {
        d.pipe.in = fdopen_null (); // /dev/null
        return pipe (d.pipe);
      }
    }

    assert (false); // Can't be here.
    return pipe ();
  }

  process::pipe curl::
  map_in (const path& f, method_proto mp, io_data& d)
  {
    pipe r;
    switch (mp)
    {
    case ftp_put:
    case http_post:
      {
        if (mp == ftp_put)
        {
          d.options.push_back ("--upload-file");
          d.options.push_back (f.string ().c_str ());
        }
        else
        {
          d.storage = '@' + f.string ();

          d.options.push_back ("--data-binary");
          d.options.push_back (d.storage.c_str ());
        }

        if (f.string () == "-")
        {
          d.pipe = fdopen_pipe (fdopen_mode::binary);
          r = pipe (d.pipe);

          out.open (move (d.pipe.out));
        }
        else
        {
          d.pipe.in = fdopen_null (); // /dev/null
          r = pipe (d.pipe);
        }

        return r;
      }
    case ftp_get:
    case http_get:
      {
        throw invalid_argument ("file input specified for GET method");
      }
    }

    assert (false); // Can't be here.
    return r;
  }

  process::pipe curl::
  map_out (nullfd_t, method_proto mp, io_data& d)
  {
    switch (mp)
    {
    case ftp_get:
    case http_get:
      throw invalid_argument ("no output specified for GET method");
    case ftp_put:
    case http_post: // May or may not produce output.
      {
        d.pipe.out = fdopen_null ();
        return pipe (d.pipe); // /dev/null
      }
    }

    assert (false); // Can't be here.
    return pipe ();
  }

  process::pipe curl::
  map_out (const path& f, method_proto mp, io_data& d)
  {
    pipe r;
    switch (mp)
    {
    case ftp_get:
    case http_get:
    case http_post:
      {
        if (f.string () == "-")
        {
          // Note: no need for any options, curl writes to stdout by default.
          //
          d.pipe = fdopen_pipe (fdopen_mode::binary);
          r = pipe (d.pipe);

          in.open (move (d.pipe.in));
        }
        else
        {
          d.options.push_back ("-o");
          d.options.push_back (f.string ().c_str ());
          d.pipe.out = fdopen_null (); // /dev/null
          r = pipe (d.pipe);
        }

        return r;
      }
    case ftp_put:
      {
        throw invalid_argument ("file output specified for PUT method");
      }
    }

    assert (false); // Can't be here.
    return r;
  }

  curl::method_proto curl::
  translate (method_type m, const string& u, method_proto_options& o)
  {
    size_t n (u.find ("://"));

    if (n == string::npos)
      throw invalid_argument ("no protocol in URL");

    if (icasecmp (u, "ftp", n) == 0 || icasecmp (u, "tftp", n) == 0)
    {
      switch (m)
      {
      case method_type::get: return method_proto::ftp_get;
      case method_type::put: return method_proto::ftp_put;
      case method_type::post:
        throw invalid_argument ("POST method with FTP protocol");
      }
    }
    else if (icasecmp (u, "http", n) == 0 || icasecmp (u, "https", n) == 0)
    {
      o.push_back ("--fail");     // Fail on HTTP errors (e.g., 404).
      o.push_back ("--location"); // Follow redirects.

      switch (m)
      {
      case method_type::get:  return method_proto::http_get;
      case method_type::post: return method_proto::http_post;
      case method_type::put:
        throw invalid_argument ("PUT method with HTTP protocol");
      }
    }

    throw invalid_argument ("unsupported protocol");
  }
}
