/***************************************************************************
 *                                  _   _ ____  _
 *  Project                     ___| | | |  _ \| |
 *                             / __| | | | |_) | |
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
 * are also available at https://curl.se/docs/copyright.html.
 *
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
 * copies of the Software, and permit persons to whom the Software is
 * furnished to do so, under the terms of the COPYING file.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 * SPDX-License-Identifier: curl
 *
 ***************************************************************************/
#include "tool_setup.h"

#include "slist_wc.h"

#ifndef CURL_DISABLE_LIBCURL_OPTION

#include "tool_cfgable.h"
#include "tool_easysrc.h"
#include "tool_msgs.h"

/* global variable definitions, for easy-interface source code generation */

struct slist_wc *easysrc_decl; /* Variable declarations */
struct slist_wc *easysrc_data; /* Build slists, forms etc. */
struct slist_wc *easysrc_code; /* Setopt calls */
struct slist_wc *easysrc_toohard; /* Unconvertible setopt */
struct slist_wc *easysrc_clean;  /* Clean up allocated data */
int easysrc_mime_count;
int easysrc_slist_count;
static bool easysrc_performed;

static const char * const srchead[] = {
  "/********* Sample code generated by the curl command line tool **********",
  " * All curl_easy_setopt() options are documented at:",
  " * https://curl.se/libcurl/c/curl_easy_setopt.html",
  " ************************************************************************/",
  "#include <curl/curl.h>",
  "",
  "int main(int argc, char *argv[])",
  "{",
  "  CURLcode result;",
  "  CURL *curl;",
  NULL
};
/* easysrc_decl declarations come here */
/* easysrc_data initialization come here */
/* easysrc_code statements come here */
static const char * const srchard[] = {
  "/* Here is a list of options the curl code used that cannot get generated",
  "   as source easily. You may choose to either not use them or implement",
  "   them yourself.",
  "",
  NULL
};
static const char * const srcend[] = {
  "",
  "  return (int)result;",
  "}",
  "/**** End of sample code ****/",
  NULL
};

/* Clean up all source code if we run out of memory */
static void easysrc_free(void)
{
  slist_wc_free_all(easysrc_decl);
  easysrc_decl = NULL;
  slist_wc_free_all(easysrc_data);
  easysrc_data = NULL;
  slist_wc_free_all(easysrc_code);
  easysrc_code = NULL;
  slist_wc_free_all(easysrc_toohard);
  easysrc_toohard = NULL;
  slist_wc_free_all(easysrc_clean);
  easysrc_clean = NULL;
}

/* Add a source line to the main code or remarks */
CURLcode easysrc_add(struct slist_wc **plist, const char *line)
{
  CURLcode result = CURLE_OK;
  struct slist_wc *list = slist_wc_append(*plist, line);
  if(!list) {
    easysrc_free();
    result = CURLE_OUT_OF_MEMORY;
  }
  else
    *plist = list;
  return result;
}

CURLcode easysrc_addf(struct slist_wc **plist, const char *fmt, ...)
{
  CURLcode result;
  char *bufp;
  va_list ap;
  va_start(ap, fmt);
  bufp = curl_mvaprintf(fmt, ap);
  va_end(ap);
  if(!bufp) {
    result = CURLE_OUT_OF_MEMORY;
  }
  else {
    result = easysrc_add(plist, bufp);
    curl_free(bufp);
  }
  return result;
}

CURLcode easysrc_init(void)
{
  easysrc_performed = FALSE;
  return easysrc_add(&easysrc_code, "curl = curl_easy_init();");
}

bool easysrc_is_first(void)
{
  return !easysrc_performed;
}

CURLcode easysrc_perform(void)
{
  CURLcode result = CURLE_OK;
  /* Note any setopt calls which we could not convert */
  if(easysrc_toohard) {
    int i;
    struct curl_slist *ptr;
    result = easysrc_add(&easysrc_code, "");
    /* Preamble comment */
    for(i = 0; srchard[i] && !result; i++)
      result = easysrc_add(&easysrc_code, srchard[i]);
    /* Each unconverted option */
    if(easysrc_toohard && !result) {
      for(ptr = easysrc_toohard->first; ptr && !result; ptr = ptr->next)
        result = easysrc_add(&easysrc_code, ptr->data);
    }
    if(!result)
      result = easysrc_add(&easysrc_code, "");
    if(!result)
      result = easysrc_add(&easysrc_code, "*/");

    slist_wc_free_all(easysrc_toohard);
    easysrc_toohard = NULL;
  }

  if(!result)
    result = easysrc_add(&easysrc_code, "");
  if(!result)
    result = easysrc_add(&easysrc_code, "result = curl_easy_perform(curl);");
  if(!result)
    result = easysrc_add(&easysrc_code, "");
  if(!result)
    easysrc_performed = TRUE;

  return result;
}

CURLcode easysrc_cleanup(void)
{
  CURLcode result = easysrc_add(&easysrc_code, "curl_easy_cleanup(curl);");
  if(!result)
    result = easysrc_add(&easysrc_code, "curl = NULL;");

  return result;
}

void dumpeasysrc(void)
{
  struct curl_slist *ptr;
  char *o = global->libcurl;

  FILE *out;
  bool fopened = FALSE;
  if(strcmp(o, "-")) {
    out = curlx_fopen(o, FOPEN_WRITETEXT);
    fopened = TRUE;
  }
  else
    out = stdout;
  if(!out)
    warnf("Failed to open %s to write libcurl code", o);
  else {
    int i;
    const char *c;

    for(i = 0; ((c = srchead[i]) != NULL); i++)
      curl_mfprintf(out, "%s\n", c);

    /* Declare variables used for complex setopt values */
    if(easysrc_decl) {
      for(ptr = easysrc_decl->first; ptr; ptr = ptr->next)
        curl_mfprintf(out, "  %s\n", ptr->data);
    }

    /* Set up complex values for setopt calls */
    if(easysrc_data) {
      curl_mfprintf(out, "\n");

      for(ptr = easysrc_data->first; ptr; ptr = ptr->next)
        curl_mfprintf(out, "  %s\n", ptr->data);
    }

    curl_mfprintf(out, "\n");
    if(easysrc_code) {
      for(ptr = easysrc_code->first; ptr; ptr = ptr->next) {
        if(ptr->data[0]) {
          curl_mfprintf(out, "  %s\n", ptr->data);
        }
        else {
          curl_mfprintf(out, "\n");
        }
      }
    }

    if(easysrc_clean) {
      for(ptr = easysrc_clean->first; ptr; ptr = ptr->next)
        curl_mfprintf(out, "  %s\n", ptr->data);
    }

    for(i = 0; ((c = srcend[i]) != NULL); i++)
      curl_mfprintf(out, "%s\n", c);

    if(fopened)
      curlx_fclose(out);
  }

  easysrc_free();
}

#endif /* CURL_DISABLE_LIBCURL_OPTION */
