// file      : butl/curl.cxx -*- C++ -*-
// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
// license   : MIT; see accompanying LICENSE file

#include <butl/curl>

#include <utility>   // move(), forward()
#include <exception> // invalid_argument

#include <butl/utility> // casecmp()

using namespace std;

namespace butl
{
  int 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 = fdnull (); // /dev/null
        return d.pipe.in.get ();
      }
    }

    return -1;
  }

  int curl::
  map_in (const path& f, method_proto mp, io_data& d)
  {
    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);
          out.open (move (d.pipe.out));
        }
        else
          d.pipe.in = fdnull (); // /dev/null

        return d.pipe.in.get ();
      }
    case ftp_get:
    case http_get:
      {
        throw invalid_argument ("file input specified for GET method");
      }
    }

    return -1;
  }

  int 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 = fdnull ();
        return d.pipe.out.get (); // /dev/null
      }
    }

    return -1;
  }

  int curl::
  map_out (const path& f, method_proto mp, io_data& d)
  {
    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);
          in.open (move (d.pipe.in));
        }
        else
        {
          d.options.push_back ("-o");
          d.options.push_back (f.string ().c_str ());
          d.pipe.out = fdnull (); // /dev/null
        }

        return d.pipe.out.get ();
      }
    case ftp_put:
      {
        throw invalid_argument ("file output specified for PUT method");
      }
    }

    return -1;
  }

  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 (casecmp (u, "ftp", n) == 0 ||
        casecmp (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 (casecmp (u, "http", n) == 0 ||
             casecmp (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");
  }
}