/* GNU Guix --- Functional package management for GNU
   Copyright © 2020 Maxime Devos <maxime.devos@student.kuleuven.be>

   This file is part of GNU Guix.

   GNU Guix 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 of the License, or (at
   your option) any later version.

   GNU Guix 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 GNU Guix.  If not, see <http://www.gnu.org/licenses/>. */

/**
 * @brief Client-side API to the rehash service
 * @author Maxime Devos
 */

#include <stdio.h>
#include <gnunet/gnunet_config.h>
#include <gnunet/gnunet_service_lib.h>
#include <gnunet/gnunet_dht_service.h>
#include <gnunet/gnunet_mq_lib.h>

#include "rehash_service.h"
#include "extra_gnunet_protocols.h"
#include "rehash.h"

struct GNUNET_REHASH_Handle
{
  const struct GNUNET_CONFIGURATION_Handle *cfg;
  struct GNUNET_MQ_Handle *mq;
};

static int
check_client_result(void *cls, const struct GNUNET_REHASH_ResultMessage *msg)
{
  if (msg->output_length == msg->header.size - sizeof(*msg))
    return GNUNET_OK;
  else
    return GNUNET_SYSERR;
};

static void
handle_client_result(void *cls, const struct GNUNET_REHASH_ResultMessage *msg)
{
  /* TODO call callbacks */
  GNUNET_assert (0);
};

static void
mq_error_handler (void *h, enum GNUNET_MQ_Error e)
{
  /* FIXME! */
  GNUNET_assert (0);
}

struct GNUNET_REHASH_Handle *
GNUNET_REHASH_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
{
  struct GNUNET_REHASH_Handle *handle;
  handle = GNUNET_new (struct GNUNET_REHASH_Handle);
  struct GNUNET_MQ_MessageHandler handlers[] = {
    GNUNET_MQ_hd_var_size (client_result,
                           GNUNET_MESSAGE_TYPE_REHASH_CLIENT_RESULT,
                           struct GNUNET_REHASH_ResultMessage,
                           handle),
    GNUNET_MQ_handler_end ()
  };
  handle->cfg = cfg;
  handle->mq = GNUNET_CLIENT_connect (cfg,
				      "rehash",
				      handlers,
				      &mq_error_handler,
				      handle);
  if (NULL == handle->mq)
  {
    GNUNET_free (handle);
    return NULL;
  }
  return handle;
}

void
GNUNET_REHASH_disconnect (struct GNUNET_REHASH_Handle *h)
{
  GNUNET_MQ_destroy (h->mq);
  GNUNET_free (h);
}

void
GNUNET_REHASH_store_abort (struct GNUNET_REHASH_StoreContext *cls)
{
  /* TODO stub */
}

struct GNUNET_REHASH_QueryContext *
GNUNET_REHASH_query_start (struct GNUNET_REHASH_Handle *h,
                           enum GNUNET_FS_SearchOptions options,
                           uint32_t anonymity,
                           enum GNUNET_REHASH_Hash_Type in_type,
                           enum GNUNET_REHASH_Hash_Type out_type,
                           const char *input,
                           size_t input_length,
                           GNUNET_REHASH_QueryContinuation cont,
                           void *cls)
{
  /* TODO stub */
  struct GNUNET_REHASH_GetMessage *msg;
  struct GNUNET_MQ_Envelope *ev;
  char *msg_input;

  /* TODO proper error messages, less magic */
  /* Prevent buffer overflows! */
  GNUNET_assert (input_length <= 64);

  ev = GNUNET_MQ_msg_extra (msg, input_length, GNUNET_MESSAGE_TYPE_REHASH_CLIENT_GET);
  msg->options = htonl (options);
  msg->anonymity_level = htonl (anonymity);
  msg->in_type = htonl (out_type);
  msg->out_type = htonl (out_type);
  msg->input_length = htonl ((uint32_t) input_length);
  msg_input = (char *) &msg[1];
  memcpy (msg_input, input, input_length);
  GNUNET_MQ_send (h->mq, ev);

  /* TODO allow abort */
  return NULL;
}

struct GNUNET_REHASH_StoreContext *
GNUNET_REHASH_store_start (struct GNUNET_REHASH_Handle *h,
		           const struct GNUNET_FS_BlockOptions *options,
                           enum GNUNET_REHASH_Hash_Type in_type,
                           enum GNUNET_REHASH_Hash_Type out_type,
                           const char *input,
                           size_t input_length,
                           const char *output,
                           size_t output_length,
                           GNUNET_REHASH_StoreContinuation cont,
                           void *cls)
{
  struct GNUNET_REHASH_PutMessage *msg;
  struct GNUNET_MQ_Envelope *ev;

  /* TODO proper error messages, less magic */
  /* Prevent buffer overflows! */
  GNUNET_assert (input_length <= 64);
  GNUNET_assert (output_length <= 64);

  ev = GNUNET_MQ_msg_extra (msg, input_length + output_length, GNUNET_MESSAGE_TYPE_REHASH_CLIENT_PUT);
  msg->expiration_time = GNUNET_TIME_absolute_hton(options->expiration_time);
  msg->anonymity_level = htonl (options->anonymity_level);
  msg->content_priority = htonl (options->content_priority);
  msg->replication_level = htonl (options->replication_level);
  msg->in_type = htonl (in_type);
  msg->in_type = htonl (out_type);
  msg->input_length = htonl ((uint32_t) input_length);
  msg->output_length = htonl ((uint32_t) output_length);

  GNUNET_MQ_send (h->mq, ev);
  /* TODO: check memory allocation / freeing */
  /* TODO: progress / abort / ... */
  return NULL;
}
