/*
  This file is part of TALER
  Copyright (C) 2018 Taler Systems SA
  TALER is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 3, or (at your
  option) any later version.
  TALER is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  General Public License for more details.
  You should have received a copy of the GNU General Public
  License along with TALER; see the file COPYING.  If not, see
  
*/
/**
 * @file exchange-lib/testing_api_cmd_wire.c
 * @brief command for testing /wire.
 * @author Marcello Stanisci
 */
#include "platform.h"
#include "taler_json_lib.h"
#include 
#include "exchange_api_handle.h"
#include "taler_testing_lib.h"
struct WireState
{
  /**
   * Handle to the /wire operation.
   */
  struct TALER_EXCHANGE_WireHandle *wh;
  /**
   * Which wire-method we expect are offered by the exchange.
   */
  const char *expected_method;
  /**
   * Flag indicating if the expected method is actually
   * offered.
   */
  unsigned int method_found;
  /**
   * Fee we expect is charged for this wire-transfer method.
   */
  const char *expected_fee;
  /**
   * Expected HTTP response code.
   */
  unsigned int expected_response_code;
  /**
   * Interpreter state.
   */
  struct TALER_TESTING_Interpreter *is;
  /**
   * Connection to the exchange.
   */
  struct TALER_EXCHANGE_Handle *exchange;
};
/**
 * Check all the expected values have been returned by /wire.
 *
 * @param cls closure
 * @param wire_method name of the wire method (i.e. "sepa")
 * @param fees fee structure for this method
 */
static void
check_method_and_fee_cb
  (void *cls,
   const char *wire_method,
   const struct TALER_EXCHANGE_WireAggregateFees *fees);
/**
 * Callbacks called with the result(s) of a wire format inquiry
 * request to the exchange.
 *
 * @param cls closure with the interpreter state
 * @param http_status HTTP response code, #MHD_HTTP_OK (200)
 *                    for successful request; 0 if the exchange's
 *                    reply is bogus (fails to follow the protocol)
 * @param ec taler-specific error code, #TALER_EC_NONE on success
 * @param obj the received JSON reply, if successful this should
 *            be the wire format details as provided by /wire.
 */
static void
wire_cb (void *cls,
         unsigned int http_status,
	 enum TALER_ErrorCode ec,
         const json_t *obj)
{
  struct WireState *ws = cls;
  struct TALER_TESTING_Command *cmd
    = &ws->is->commands[ws->is->ip];
  /**
   * The handle has been free'd by GNUnet curl-lib. FIXME:
   * shouldn't GNUnet nullify it once it frees it?
   */
  ws->wh = NULL;
  if (ws->expected_response_code != http_status)
  {
    GNUNET_break (0);
    TALER_TESTING_interpreter_fail (ws->is);
    return;
  }
  if (GNUNET_OK != TALER_EXCHANGE_wire_get_fees
       (&TALER_EXCHANGE_get_keys (ws->exchange)->master_pub,
        obj,
        // will check synchronously.
        &check_method_and_fee_cb,
        ws))
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Wire fee extraction in command %s failed\n",
                cmd->label);
    json_dumpf (obj, stderr, 0);
    TALER_TESTING_interpreter_fail (ws->is);
    return;
  }
  if (ws->method_found != GNUNET_OK)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "/wire does not offer method '%s'\n",
                ws->expected_method);
    TALER_TESTING_interpreter_fail (ws->is);
    return;
  }
  TALER_TESTING_interpreter_next (ws->is);
}
/**
 * Check all the expected values have been returned by /wire.
 *
 * @param cls closure
 * @param wire_method name of the wire method (i.e. "sepa")
 * @param fees fee structure for this method
 */
static void
check_method_and_fee_cb
  (void *cls,
   const char *wire_method,
   const struct TALER_EXCHANGE_WireAggregateFees *fees)
{
  struct WireState *ws = cls;
  struct TALER_TESTING_Command *cmd
    = &ws->is->commands[ws->is->ip]; // ugly?
  struct TALER_Amount expected_fee;
  if (0 == strcmp (ws->expected_method, wire_method))
    ws->method_found = GNUNET_OK;
  if ( ws->expected_fee && (ws->method_found == GNUNET_OK) )
  {
    GNUNET_assert (GNUNET_OK == TALER_string_to_amount
                     (ws->expected_fee,
                      &expected_fee));
    while (NULL != fees)
    {
      if (0 != TALER_amount_cmp (&fees->wire_fee,
                                 &expected_fee))
      {
        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                    "Wire fee missmatch to command %s\n",
                    cmd->label);
        TALER_TESTING_interpreter_fail (ws->is);
        return;
      }
      fees = fees->next;
    }
  }
}
/**
 * Run the command.
 *
 * @param cls closure, typically a #struct WireState.
 * @param cmd the command to execute, a /wire one.
 * @param i the interpreter state.
 */
void
wire_run (void *cls,
          const struct TALER_TESTING_Command *cmd,
          struct TALER_TESTING_Interpreter *i)
{
  struct WireState *ws = cls;
  ws->is = i;
  ws->wh = TALER_EXCHANGE_wire (ws->exchange,
                                &wire_cb,
                                ws);
}
/**
 * Cleanup the state.
 *
 * @param cls closure, typically a #struct WireState.
 * @param cmd the command which is being cleaned up.
 */
void
wire_cleanup (void *cls,
              const struct TALER_TESTING_Command *cmd)
{
  struct WireState *ws = cls;
  if (NULL != ws->wh)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                "Command %u (%s) did not complete\n",
                ws->is->ip,
                cmd->label);
    TALER_EXCHANGE_wire_cancel (ws->wh);
    ws->wh = NULL;
  }
  GNUNET_free (ws);
}
/**
 * Create a /wire command.
 *
 * @param label the command label.
 * @param exchange the exchange to connect to.
 * @param expected_method which wire-transfer method is expected
 *        to be offered by the exchange.
 * @param expected_fee the fee the exchange should charge.
 * @param expected_response_code the HTTP response the exchange
 *        should return.
 *
 * @return the command to be executed by the interpreter.
 */
struct TALER_TESTING_Command
TALER_TESTING_cmd_wire (const char *label,
                        struct TALER_EXCHANGE_Handle *exchange,
                        const char *expected_method,
                        const char *expected_fee,
                        unsigned int expected_response_code)
{
  struct TALER_TESTING_Command cmd;
  struct WireState *ws;
  ws = GNUNET_new (struct WireState);
  ws->exchange = exchange;
  ws->expected_method = expected_method;
  ws->expected_fee = expected_fee;
  ws->expected_response_code = expected_response_code;
  cmd.cls = ws;
  cmd.label = label;
  cmd.run = &wire_run;
  cmd.cleanup = &wire_cleanup;
  return cmd;
}