/*
  This file is part of Anastasis
  Copyright (C) 2020, 2021, 2022 Anastasis SARL

  Anastasis 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.

  Anastasis 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
  Anastasis; see the file COPYING.GPL.  If not, see <http://www.gnu.org/licenses/>
*/
/**
 * @file reducer/anastasis_api_redux_free.c
 * @brief release a parsed reducer state
 * @author Christian Grothoff
 *
 * Every structure here is allocated zeroed (GNUNET_new(_array)) and every
 * array's length field is set before the array is filled, so this code also
 * has to cope with partially initialized states: the parser simply hands a
 * half-built state to ANASTASIS_REDUX_state_free_() when it gives up.
 */
#include "platform.h"
#include "anastasis_redux.h"
#include "anastasis_api_redux.h"
#include "anastasis_api_redux_state.h"


void
ANASTASIS_REDUX_provider_url_clear_ (struct ANASTASIS_ProviderUrl *pu)
{
  GNUNET_free (pu->url);
}


void
ANASTASIS_REDUX_provider_config_clear_ (struct ANASTASIS_ReduxProviderConfig *
                                        cfg
                                        )
{
  for (unsigned int j = 0; j < cfg->methods_len; j++)
    GNUNET_free (cfg->methods[j].type);
  GNUNET_free (cfg->methods);
  GNUNET_free (cfg->business_name);
  GNUNET_free (cfg->currency);
  memset (cfg,
          0,
          sizeof (*cfg));
}


void
ANASTASIS_REDUX_providers_clear_ (struct ANASTASIS_ReduxCommon *common)
{
  for (unsigned int i = 0; i < common->providers_len; i++)
  {
    struct ANASTASIS_ReduxProvider *p = &common->providers[i];

    ANASTASIS_REDUX_provider_url_clear_ (&p->url);
    if (p->have_config)
      ANASTASIS_REDUX_provider_config_clear_ (&p->config);
  }
  GNUNET_free (common->providers);
  common->providers = NULL;
  common->providers_len = 0;
}


void
ANASTASIS_REDUX_continents_clear_ (struct ANASTASIS_ReduxCommon *common)
{
  for (unsigned int i = 0; i < common->continents_len; i++)
  {
    struct ANASTASIS_ReduxContinent *c = &common->continents[i];

    GNUNET_free (c->name);
    json_decref (c->name_i18n);
  }
  GNUNET_free (common->continents);
  common->continents = NULL;
  common->continents_len = 0;
}


void
ANASTASIS_REDUX_countries_clear_ (struct ANASTASIS_ReduxCommon *common)
{
  for (unsigned int i = 0; i < common->countries_len; i++)
  {
    struct ANASTASIS_ReduxCountry *c = &common->countries[i];

    GNUNET_free (c->code);
    GNUNET_free (c->name);
    GNUNET_free (c->continent);
    GNUNET_free (c->call_code);
    GNUNET_free (c->currency);
    json_decref (c->name_i18n);
    json_decref (c->continent_i18n);
  }
  GNUNET_free (common->countries);
  common->countries = NULL;
  common->countries_len = 0;
}


void
ANASTASIS_REDUX_required_attributes_clear_ (
  struct ANASTASIS_ReduxCommon *common)
{
  for (unsigned int i = 0; i < common->required_attributes_len; i++)
  {
    struct ANASTASIS_ReduxAttributeSpec *a = &common->required_attributes[i];

    GNUNET_free (a->type);
    GNUNET_free (a->name);
    GNUNET_free (a->label);
    GNUNET_free (a->widget);
    GNUNET_free (a->uuid);
    GNUNET_free (a->tooltip);
    GNUNET_free (a->validation_regex);
    GNUNET_free (a->validation_logic);
    GNUNET_free (a->autocomplete);
    json_decref (a->label_i18n);
  }
  GNUNET_free (common->required_attributes);
  common->required_attributes = NULL;
  common->required_attributes_len = 0;
}


/**
 * Free the contents of @a common.
 *
 * @param[in,out] common state to free
 */
static void
free_common (struct ANASTASIS_ReduxCommon *common)
{
  ANASTASIS_REDUX_continents_clear_ (common);
  ANASTASIS_REDUX_countries_clear_ (common);
  ANASTASIS_REDUX_required_attributes_clear_ (common);
  ANASTASIS_REDUX_providers_clear_ (common);
  for (unsigned int i = 0; i < common->currencies_len; i++)
    GNUNET_free (common->currencies[i]);
  GNUNET_free (common->currencies);
  json_decref (common->identity_attributes);
  GNUNET_free (common->selected_continent);
  GNUNET_free (common->selected_country);
}


/**
 * Free the contents of @a backup.
 *
 * @param[in,out] backup state to free
 */
void
ANASTASIS_REDUX_policy_clear_ (struct ANASTASIS_ReduxPolicy *p)
{
  for (unsigned int j = 0; j < p->methods_len; j++)
  {
    struct ANASTASIS_ReduxPolicyMethod *pm = &p->methods[j];

    ANASTASIS_REDUX_provider_url_clear_ (&pm->provider);
    if (NULL != pm->truth)
      ANASTASIS_truth_free (pm->truth);
  }
  GNUNET_free (p->methods);
  p->methods = NULL;
  p->methods_len = 0;
}


void
ANASTASIS_REDUX_policies_clear_ (struct ANASTASIS_ReduxBackup *backup)
{
  for (unsigned int i = 0; i < backup->policies_len; i++)
    ANASTASIS_REDUX_policy_clear_ (&backup->policies[i]);
  GNUNET_free (backup->policies);
  backup->policies = NULL;
  backup->policies_len = 0;
}


void
ANASTASIS_REDUX_policy_providers_clear_ (struct ANASTASIS_ReduxBackup *backup)
{
  for (unsigned int i = 0; i < backup->policy_providers_len; i++)
    ANASTASIS_REDUX_provider_url_clear_ (
      &backup->policy_providers[i].provider_url);
  GNUNET_free (backup->policy_providers);
  backup->policy_providers = NULL;
  backup->policy_providers_len = 0;
}


void
ANASTASIS_REDUX_payments_clear_ (struct ANASTASIS_ReduxBackup *backup)
{
  for (unsigned int i = 0; i < backup->payments_len; i++)
    GNUNET_free (backup->payments[i]);
  GNUNET_free (backup->payments);
  backup->payments = NULL;
  backup->payments_len = 0;
  backup->have_payments = false;
}


void
ANASTASIS_REDUX_policy_payment_requests_clear_ (
  struct ANASTASIS_ReduxBackup *backup)
{
  for (unsigned int i = 0; i < backup->policy_payment_requests_len; i++)
  {
    struct ANASTASIS_ReduxPolicyPaymentRequest *ppr
      = &backup->policy_payment_requests[i];

    GNUNET_free (ppr->payto);
    ANASTASIS_REDUX_provider_url_clear_ (&ppr->provider);
  }
  GNUNET_free (backup->policy_payment_requests);
  backup->policy_payment_requests = NULL;
  backup->policy_payment_requests_len = 0;
  backup->have_policy_payment_requests = false;
}


static void
free_backup (struct ANASTASIS_ReduxBackup *backup)
{
  for (unsigned int i = 0; i < backup->authentication_methods_len; i++)
  {
    struct ANASTASIS_ReduxAuthMethod *am = &backup->authentication_methods[i];

    GNUNET_free (am->type);
    GNUNET_free (am->instructions);
    GNUNET_free (am->challenge);
    GNUNET_free (am->mime_type);
  }
  GNUNET_free (backup->authentication_methods);
  ANASTASIS_REDUX_policies_clear_ (backup);
  ANASTASIS_REDUX_policy_providers_clear_ (backup);
  GNUNET_free (backup->upload_fees);
  ANASTASIS_REDUX_payments_clear_ (backup);
  ANASTASIS_REDUX_policy_payment_requests_clear_ (backup);
  for (unsigned int i = 0; i < backup->success_details_len; i++)
    ANASTASIS_REDUX_provider_url_clear_ (
      &backup->success_details[i].provider_url);
  GNUNET_free (backup->success_details);
  json_decref (backup->core_secret);
  GNUNET_free (backup->secret_name);
}


/**
 * Free the variant-specific contents of @a fb, but not @a fb itself.
 *
 * @param[in,out] fb feedback to reset
 */
void
ANASTASIS_REDUX_feedback_clear_ (struct ANASTASIS_ReduxFeedback *fb)
{
  GNUNET_free (fb->display_hint);
  switch (fb->status)
  {
  case ANASTASIS_RFS_CODE_IN_FILE:
    GNUNET_free (fb->details.code_in_file.filename);
    break;
  case ANASTASIS_RFS_SEND_TO_ADDRESS:
    GNUNET_free (fb->details.send_to_address.address_hint);
    break;
  case ANASTASIS_RFS_TALER_PAYMENT:
    GNUNET_free (fb->details.taler_payment.taler_pay_uri);
    ANASTASIS_REDUX_provider_url_clear_ (&fb->details.taler_payment.provider);
    break;
  case ANASTASIS_RFS_IBAN_INSTRUCTIONS:
    GNUNET_free (fb->details.iban_instructions.target_iban);
    GNUNET_free (fb->details.iban_instructions.target_business_name);
    GNUNET_free (fb->details.iban_instructions.wire_transfer_subject);
    break;
  case ANASTASIS_RFS_SERVER_FAILURE:
  case ANASTASIS_RFS_TRUTH_UNKNOWN:
  case ANASTASIS_RFS_SOLVED:
  case ANASTASIS_RFS_INCORRECT_ANSWER:
  case ANASTASIS_RFS_RATE_LIMIT_EXCEEDED:
    /* nothing heap-allocated beyond the display hint */
    break;
  }
  memset (&fb->details,
          0,
          sizeof (fb->details));
  fb->display_hint = NULL;
}


void
ANASTASIS_REDUX_recovery_info_free_ (struct ANASTASIS_ReduxRecoveryInfo *ri)
{
  if (NULL == ri)
    return;
  for (unsigned int i = 0; i < ri->challenges_len; i++)
  {
    GNUNET_free (ri->challenges[i].type);
    GNUNET_free (ri->challenges[i].instructions);
    GNUNET_free (ri->challenges[i].answer);
  }
  GNUNET_free (ri->challenges);
  for (unsigned int i = 0; i < ri->policies_len; i++)
    GNUNET_free (ri->policies[i].uuids);
  GNUNET_free (ri->policies);
  GNUNET_free (ri->secret_name);
  ANASTASIS_REDUX_provider_url_clear_ (&ri->provider_url);
  GNUNET_free (ri);
}


/**
 * Free the contents of @a recovery.
 *
 * @param[in,out] recovery state to free
 */
static void
free_recovery (struct ANASTASIS_ReduxRecovery *recovery)
{
  if (NULL != recovery->r)
    ANASTASIS_recovery_abort (recovery->r);
  ANASTASIS_REDUX_recovery_info_free_ (recovery->ri);
  for (unsigned int i = 0; i < recovery->challenge_feedback_len; i++)
    ANASTASIS_REDUX_feedback_clear_ (&recovery->challenge_feedback[i]);
  GNUNET_free (recovery->challenge_feedback);
  json_decref (recovery->core_secret);
}


void
ANASTASIS_REDUX_state_free_ (struct ANASTASIS_ReduxState *rs)
{
  if (NULL == rs)
    return;
  switch (rs->type)
  {
  case ANASTASIS_RT_BACKUP:
    free_backup (&rs->details.backup);
    free_common (&rs->common);
    break;
  case ANASTASIS_RT_RECOVERY:
    free_recovery (&rs->details.recovery);
    free_common (&rs->common);
    break;
  case ANASTASIS_RT_ERROR:
    GNUNET_free (rs->details.error.hint);
    GNUNET_free (rs->details.error.detail);
    break;
  }
  GNUNET_free (rs);
}


/* end of anastasis_api_redux_free.c */
