/******************************************************************************
 * Copyright (c) 2000-2021 Ericsson Telecom AB
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
 *
 * Contributors:
 *   Baji, Laszlo
 *   Balasko, Jeno
 *   Baranyi, Botond
 *   Delic, Adam
 *   Forstner, Matyas
 *   Kovacs, Ferenc
 *   Raduly, Csaba
 *   Szabados, Kristof
 *   Szabo, Bence Janos
 *   Szabo, Janos Zoltan – initial implementation
 *   Szalai, Gabor
 *
 ******************************************************************************/
#include "../common/memory.h"
#include "datatypes.h"
#include "main.hh"
#include "encdec.h"

void def_encdec(const char *p_classname,
                char **p_classdef, char **p_classsrc,
                boolean ber, boolean raw, boolean text, boolean xer,
                boolean json, boolean oer, boolean is_leaf)
{
  char *def=NULL;
  char *src=NULL;

  def=mputstr
    (def,
     "void encode(const TTCN_Typedescriptor_t&, TTCN_Buffer&,"
     " int, ...) const;\n"
     "void decode(const TTCN_Typedescriptor_t&, TTCN_Buffer&,"
     " int, ...);\n");
  if(ber)
    def=mputstr(def,
     "ASN_BER_TLV_t* BER_encode_TLV(const TTCN_Typedescriptor_t& p_td,"
     " unsigned p_coding) const;\n"
     "boolean BER_decode_TLV(const TTCN_Typedescriptor_t& p_td, const"
     " ASN_BER_TLV_t& p_tlv, unsigned L_form);\n"
     );
  if(raw)
    def=mputprintf(def,
     "int RAW_encode(const TTCN_Typedescriptor_t&, RAW_enc_tree&) const;\n"
     "int RAW_decode(const TTCN_Typedescriptor_t&, TTCN_Buffer&,"
     " int, raw_order_t, boolean no_err=FALSE,"
     "int sel_field=-1, boolean first_call=TRUE, "
     "const RAW_Force_Omit* force_omit = NULL);\n"
     );
  if(text)
    def=mputprintf(def,
     "int TEXT_encode(const TTCN_Typedescriptor_t&, TTCN_Buffer&) const;\n"
     "int TEXT_decode(const TTCN_Typedescriptor_t&,"
     "TTCN_Buffer&, Limit_Token_List&, boolean no_err=FALSE,"
     "boolean first_call=TRUE);\n"
     );
  if (xer) /* XERSTUFF encdec function headers */
    def=mputprintf(def,
#ifndef NDEBUG
      "// written by %s in " __FILE__ " at %d\n"
#endif
      "int XER_encode(const XERdescriptor_t&, TTCN_Buffer&, unsigned int,"
      "unsigned int, int, embed_values_enc_struct_t*) const;\n"
      "int XER_decode(const XERdescriptor_t&, XmlReaderWrap&, unsigned int, "
      "unsigned int, embed_values_dec_struct_t*);\n"
      "static boolean can_start(const char *name, const char *uri, "
      "XERdescriptor_t const& xd, unsigned int, unsigned int);\n"
      "%s"
#ifndef NDEBUG
      , __FUNCTION__, __LINE__
#endif
      , use_runtime_2 ?
        "boolean can_start_v(const char *name, const char *uri, "
        "XERdescriptor_t const& xd, unsigned int, unsigned int);\n" : ""
    );
  if(json) {
    def = mputprintf(def,
      "int JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean) const;\n"
      "int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, "
      "boolean, int p_chosen_field = CHOSEN_FIELD_UNSET);\n");
  }
  if(oer) {
    def = mputprintf(def,
      "int OER_encode(const TTCN_Typedescriptor_t&, TTCN_Buffer&) const;\n"
      "int OER_decode(const TTCN_Typedescriptor_t&, TTCN_Buffer&, OER_struct&);\n");
  }

  src=mputprintf(src,
     "void %s::encode(const TTCN_Typedescriptor_t& p_td,"
     " TTCN_Buffer& p_buf, int p_coding, ...) const\n"
     "{\n"
     "  va_list pvar;\n"
     "  va_start(pvar, p_coding);\n"
     "  switch(p_coding) {\n"
     "  case TTCN_EncDec::CT_BER: {\n"
     "    TTCN_EncDec_ErrorContext ec(\"While BER-encoding type"
     " '%%s': \", p_td.name);\n"
     "    unsigned BER_coding=va_arg(pvar, unsigned);\n"
     "    BER_encode_chk_coding(BER_coding);\n"
     "    ASN_BER_TLV_t *tlv=BER_encode_TLV(p_td, BER_coding);\n"
     "    tlv->put_in_buffer(p_buf);\n"
     "    ASN_BER_TLV_t::destruct(tlv);\n"
     "    break;}\n"
     "  case TTCN_EncDec::CT_RAW: {\n"
     "    TTCN_EncDec_ErrorContext ec(\"While RAW-encoding type"
     " '%%s': \", p_td.name);\n"
     "    if(!p_td.raw)\n"
     "      TTCN_EncDec_ErrorContext::error_internal\n"
     "        (\"No RAW descriptor available for type '%%s'.\", p_td.name);\n"
     "    RAW_enc_tr_pos rp;\n"
     "    rp.level=0;\n"
     "    rp.pos=NULL;\n"
     "    RAW_enc_tree root(%s, NULL, &rp, 1, p_td.raw);\n"
     "    RAW_encode(p_td, root);\n"
     "    root.put_to_buf(p_buf);\n"
     "    break;}\n"
     "  case TTCN_EncDec::CT_TEXT: {\n"
     "    TTCN_EncDec_ErrorContext ec("
     "\"While TEXT-encoding type '%%s': \", p_td.name);\n"
     "    if(!p_td.text)\n"
     "      TTCN_EncDec_ErrorContext::error_internal\n"
     "      (\"No TEXT descriptor available for type '%%s'.\", p_td.name);\n"
     "    TEXT_encode(p_td,p_buf);\n"
     "    break;}\n"
     /* XERSTUFF encoder */
     "  case TTCN_EncDec::CT_XER: {\n"
     "    TTCN_EncDec_ErrorContext ec("
     "\"While XER-encoding type '%%s': \", p_td.name);\n"
     "    unsigned XER_coding=va_arg(pvar, unsigned);\n"
     "    XER_encode_chk_coding(XER_coding, p_td);\n"
      /* Do not use %s_xer_ here. It supplies the XER descriptor of oldtype
       * even if encoding newtype for:
       * <ttcn>type newtype oldtype;</ttcn> */
     "    XER_encode(*(p_td.xer),p_buf, XER_coding, 0, 0, 0);\n"
     "    p_buf.put_c('\\n');\n" /* make sure it has a newline */
     "    break;}\n"
     "  case TTCN_EncDec::CT_JSON: {\n"
     "    TTCN_EncDec_ErrorContext ec("
     "\"While JSON-encoding type '%%s': \", p_td.name);\n"
     "    if(!p_td.json)\n"
     "      TTCN_EncDec_ErrorContext::error_internal\n"
     "        (\"No JSON descriptor available for type '%%s'.\", p_td.name);\n"
     "    JSON_Tokenizer tok(va_arg(pvar, int) != 0);\n"
     "    JSON_encode(p_td, tok, FALSE);\n"
     "    p_buf.put_s(tok.get_buffer_length(), (const unsigned char*)tok.get_buffer());\n"
     "    break;}\n"
     "  case TTCN_EncDec::CT_OER: {\n"
     "    TTCN_EncDec_ErrorContext ec("
     "\"While OER-encoding type '%%s': \", p_td.name);\n"
     "    if(!p_td.oer)\n"
     "      TTCN_EncDec_ErrorContext::error_internal\n"
     "        (\"No OER descriptor available for type '%%s'.\", p_td.name);\n"
     "    OER_encode(p_td, p_buf);\n"
     "    break;}\n"
     "  default:\n"
     "    TTCN_error(\"Unknown coding method requested to encode"
     " type '%%s'\", p_td.name);\n"
     "  }\n"
     "  va_end(pvar);\n"
     "}\n"
     "\n"
    , p_classname, is_leaf?"TRUE":"FALSE"
    );

  src=mputprintf(src,
#ifndef NDEBUG
     "// written by %s in " __FILE__ " at %d\n"
#endif
     "void %s::decode(const TTCN_Typedescriptor_t& p_td,"
     " TTCN_Buffer& p_buf, int p_coding, ...)\n"
     "{\n"
     "  va_list pvar;\n"
     "  va_start(pvar, p_coding);\n"
     "  switch(p_coding) {\n"
     "  case TTCN_EncDec::CT_BER: {\n"
     "    TTCN_EncDec_ErrorContext ec(\"While BER-decoding type"
     " '%%s': \", p_td.name);\n"
     "    unsigned L_form=va_arg(pvar, unsigned);\n"
     "    ASN_BER_TLV_t tlv;\n"
     "    BER_decode_str2TLV(p_buf, tlv, L_form);\n"
     "    BER_decode_TLV(p_td, tlv, L_form);\n"
     "    if(tlv.isComplete) p_buf.increase_pos(tlv.get_len());\n"
     "    break;}\n"
     "  case TTCN_EncDec::CT_RAW: {\n"
     "    TTCN_EncDec_ErrorContext ec(\"While RAW-decoding"
     " type '%%s': \", p_td.name);\n"
     "    if(!p_td.raw)\n"
     "      TTCN_EncDec_ErrorContext::error_internal\n"
     "        (\"No RAW descriptor available for type '%%s'.\", p_td.name);\n"
     "    raw_order_t r_order;\n"
     "    switch(p_td.raw->top_bit_order) {\n"
     "    case TOP_BIT_LEFT:\n"
     "      r_order=ORDER_LSB;\n"
     "      break;\n"
     "    case TOP_BIT_RIGHT:\n"
     "    default:\n"
     "      r_order=ORDER_MSB;\n"
     "    }\n"
     "    int rawr = RAW_decode(p_td, p_buf, p_buf.get_len()*8, r_order);\n"
     "    if(rawr<0) switch (-rawr) {\n"
     "    case TTCN_EncDec::ET_INCOMPL_MSG:\n"
     "    case TTCN_EncDec::ET_LEN_ERR:\n"
     "      ec.error((TTCN_EncDec::error_type_t)-rawr, "
     "\"Can not decode type '%%s', because incomplete message was received\", "
     "p_td.name);\n"
     "      break;\n"
     "    case 1:\n" /* from the generic -1 return value */
     "    default:\n"
     "      ec.error(TTCN_EncDec::ET_INVAL_MSG, "
     "\"Can not decode type '%%s', because invalid "
     "message was received\", p_td.name);\n"
     "      break;\n"
     "    }\n"
     "    break;}\n"
     "  case TTCN_EncDec::CT_TEXT: {\n"
     "    Limit_Token_List limit;\n"
     "    TTCN_EncDec_ErrorContext ec(\"While TEXT-decoding type '%%s': \","
     " p_td.name);\n"
     "    if(!p_td.text)\n"
     "      TTCN_EncDec_ErrorContext::error_internal\n"
     "        (\"No TEXT descriptor available for type '%%s'.\", p_td.name);\n"
     "    const unsigned char *b_data=p_buf.get_data();\n"
     "    int null_added=0;\n"
     "    if(b_data[p_buf.get_len()-1]!='\\0'){\n"
     "      null_added=1;\n"
     "      p_buf.set_pos(p_buf.get_len());\n"
     "      p_buf.put_zero(8,ORDER_LSB);\n"
     "      p_buf.rewind();\n"
     "    }\n"
     "    if(TEXT_decode(p_td,p_buf,limit)<0)\n"
     "      ec.error(TTCN_EncDec::ET_INCOMPL_MSG,"
     "\"Can not decode type '%%s', because invalid or incomplete"
     " message was received\", p_td.name);\n"
     "    if(null_added){\n"
     "      size_t actpos=p_buf.get_pos();\n"
     "      p_buf.set_pos(p_buf.get_len()-1);\n"
     "      p_buf.cut_end();\n"
     "      p_buf.set_pos(actpos);\n"
     "    }\n"
     "    break;}\n"
     /* XERSTUFF decoder */
     "  case TTCN_EncDec::CT_XER: {\n"
     "    TTCN_EncDec_ErrorContext ec("
     "\"While XER-decoding type '%%s': \", p_td.name);\n"
     "    unsigned XER_coding=va_arg(pvar, unsigned);\n"
     "    XER_encode_chk_coding(XER_coding, p_td);\n"
     "    XmlReaderWrap reader(p_buf);\n"
     "    for (int rd_ok=reader.Read(); rd_ok==1; rd_ok=reader.Read()) {\n"
     "      if (reader.NodeType() == XML_READER_TYPE_ELEMENT) break;\n"
     "    }\n"
     "    XER_decode(*(p_td.xer), reader, XER_coding | XER_TOPLEVEL, XER_NONE, 0);\n"
     "    size_t bytes = reader.ByteConsumed();\n"
     "    p_buf.set_pos(bytes);\n"
     "    break;}\n"
     "  case TTCN_EncDec::CT_JSON: {\n"
     "    TTCN_EncDec_ErrorContext ec(\"While JSON-decoding type '%%s': \","
     " p_td.name);\n"
     "    if(!p_td.json)\n"
     "      TTCN_EncDec_ErrorContext::error_internal\n"
     "        (\"No JSON descriptor available for type '%%s'.\", p_td.name);\n"
     "    JSON_Tokenizer tok((const char*)p_buf.get_data(), p_buf.get_len());\n"
     "    if(JSON_decode(p_td, tok, FALSE, FALSE)<0)\n"
     "      ec.error(TTCN_EncDec::ET_INCOMPL_MSG,"
     "\"Can not decode type '%%s', because invalid or incomplete"
     " message was received\", p_td.name);\n"
     "    p_buf.set_pos(tok.get_buf_pos());\n"
     "    break;}\n"
     "  case TTCN_EncDec::CT_OER: {\n"
     "    TTCN_EncDec_ErrorContext ec(\"While OER-decoding type '%%s': \","
     " p_td.name);\n"
     "    if(!p_td.oer)\n"
     "      TTCN_EncDec_ErrorContext::error_internal\n"
     "        (\"No OER descriptor available for type '%%s'.\", p_td.name);\n"
    "     OER_struct p_oer;\n"
     "    OER_decode(p_td, p_buf, p_oer);\n"
     "    break;}\n"
     "  default:\n"
     "    TTCN_error(\"Unknown coding method requested to decode"
     " type '%%s'\", p_td.name);\n"
     "  }\n"
     "  va_end(pvar);\n"
     "}\n\n"
#ifndef NDEBUG
     , __FUNCTION__, __LINE__
#endif
     , p_classname);

  *p_classdef=mputstr(*p_classdef, def);
  Free(def);
  *p_classsrc=mputstr(*p_classsrc, src);
  Free(src);
}

char *genRawFieldChecker(char *src, const rawAST_coding_taglist *taglist,
  boolean is_equal)
{
  int i;
  for (i = 0; i < taglist->nElements; i++) {
    rawAST_coding_field_list *fields = taglist->fields + i;
    char *field_name = NULL;
    boolean first_expr = TRUE;
    int j;
    if (i > 0) src = mputstr(src, is_equal ? " || " : " && ");
    for (j = 0; j < fields->nElements; j++) {
      rawAST_coding_fields *field = fields->fields + j;
      if (j == 0) {
        /* this is the first field reference */
        if (field->fieldtype == UNION_FIELD)
          field_name = mputprintf(field_name,"(*field_%s)",field->nthfieldname);
        else
          field_name = mputprintf(field_name,"field_%s", field->nthfieldname);
      }
      else {
        /* this is not the first field reference */
        if (field->fieldtype == UNION_FIELD) {
          /* checking for the right selection within the union */
          if (first_expr) {
            if (taglist->nElements > 1) src = mputc(src, '(');
            first_expr = FALSE;
          }
          else src = mputstr(src, is_equal ? " && " : " || ");
          src = mputprintf(src, "%s.get_selection() %s %s%s%s", field_name,
            is_equal ? "==" : "!=", fields->fields[j - 1].type,
            "::ALT_", field->nthfieldname);
        }
        /* appending the current field name to the field reference */
        field_name = mputprintf(field_name, ".%s()", field->nthfieldname);
      }
      if (j < fields->nElements - 1 && field->fieldtype == OPTIONAL_FIELD) {
        /* this is not the last field in the chain and it is optional */
        if (first_expr) {
          if (taglist->nElements > 1) src = mputc(src, '(');
          first_expr = FALSE;
        }
        else src = mputstr(src, is_equal ? " && " : " || ");
        /* check for the presence */
        if (!is_equal) src = mputc(src, '!');
        src = mputprintf(src, "%s.ispresent()", field_name);
        /* add an extra () to the field reference */
        field_name = mputstr(field_name, "()");
      }
    }
    if (!first_expr) src = mputstr(src, is_equal ? " && " : " || ");
    /* compare the referred field with the given value */
    src = mputprintf(src, "%s %s %s", field_name, is_equal ? "==" : "!=",
      fields->value);
    if (!first_expr && taglist->nElements > 1) src = mputc(src, ')');
    Free(field_name);
  }
  return src;
}

char *genRawTagChecker(char *src, const rawAST_coding_taglist *taglist)
{
  int temp_tag, l;
  rawAST_coding_field_list temp_field;
  src = mputstr(src, "  RAW_enc_tree* temp_leaf;\n");
  for (temp_tag = 0; temp_tag < taglist->nElements; temp_tag++) {
    temp_field = taglist->fields[temp_tag];
    src = mputprintf(src, "  {\n"
      "  RAW_enc_tr_pos pr_pos%d;\n"
      "  pr_pos%d.level=myleaf.curr_pos.level+%d;\n"
      "  int new_pos%d[]={", temp_tag, temp_tag, temp_field.nElements, temp_tag);
    for (l = 0; l < temp_field.nElements; l++) {
      src= mputprintf(src, "%s%d", l ? "," : "", temp_field.fields[l].nthfield);
    }
    src = mputprintf(src, "};\n"
      "  pr_pos%d.pos=init_new_tree_pos(myleaf.curr_pos,%d,new_pos%d);\n"
      "  temp_leaf = myleaf.get_node(pr_pos%d);\n"
      "  if(temp_leaf != NULL){\n", temp_tag, temp_field.nElements, temp_tag, temp_tag);
    if (temp_field.value[0] != ' ') {
      src = mputprintf(src, "  %s new_val = %s;\n"
        "  new_val.RAW_encode(%s_descr_,*temp_leaf);\n",
        temp_field.fields[temp_field.nElements - 1].type, temp_field.value,
        temp_field.fields[temp_field.nElements - 1].typedescr);
    }
    else {
      src = mputprintf(src, "  %s.RAW_encode(%s_descr_,*temp_leaf);\n",
        temp_field.value, temp_field.fields[temp_field.nElements - 1].typedescr);
    }
    src = mputstr(src, "  } else");
  }
  src = mputstr(src, " {\n"
    "    TTCN_EncDec_ErrorContext::error\n"
    "      (TTCN_EncDec::ET_OMITTED_TAG, \"Encoding a tagged, but omitted"
    " value.\");\n"
    "  }\n");
  for (temp_tag = taglist->nElements - 1; temp_tag >= 0 ; temp_tag--) {
    src = mputprintf(src, "  free_tree_pos(pr_pos%d.pos);\n"
      "  }\n", temp_tag);
  }
  return src;
}
