/******************************************************************************
 * Copyright (c) 2000-2025 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:
 *   Balasko, Jeno
 *   Delic, Adam
 *   Raduly, Csaba
 *
 ******************************************************************************/
module EmbedValues {
modulepar boolean EmbedValues_verbose := false;
#define verbose EmbedValues_verbose
#include "../macros.ttcnin"

const charstring LF  := "\n";

type record Notification {
    record length(4) of universal charstring embed,
    integer account,
    integer amount_due,
    charstring payable_by
}
with {
    variant "embedValues"
    variant (payable_by) "name as 'payable-by'"
    variant (payable_by) "XSD:date"
    variant (amount_due) "name as 'amount-due'"
}

type record NotificationU {
    record length(4) of universal charstring embed,
    integer account,
    integer amount_due,
    charstring payable_by
}
with {
    variant "embedValues"
    variant (payable_by) "name as 'payable-by'"
    variant (payable_by) "XSD:date"
    variant (amount_due) "name as 'amount-due'"
}

type component Embedder {}

DECLARE_XER_ENCODERS(Notification, notif);
DECLARE_EXER_ENCODERS(Notification, notif);

DECLARE_XER_ENCODERS(NotificationU, notifU);
DECLARE_EXER_ENCODERS(NotificationU, notifU);

const Notification inputS := {
    embed := {
        "Please note the following details:",
        "(your business account)",
        "This is in excess of your normal monthly allowance",
        "or earlier"
    },
    account := 568903,
    amount_due := 536,
    payable_by := "27-08-2003"
}

const NotificationU inputU := {
    embed := {
        "Please note the following details:",
        "(your business account)",
        "This is in excess of your normal monthly allowance",
        "or earlier"
    },
    account := 568903,
    amount_due := 536,
    payable_by := "27-08-2003"
}

const universal charstring notif_str :=
"<Notification>" &
"Please note the following details:" &
"<account>568903</account>" &
"(your business account)" &
"<amount-due>536</amount-due>" &
"This is in excess of your normal monthly allowance" &
"<payable-by>27-08-2003</payable-by>" &
"or earlier" &
"</Notification>" &
LF;

const universal charstring notifU_str := // almost, but not quite, the same as notif_str
"<NotificationU>" &
"Please note the following details:" &
"<account>568903</account>" &
"(your business account)" &
"<amount-due>536</amount-due>" &
"This is in excess of your normal monthly allowance" &
"<payable-by>27-08-2003</payable-by>" &
"or earlier" &
"</NotificationU>" &
LF;

const universal charstring notif_str_b :=
"<Notification>\n" &
"\t<embed>\n" &
"\t\t<UNIVERSAL_CHARSTRING>Please note the following details:</UNIVERSAL_CHARSTRING>\n" &
"\t\t<UNIVERSAL_CHARSTRING>(your business account)</UNIVERSAL_CHARSTRING>\n" &
"\t\t<UNIVERSAL_CHARSTRING>This is in excess of your normal monthly allowance</UNIVERSAL_CHARSTRING>\n" &
"\t\t<UNIVERSAL_CHARSTRING>or earlier</UNIVERSAL_CHARSTRING>\n" &
"\t</embed>\n" &
"\t<account>568903</account>\n" &
"\t<amount_due>536</amount_due>\n" &
"\t<payable_by>27-08-2003</payable_by>\n" &
"</Notification>\n" &
LF;

const universal charstring notifU_str_b :=
"<NotificationU>\n" &
"\t<embed>\n" &
"\t\t<UNIVERSAL_CHARSTRING>Please note the following details:</UNIVERSAL_CHARSTRING>\n" &
"\t\t<UNIVERSAL_CHARSTRING>(your business account)</UNIVERSAL_CHARSTRING>\n" &
"\t\t<UNIVERSAL_CHARSTRING>This is in excess of your normal monthly allowance</UNIVERSAL_CHARSTRING>\n" &
"\t\t<UNIVERSAL_CHARSTRING>or earlier</UNIVERSAL_CHARSTRING>\n" &
"\t</embed>\n" &
"\t<account>568903</account>\n" &
"\t<amount_due>536</amount_due>\n" &
"\t<payable_by>27-08-2003</payable_by>\n" &
"</NotificationU>\n" &
LF;

testcase enc_embed() runs on Embedder
{
    CHECK_METHOD(bxer_enc_notif, inputS, notif_str_b);
    CHECK_METHOD(exer_enc_notif, inputS, notif_str);

    CHECK_METHOD(bxer_enc_notifU, inputU, notifU_str_b);
    CHECK_METHOD(exer_enc_notifU, inputU, notifU_str);
}

testcase dec_embed() runs on Embedder
{
  CHECK_DECODE(bxer_dec_notif, notif_str_b, Notification, inputS);
  CHECK_DECODE(exer_dec_notif, notif_str, Notification, inputS);

  CHECK_DECODE(bxer_dec_notifU, notifU_str_b, NotificationU, inputU);
  CHECK_DECODE(exer_dec_notifU, notifU_str, NotificationU, inputU);
}

/********************** EMEBD-VALUES and ATTRIBUTE **********************/

type record EandA {
  record of universal charstring embed,
  integer id,
  charstring color
}
with {
  variant "embedValues";
  variant (id) "attribute";
}

DECLARE_XER_ENCODERS(EandA, ea);
DECLARE_EXER_ENCODERS(EandA, ea);

const EandA c_ea := {
  embed := { "The shirt has", "" },
  id    := 13,
  color := "purple"
}

const universal charstring e_str_ea :=
"<EandA id='13'>" &
"The shirt has" &
"<color>purple</color>" &
"</EandA>\n";

const universal charstring b_str_ea :=
"<EandA>\n" &
"\t<embed>\n" &
"\t\t<UNIVERSAL_CHARSTRING>The shirt has</UNIVERSAL_CHARSTRING>\n" &
"\t\t<UNIVERSAL_CHARSTRING/>\n" &
"\t</embed>\n" &
"\t<id>13</id>\n" &
"\t<color>purple</color>\n" &
"</EandA>\n\n";

testcase enc_ea() runs on Embedder
{
  CHECK_METHOD(bxer_enc_ea, c_ea, b_str_ea);
  CHECK_METHOD(exer_enc_ea, c_ea, e_str_ea);
}

testcase dec_ea() runs on Embedder
{
  CHECK_DECODE(bxer_dec_ea, b_str_ea, EandA, c_ea);
  CHECK_DECODE(exer_dec_ea, e_str_ea, EandA, c_ea);
}

/********************** EMBED-VALUES and USE-NIL **********************/

type union j
{
  integer just,
  boolean unjust
};

type record ENil {
  record of universal charstring embeddings,
  //must not be character-encodable, hence sequence, set, choice, se*-of...
  //integer nile optional
  j nile optional
}
with {
  variant "embedValues";
  variant "useNil";
}

const ENil there := {
  embeddings := { "She was ", "!" },
  nile := { just := 17 }
}

const universal charstring bstr_there :=
"<ENil>\n" &
"\t<embeddings>\n" &
"\t\t<UNIVERSAL_CHARSTRING>She was </UNIVERSAL_CHARSTRING>\n" &
"\t\t<UNIVERSAL_CHARSTRING>!</UNIVERSAL_CHARSTRING>\n" &
"\t</embeddings>\n" &
"\t<nile>\n" &
"\t\t<just>17</just>\n" &
"\t</nile>\n" &
"</ENil>\n\n" ;

const universal charstring estr_there :=
"<ENil>She was <just>17</just>!</ENil>\n";

const ENil gone := {
  embeddings := {},
  nile := omit
}

const universal charstring bstr_gone :=
"<ENil>\n" &
"\t<embeddings/>\n" &
"</ENil>\n\n" ;

const universal charstring estr_gone :=
"<ENil xmlns:pix='foo' pix:nil='true'/>\n";

DECLARE_XER_ENCODERS(ENil, en);
DECLARE_EXER_ENCODERS(ENil, en);

testcase enc_enil() runs on Embedder
{
  CHECK_METHOD(bxer_enc_en, there, bstr_there);
  CHECK_METHOD(exer_enc_en, there, estr_there);

  CHECK_METHOD(bxer_enc_en, gone, bstr_gone);
  CHECK_METHOD(exer_enc_en, gone, estr_gone);
}

testcase dec_enil() runs on Embedder
{
  CHECK_DECODE(bxer_dec_en, bstr_there, ENil, there);
  CHECK_DECODE(exer_dec_en, estr_there, ENil, there);

  CHECK_DECODE(bxer_dec_en, bstr_gone, ENil, gone);
  CHECK_DECODE(exer_dec_en, estr_gone, ENil, gone);
}

/****************** EMBED-VALUES and optional fields ******************/

type record EmOpt {
  record of universal charstring embed,
  integer doi  optional,
  integer doii optional,
  integer zece optional,
  integer sase optional
}
with {
  variant "embedValues"
}

DECLARE_XER_ENCODERS(EmOpt, eo);
DECLARE_EXER_ENCODERS(EmOpt, eo);

const EmOpt c_testing := {
  embed := { "Doi:", "doi:", "zece:", "sase:", "Se aude?" },
  doi  :=  2,
  doii :=  2,
  zece := 10,
  sase :=  6
}

const universal charstring bstr_test6 :=
"<EmOpt>\n" &
"\t<embed>\n" &
"\t\t<UNIVERSAL_CHARSTRING>Doi:</UNIVERSAL_CHARSTRING>\n" &
"\t\t<UNIVERSAL_CHARSTRING>doi:</UNIVERSAL_CHARSTRING>\n" &
"\t\t<UNIVERSAL_CHARSTRING>zece:</UNIVERSAL_CHARSTRING>\n" &
"\t\t<UNIVERSAL_CHARSTRING>sase:</UNIVERSAL_CHARSTRING>\n" &
"\t\t<UNIVERSAL_CHARSTRING>Se aude?</UNIVERSAL_CHARSTRING>\n" &
"\t</embed>\n" &
"\t<doi>2</doi>\n" &
"\t<doii>2</doii>\n" &
"\t<zece>10</zece>\n" &
"\t<sase>6</sase>\n" &
"</EmOpt>\n\n";

const universal charstring estr_test6 :=
"<EmOpt>" &
"Doi:<doi>2</doi>" &
"doi:<doii>2</doii>" &
"zece:<zece>10</zece>" &
"sase:<sase>6</sase>" &
"Se aude?" &
"</EmOpt>\n";

const universal charstring bstr_test10 :=
"<EmOpt>\n" &
"\t<embed>\n" &
"\t\t<UNIVERSAL_CHARSTRING>Doi:</UNIVERSAL_CHARSTRING>\n" &
"\t\t<UNIVERSAL_CHARSTRING>doi:</UNIVERSAL_CHARSTRING>\n" &
"\t\t<UNIVERSAL_CHARSTRING>zece:</UNIVERSAL_CHARSTRING>\n" &
"\t\t<UNIVERSAL_CHARSTRING>Se aude?</UNIVERSAL_CHARSTRING>\n" &
"\t</embed>\n" &
"\t<doi>2</doi>\n" &
"\t<doii>2</doii>\n" &
"\t<zece>10</zece>\n" &
"</EmOpt>\n\n";

const universal charstring estr_test10 :=
"<EmOpt>" &
"Doi:<doi>2</doi>" &
"doi:<doii>2</doii>" &
"zece:<zece>10</zece>" &
"Se aude?" &
"</EmOpt>\n";

const universal charstring bstr_test2 :=
"<EmOpt>\n" &
"\t<embed>\n" &
"\t\t<UNIVERSAL_CHARSTRING>Doi:</UNIVERSAL_CHARSTRING>\n" &
"\t\t<UNIVERSAL_CHARSTRING>Se aude?</UNIVERSAL_CHARSTRING>\n" &
"\t</embed>\n" &
"\t<doi>2</doi>\n" &
"</EmOpt>\n\n";

const universal charstring estr_test2 :=
"<EmOpt>" &
"Doi:<doi>2</doi>" &
"Se aude?" &
"</EmOpt>\n";


testcase enc_emb_opt() runs on Embedder
{
  var EmOpt testing := c_testing;
  CHECK_METHOD(bxer_enc_eo, testing, bstr_test6);
  CHECK_METHOD(exer_enc_eo, testing, estr_test6);
  
  testing.sase := omit;
  testing.embed := { "Doi:", "doi:", "zece:", "Se aude?" };
  CHECK_METHOD(bxer_enc_eo, testing, bstr_test10);
  CHECK_METHOD(exer_enc_eo, testing, estr_test10);
  
  testing.zece := omit;
  testing.doii := omit;
  testing.embed := { "Doi:", "Se aude?" };
  CHECK_METHOD(bxer_enc_eo, testing, bstr_test2);
  CHECK_METHOD(exer_enc_eo, testing, estr_test2);
}

testcase dec_emb_opt() runs on Embedder
{
  var EmOpt expected := c_testing;
  //       embed := {"Doi:", "doi:", "zece:", "sase:", "Se aude?" }
  CHECK_DECODE(bxer_dec_eo, bstr_test6, EmOpt, expected);
  CHECK_DECODE(exer_dec_eo, estr_test6, EmOpt, expected);

  expected.sase  := omit;
  expected.embed := {"Doi:", "doi:", "zece:", "Se aude?" };
  CHECK_DECODE(bxer_dec_eo, bstr_test10, EmOpt, expected);
  CHECK_DECODE(exer_dec_eo, estr_test10, EmOpt, expected);

  expected.zece  := omit;
  expected.doii  := omit;
  expected.embed := {"Doi:", "Se aude?" }
  CHECK_DECODE(bxer_dec_eo, bstr_test2, EmOpt, expected);
  CHECK_DECODE(exer_dec_eo, estr_test2, EmOpt, expected);
}



/****************** EMBED-VALUES and record of record fields untagged ******************/

type record EmbRecordOrRecordUntagged {
  record of universal charstring embed,
  record of record {
    universal charstring a,
    boolean b
  } sequence_list
}
with {
  variant "embedValues"
  variant "element";
  variant (sequence_list) "untagged";
  variant (sequence_list[-]) "untagged";
}

DECLARE_XER_ENCODERS(EmbRecordOrRecordUntagged, erecofrecuntagged);
DECLARE_EXER_ENCODERS(EmbRecordOrRecordUntagged, erecofrecuntagged);

const EmbRecordOrRecordUntagged c_testingrecofrecuntagged := {
  embed := { "a1", "b1", "a2", "b2", "end" },
  sequence_list := {
    {
      a := "firsta",
      b := false
    },
    {
      a := "seconda",
      b := false
    }
  }
}

const universal charstring bstr_test7 :=
"<EmbRecordOrRecordUntagged>\n" &
"\t<embed>\n" &
"\t\t<UNIVERSAL_CHARSTRING>a1</UNIVERSAL_CHARSTRING>\n" &
"\t\t<UNIVERSAL_CHARSTRING>b1</UNIVERSAL_CHARSTRING>\n" &
"\t\t<UNIVERSAL_CHARSTRING>a2</UNIVERSAL_CHARSTRING>\n" &
"\t\t<UNIVERSAL_CHARSTRING>b2</UNIVERSAL_CHARSTRING>\n" &
"\t\t<UNIVERSAL_CHARSTRING>end</UNIVERSAL_CHARSTRING>\n" &
"\t</embed>\n" &
"\t<sequence_list>\n" &
"\t\t<SEQUENCE>\n" &
"\t\t\t<a>firsta</a>\n" &
"\t\t\t<b><false/></b>\n" &
"\t\t</SEQUENCE>\n" &
"\t\t<SEQUENCE>\n" &
"\t\t\t<a>seconda</a>\n" &
"\t\t\t<b><false/></b>\n" &
"\t\t</SEQUENCE>\n" &
"\t</sequence_list>\n" &
"</EmbRecordOrRecordUntagged>\n\n";

const universal charstring estr_test7 :=
"<EmbRecordOrRecordUntagged>a1" &
"<a>firsta</a>b1"&
"<b>false</b>a2"&
"<a>seconda</a>b2"&
"<b>false</b>end"&
"</EmbRecordOrRecordUntagged>\n"


const universal charstring bstr_test8 :=
"<EmbRecordOrRecordUntagged>\n"&
"\t<embed>\n"&
"\t\t<UNIVERSAL_CHARSTRING>first</UNIVERSAL_CHARSTRING>\n"&
"\t\t<UNIVERSAL_CHARSTRING>second</UNIVERSAL_CHARSTRING>\n"&
"\t\t<UNIVERSAL_CHARSTRING>third</UNIVERSAL_CHARSTRING>\n"&
"\t</embed>\n"&
"\t<sequence_list>\n"&
"\t\t<SEQUENCE>\n"&
"\t\t\t<a>firsta</a>\n"&
"\t\t\t<b><false/></b>\n"&
"\t\t</SEQUENCE>\n"&
"\t</sequence_list>\n"&
"</EmbRecordOrRecordUntagged>\n"&
"\n"

const universal charstring estr_test8 :=
"<EmbRecordOrRecordUntagged>first"&
"<a>firsta</a>second"&
"<b>false</b>third"&
"</EmbRecordOrRecordUntagged>\n"

const universal charstring bstr_test9 :=
"<EmbRecordOrRecordUntagged>\n"&
"\t<embed>\n"&
"\t\t<UNIVERSAL_CHARSTRING>first</UNIVERSAL_CHARSTRING>\n"&
"\t\t<UNIVERSAL_CHARSTRING/>\n"&
"\t</embed>\n"&
"\t<sequence_list/>\n"&
"</EmbRecordOrRecordUntagged>\n"&
"\n"

const universal charstring estr_test9 :=
"<EmbRecordOrRecordUntagged>first"&
"</EmbRecordOrRecordUntagged>\n"

testcase enc_emb_record_of_record_untagged() runs on Embedder
{
  var EmbRecordOrRecordUntagged testing := c_testingrecofrecuntagged;
  CHECK_METHOD(bxer_enc_erecofrecuntagged, testing, bstr_test7);
  CHECK_METHOD(exer_enc_erecofrecuntagged, testing, estr_test7);
  
  testing.sequence_list := { { a := "firsta", b := false } }
  testing.embed := {"first", "second", "third"}
  CHECK_METHOD(bxer_enc_erecofrecuntagged, testing, bstr_test8);
  CHECK_METHOD(exer_enc_erecofrecuntagged, testing, estr_test8);
  
  testing.sequence_list := { }
  testing.embed := {"first", ""}
  CHECK_METHOD(bxer_enc_erecofrecuntagged, testing, bstr_test9);
  CHECK_METHOD(exer_enc_erecofrecuntagged, testing, estr_test9);
}

testcase dec_emb_record_of_record_untagged() runs on Embedder
{
  var EmbRecordOrRecordUntagged expected := c_testingrecofrecuntagged;
  CHECK_DECODE(bxer_dec_erecofrecuntagged, bstr_test7, EmbRecordOrRecordUntagged, expected);
  CHECK_DECODE(exer_dec_erecofrecuntagged, estr_test7, EmbRecordOrRecordUntagged, expected);

  expected.sequence_list := { { a := "firsta", b := false } }
  expected.embed := {"first", "second", "third"}
  CHECK_DECODE(bxer_dec_erecofrecuntagged, bstr_test8, EmbRecordOrRecordUntagged, expected);
  CHECK_DECODE(exer_dec_erecofrecuntagged, estr_test8, EmbRecordOrRecordUntagged, expected);

  expected.sequence_list := { }
  expected.embed := {"first", ""}
  CHECK_DECODE(bxer_dec_erecofrecuntagged, bstr_test9, EmbRecordOrRecordUntagged, expected);
  CHECK_DECODE(exer_dec_erecofrecuntagged, estr_test9, EmbRecordOrRecordUntagged, expected);
}


/********************** Control part **********************/

control {
    execute(enc_embed());
    execute(dec_embed());

    execute(enc_ea());
    execute(dec_ea());

    execute(enc_enil());
    execute(dec_enil());

    execute(enc_emb_opt());
    execute(dec_emb_opt());

    execute(enc_emb_record_of_record_untagged());
    execute(dec_emb_record_of_record_untagged());
  }
}
with {
  encode "XML"
  variant "controlNamespace 'foo' prefix 'pix'"
}
