/*---------------------------------------------------------------------------*\
                          ____  _ _ __ _ __  ___ _ _
                         |_ / || | '_ \ '_ \/ -_) '_|
                         /__|\_, | .__/ .__/\___|_|
                             |__/|_|  |_|
\*---------------------------------------------------------------------------*/

#include <cstdio>
#include <iostream>
#include <unistd.h> // for getpass

#include "callbacks/media.h"

#include "utils/messages.h"
#include "utils/prompt.h"

#include <zypp/media/MediaManager.h>
#include <zypp-curl/auth/CurlAuthData>

using std::cin;
using namespace zypp;

// ---------[ common ]--------------------------------------------------------

static void set_common_option_help( PromptOptions & popts )
{
  // help text for the "Abort, retry, ignore?" prompt for media errors
  popts.setOptionHelp( 0, _("Skip retrieval of the file and abort current operation.") );
  // help text for the "Abort, retry, ignore?" prompt for media errors
  popts.setOptionHelp( 1, _("Try to retrieve the file again.") );
  // help text for the "Abort, retry, ignore?" prompt for media errors
  popts.setOptionHelp( 2, _("Skip retrieval of the file and try to continue with the operation without the file.") );
  // help text for the "Abort, retry, ignore?" prompt for media errors
  popts.setOptionHelp( 3, _("Change current base URI and try retrieving the file again.") );
  // hide advanced options
  popts.setShownCount( 3 );
}

static MediaChangeReport::Action handle_common_options( unsigned reply, Url & url )
{
  MediaChangeReport::Action action = MediaChangeReport::ABORT;
  switch ( reply )
  {
    case 0: /* abort */
      break;
    case 3: /* change url */
    {
      // translators: this is a prompt label, will appear as "New URI: "
      Url newurl( get_text( _("New URI") + std::string(": "), url.asString() ) );
      url = newurl;
    }
    // fall through
    case 1: /* retry */
      action = MediaChangeReport::RETRY;
      break;
    case 2: /* ignore */
      action = MediaChangeReport::IGNORE;
      break;
    default:
      WAR << "invalid prompt reply: " << reply << endl;
      break;
  }
  return action;
}


// ---------[ https ]--------------------------------------------------------

static MediaChangeReport::Action request_medium_https_handler( Zypper & zypper, Url & url )
{
  // https options
  // translators: a/r/i/u are replies to the "Abort, retry, ignore?" prompt.
  // Translate the a/r/i part exactly as you did the a/r/i string.
  // The 'u' reply means 'Change URI'.
  // https protocol-specific options:
  // 's' stands for Disable SSL certificate authority check
  PromptOptions popts( _("a/r/i/u/s"), 0 );
  set_common_option_help( popts );
  popts.setOptionHelp( 4, _("Disable SSL certificate authority check and continue.") );

  // translators: this is a prompt text
  zypper.out().prompt( PROMPT_ARI_MEDIA_PROBLEM, _("Abort, retry, ignore?"), popts );
  int reply = get_prompt_reply( zypper, PROMPT_ARI_MEDIA_PROBLEM, popts );

  MediaChangeReport::Action action;
  switch ( reply )
  {
    case 4: /* disable SSL */
      url.setQueryParam( "ssl_verify", "no" );
      zypper.out().info(_("SSL certificate authority check disabled.") );
      action = MediaChangeReport::RETRY;
      break;
    default:
      action = handle_common_options( reply, url );
  }
  return action;
}


// ---------[ cd/dvd ]---------------------------------------------------------

static void eject_drive_dialog( Zypper & zypper, Url & url, const std::vector<std::string> & devices, unsigned & index )
{
  bool cancel = false;
  if ( devices.empty() )
  {
    zypper.out().info(_("No devices detected, cannot eject.") );
    zypper.out().info(_("Try to eject the device manually.") );
  }
  if ( devices.size() == 1 )
  {
    MIL << "ejecting " << devices.front() << endl;
    media::MediaManager mm;
    media::MediaAccessId mid = mm.open( url );
    mm.release( mid, devices.front() );
  }
  else
  {
    zypper.out().info( _("Detected devices:") );
    int devn = 1;
    std::ostringstream numbers;
    for ( const std::string & device : devices )
    {
      // enhancement: we could try to get nicer device names like
      // "DVDRAM GSA-U10N on /dev/sr0"
      cout << devn << "  " << device << " " << endl;
      numbers << devn << "/";
      ++devn;
    }
    numbers << "c"; // c for cancel

    int devcount = devices.size();
    PromptOptions popts( numbers.str(), 0 );
    popts.setOptionHelp( devcount, _("Cancel") );

    zypper.out().prompt( PROMPT_MEDIA_EJECT, _("Select device to eject."), popts );
    int reply = get_prompt_reply( zypper, PROMPT_MEDIA_EJECT, popts );
    if ( reply == devcount )
      cancel = true;
    else
    {
      MIL << "ejecting " << devices[reply] << endl;
      media::MediaManager mm;
      media::MediaAccessId mid = mm.open( url );
      mm.release( mid, devices[reply] );
    }
  }

  if ( !cancel )
  {
    zypper.out().info(_("Insert the CD/DVD and press ENTER to continue.") );
    getchar();
  }
  zypper.out().info(_("Retrying...") );
}


static MediaChangeReport::Action request_medium_dvd_handler( Zypper & zypper,
                                                             Url & url,
                                                             const std::vector<std::string> & devices,
                                                             unsigned & index )
{
  // cd/dvd options
  // translators: a/r/i/u are replies to the "Abort, retry, ignore?" prompt.
  // Translate the a/r/i part exactly as you did the a/r/i string.
  // The 'u' reply means 'Change URI'.
  // cd/dvd protocol-specific options:
  // 'e' stands for Eject medium
  PromptOptions popts(_("a/r/i/u/e"), 0 );
  set_common_option_help( popts );
  popts.setOptionHelp( 4, _("Eject medium.") );

  // translators: this is a prompt text
  zypper.out().prompt( PROMPT_ARI_MEDIA_PROBLEM, _("Abort, retry, ignore?"), popts );
  int reply = get_prompt_reply( zypper, PROMPT_ARI_MEDIA_PROBLEM, popts );
  MediaChangeReport::Action action;
  switch ( reply )
  {
    case 4: /* eject medium */
      eject_drive_dialog( zypper, url, devices, index );
      action = MediaChangeReport::RETRY;
      break;
    default:
      action = handle_common_options( reply, url );
  }
  return action;
}

// ---------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////
namespace ZmartRecipients
{

  MediaChangeReport::Action MediaChangeReportReceiver::requestMedia( Url & url,
                                                                     unsigned mediumNr,
                                                                     const std::string & label,
                                                                     MediaChangeReport::Error error,
                                                                     const std::string & description,
                                                                     const std::vector<std::string> & devices,
                                                                     unsigned & index)
  {
    Zypper & zypper = Zypper::instance();

    DBG << "medium problem, url: " << url.asString()
        << ", error " << error
        << ", label '" << label << "', #" << mediumNr << endl;

    zypper.out().error( description );
    if ( url.schemeIsVolatile() )	// cd/dvd
    {
      // TranslatorExplanation translate letters 'y' and 'n' to whathever is appropriate for your language.
      // Try to check what answers does zypper accept (it always accepts y/n at least)
      // You can also have a look at the regular expressions used to check the answer here:
      // /usr/lib/locale/<your_locale>/LC_MESSAGES/SYS_LC_MESSAGES
      std::string request = str::Format(_("Please insert medium [%s] #%d and type 'y' to continue or 'n' to cancel the operation."))
                            % label % mediumNr;
      if ( read_bool_answer( PROMPT_YN_MEDIA_CHANGE, request, false ) )
      {
        zypper.requestExit( false );
        return MediaChangeReport::RETRY;
      }
      else
        return MediaChangeReport::ABORT;
    }

    if ( error == MediaChangeReport::IO_SOFT )
    {
      MediaChangeReport::Action default_action = MediaChangeReport::RETRY;
      if ( repeat_counter.counter_overrun( url ) )
        default_action = MediaChangeReport::ABORT;

      MediaChangeReport::Action action = (Action)read_action_ari_with_timeout( PROMPT_ARI_MEDIA_PROBLEM, 30, default_action );

      if ( action == MediaChangeReport::RETRY )
        zypper.requestExit( false );

      return action;
    }

    Action action = MediaChangeReport::ABORT;
    if ( url.getScheme() == "https" )
      action = request_medium_https_handler( zypper, url );
    else if ( url.schemeIsVolatile() )	// cd/dvd
      action = request_medium_dvd_handler( zypper, url, devices, index );
    else
    {
      // translators: a/r/i/u are replies to the "Abort, retry, ignore?" prompt
      // Translate the a/r/i part exactly as you did the a/r/i string.
      // the 'u' reply means 'Change URI'.
      PromptOptions popts(_("a/r/i/u"), 0 );
      set_common_option_help( popts );

      // translators: this is a prompt text
      zypper.out().prompt( PROMPT_ARI_MEDIA_PROBLEM, _("Abort, retry, ignore?"), popts );
      int reply = get_prompt_reply( zypper, PROMPT_ARI_MEDIA_PROBLEM, popts );

      action = handle_common_options( reply, url );
    }

    // if an rpm download failed and user chose to ignore that, advice to
    // run zypper verify afterwards
    if ( action == MediaChangeReport::IGNORE )
    {
      if ( zypper.runtimeData().action_rpm_download
        && !zypper.runtimeData().seen_verify_hint )
      {
        print_verify_hint( Zypper::instance().out() );
      }
      zypper.requestExit( false );
    }

    if ( action == MediaChangeReport::RETRY )
      zypper.requestExit( false );

    return action;
  }


  // ---------------------------------------------------------------------------
  // - AuthenticationReportReceiver
  // ---------------------------------------------------------------------------

  bool AuthenticationReportReceiver::prompt( const Url & url,
                                             const std::string & description,
                                             media::AuthData & auth_data )
  {
    Zypper & zypper = Zypper::instance();

    if ( geteuid() != 0 && url.getQueryString().find("credentials=") != std::string::npos )
    {
      std::string credfile( "/etc/zypp/credentials.d/" + url.getQueryParam("credentials") );
      zypper.out().warning( str::form(_("Authentication required to access %s. You need to be root to be able to read the credentials from %s."),
                                      url.asString().c_str(), credfile.c_str() ) );
    }

    if ( zypper.config().non_interactive )
    {
      MIL << "Non-interactive mode: aborting" << endl;
      return false;
    }

    // curl authentication
    media::CurlAuthData * curl_auth_data = dynamic_cast<media::CurlAuthData*>(&auth_data);

    if ( curl_auth_data )
      curl_auth_data->setAuthType( "basic,digest" );

    // user name

    std::string username;
    // expect the input from machine on stdin
    if ( zypper.config().machine_readable )
    {
      zypper.out().prompt( PROMPT_AUTH_USERNAME, _("User Name"), PromptOptions(), description );
      cin >> username;
    }
    // input from human using readline
    else
    {
      zypper.out().info( description, Out::QUIET );
      username = get_text(_("User Name") + std::string(": "), auth_data.username() );
    }
    if ( username.empty() )
      return false;
    auth_data.setUsername( username );

    // password

    zypper.out().prompt( PROMPT_AUTH_PASSWORD, _("Password"), PromptOptions() );

    std::string password;
    // expect the input from machine on stdin
    if ( zypper.config().machine_readable )
      cin >> password;
    else
      password = ::getpass( "" );
    if ( password.empty() )
      return false;
    auth_data.setPassword( password );

    return true;
  }

} //namespace ZmartRecipients
///////////////////////////////////////////////////////////////////
