/******************************************************************************
 * 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
 *   Raduly, Csaba
 *
 ******************************************************************************/
module UseOrder {
modulepar boolean UseOrder_verbose := false;
#define verbose UseOrder_verbose
#include "../macros.ttcnin"

type component UO {}

// "Plain" USE-ORDER
type record UOProduct {
  record of enumerated { id, name, price, color } order,

  integer    id,
  charstring name,
  float      price,
  charstring color
}
with {
  variant "element";
  variant "useOrder";
  variant "namespace as 'http://www.example.com' prefix 'exm'";
}


DECLARE_XER_ENCODERS(UOProduct, uo);
DECLARE_EXER_ENCODERS(UOProduct, uo);

const UOProduct uopval := {
  order := { id, color, price, name },

  id    := 100,
  name  := "shirt",
  price := 12.23,
  color := "red"
}

const universal charstring str_uo_e :=
"<exm:UOProduct xmlns:exm=\'http://www.example.com\'>\n" &
"\t<id>100</id>\n" &
"\t<color>red</color>\n" &
"\t<price>12.230000</price>\n" &
"\t<name>shirt</name>\n" &
"</exm:UOProduct>\n" &
"\n";

const universal charstring str_uo_b :=
"<UOProduct>\n" &
"\t<order>\n" &
"\t\t<id/><color/><price/><name/>\n" &
"\t</order>\n" &

"\t<id>100</id>\n" &
"\t<name>shirt</name>\n" &
"\t<price>12.230000</price>\n" &
"\t<color>red</color>\n" &
"</UOProduct>\n" &
"\n";

testcase encode_uo() runs on UO
{
  CHECK_METHOD(bxer_enc_uo, uopval, str_uo_b);
  CHECK_METHOD(exer_enc_uo, uopval, str_uo_e);
}

testcase decode_uo() runs on UO
{
  CHECK_DECODE(bxer_dec_uo, str_uo_b, UOProduct, uopval);
  CHECK_DECODE(exer_dec_uo, str_uo_e, UOProduct, uopval);
}


external function exer_enc_uo_w(in UOProduct par) return octetstring
with { extension "prototype(convert) encode (XER:XER_EXTENDED) errorbehavior(ALL:WARNING)" }

external function exer_enc_uo2(in UOProduct par, out octetstring o)
with { extension "prototype(fast) encode (XER:XER_EXTENDED) errorbehavior(ALL:WARNING)" }

external function exer_dec_uo2(in octetstring par, out UOProduct o) return integer
with { extension "prototype(backtrack) decode (XER:XER_EXTENDED) errorbehavior(ALL:WARNING)" };

testcase encdec_uo() runs on UO
{
  // This is what the encoder returns on error (no fields encoded).
  const universal charstring panic := "<exm:UOProduct xmlns:exm='http://www.example.com'/>\n\n";

  const UOProduct c_uo := {
    /* There is no way to test incorrect values like { id, id, id, id } or
	{ id, name }, without a dynamic testcase error */
    order := { id, name, color, price },
    id    := 100,
    name  := "shirt",
    price := 12.23,
    color := "red"
  }
  var integer rez;
  var octetstring encoded;
  exer_enc_uo2(c_uo, encoded);

  {
  var UOProduct v_uo;
  rez := exer_dec_uo2(encoded, v_uo);
  COMPARE(v_uo, c_uo);
  }
  
  { // missing field
    const UOProduct unidentified :=
    {
      order := { name, color, price },
      id    := 100,
      name  := "shirt",
      price := 12.23,
      color := "red"
    };
    CHECK_METHOD(exer_enc_uo_w, unidentified, panic);
  }
  
  { // the right number, but some (!) duplication
    const UOProduct deja_vu :=
    {
      order := { name, name, name, name },
      id    := 100,
      name  := "shirt",
      price := 12.23,
      color := "red"
    };
    CHECK_METHOD(exer_enc_uo_w, deja_vu, panic);
  }

  // We can test errors during decoding with prototype(backtrack)
  {
    const universal charstring priceless := "<exm:UOProduct xmlns:exm='http://www.example.com'>\n\t<id>100</id>\n\t<name>shirt</name>\n\t<color>red</color>\n</exm:UOProduct>\n\n";
    // Note no price
    rez := u2o("UTF-8", priceless, encoded);
    if (rez!=0) { setverdict(fail, "Conversion failed: ", rez) }
    
    var UOProduct v_uo;
    rez := exer_dec_uo2(encoded, v_uo);
    COMPARE(rez, 1); // 1 means error
  }

  {
    const universal charstring id2 := "<exm:UOProduct xmlns:exm='http://www.example.com'>\n\t<id>100</id>\n\t<id>234</id>\n\t<name>shirt</name>\n\t<color>red</color>\n</exm:UOProduct>\n\n";
    // Still no price but 4 child elements
    rez := u2o("UTF-8", id2, encoded);
    if (rez!=0) { setverdict(fail, "Conversion failed: ", rez) }
    
    var UOProduct v_uo;
    rez := exer_dec_uo2(encoded, v_uo);
    COMPARE(rez, 1); // 1 means error
  }

  {
    const universal charstring toomuch := "<exm:UOProduct xmlns:exm='http://www.example.com'>\n\t<price>12.230000</price>\n\t<id>100</id>\n\t<id>234</id>\n\t<name>shirt</name>\n\t<color>red</color>\n</exm:UOProduct>\n\n";
    // too many child elements
    rez := u2o("UTF-8", toomuch, encoded);
    if (rez!=0) { setverdict(fail, "Conversion failed: ", rez) }
    
    var UOProduct v_uo;
    rez := exer_dec_uo2(encoded, v_uo);
    COMPARE(rez, 1); // 1 means error
  }
}

/* ***   *** *   *** * *   *** *   *** *** ***   * *** ***   * * *** *
*** *** ***   * *** *   * * *   *** *** ***   *** ***   *   ***   * * * *
* *   *** *   *** *** *   *** * *** *   *** *** ***   *** ***   * *** *** *
* *** * *   *   ***   *   * *** * *   *** * *** ***   *** * *   * *   * * *** *
* * *** *   *   * *** *   *   *** *   ***/

// USE-ORDER with optional fields (regression test for HL22039)
type record Team {
  record of enumerated { goalie, defender, midfield, forward } order,
  universal charstring goalie   optional,
  universal charstring defender optional,
  universal charstring midfield optional,
  universal charstring forward  optional
}
with {
  variant "useOrder"
}

DECLARE_XER_ENCODERS(Team, tm);
DECLARE_EXER_ENCODERS(Team, tm);

const Team arany := {
  { goalie, defender, midfield, forward },
  "Grosics",
  "Buzanski Lorant Lantos",
  "Bozsik Zakarias",
  "Czibor Kocsis Hidegkuti Puskas Budai"
}

const Team ezust := {
  { defender, midfield, forward },
  omit,
  "Buzanski Lorant Lantos",
  "Bozsik Zakarias",
  "Czibor Kocsis Hidegkuti Puskas Budai"
}

const Team attack := {
  order    := { forward, goalie },
  goalie   := "Gilmar",
  defender := omit,
  midfield := omit,
  forward  := "Garrincha Didi Vav" & char(0,0,0,225) & " Pel" & char(0,0,0,233) & " Zagalo"
}

const Team senki := {
  {},
  omit, omit, omit, omit
}

const universal charstring str_arany_e :=
"<Team>\n" &
"\t<goalie>Grosics</goalie>\n" &
"\t<defender>Buzanski Lorant Lantos</defender>\n" &
"\t<midfield>Bozsik Zakarias</midfield>\n" &
"\t<forward>Czibor Kocsis Hidegkuti Puskas Budai</forward>\n" &
"</Team>\n\n";

const universal charstring str_arany_b :=
"<Team>\n" &
"\t<order>\n" &
"\t\t<goalie/><defender/><midfield/><forward/>\n" &
"\t</order>\n" &
"\t<goalie>Grosics</goalie>\n" &
"\t<defender>Buzanski Lorant Lantos</defender>\n" &
"\t<midfield>Bozsik Zakarias</midfield>\n" &
"\t<forward>Czibor Kocsis Hidegkuti Puskas Budai</forward>\n" &
"</Team>\n\n";

const universal charstring str_ezust_e :=
"<Team>\n" &
"\t<defender>Buzanski Lorant Lantos</defender>\n" &
"\t<midfield>Bozsik Zakarias</midfield>\n" &
"\t<forward>Czibor Kocsis Hidegkuti Puskas Budai</forward>\n" &
"</Team>\n\n";

const universal charstring str_ezust_b :=
"<Team>\n" &
"\t<order>\n" &
"\t\t<defender/><midfield/><forward/>\n" &
"\t</order>\n" &
"\t<defender>Buzanski Lorant Lantos</defender>\n" &
"\t<midfield>Bozsik Zakarias</midfield>\n" &
"\t<forward>Czibor Kocsis Hidegkuti Puskas Budai</forward>\n" &
"</Team>\n\n";

const universal charstring str_attack_e :=
"<Team>\n" &
"\t<forward>Garrincha Didi Vavá Pelé Zagalo</forward>\n" &
"\t<goalie>Gilmar</goalie>\n" &
"</Team>\n\n";

const universal charstring str_attack_b :=
"<Team>\n" &
"\t<order>\n" &
"\t\t<forward/><goalie/>\n" &
"\t</order>\n" &
"\t<goalie>Gilmar</goalie>\n" &
"\t<forward>Garrincha Didi Vavá Pelé Zagalo</forward>\n" &
"</Team>\n\n";


const universal charstring str_senki_e := "<Team/>\n\n";
const universal charstring str_senki_e2 := "<Team></Team>\n\n";
//const universal charstring str_senki_e2 := "<Team/>\n\n";

const universal charstring str_senki_b :=
"<Team>\n" &
"\t<order/>\n" &
"</Team>\n\n";



testcase encode_uo_team() runs on UO
{
  CHECK_METHOD(bxer_enc_tm, arany, str_arany_b);
  CHECK_METHOD(exer_enc_tm, arany, str_arany_e);

  CHECK_METHOD(bxer_enc_tm, ezust, str_ezust_b);
  CHECK_METHOD(exer_enc_tm, ezust, str_ezust_e);

  CHECK_METHOD(bxer_enc_tm, attack, str_attack_b);
  CHECK_METHOD(exer_enc_tm, attack, str_attack_e);

  CHECK_METHOD(bxer_enc_tm, senki, str_senki_b);
  CHECK_METHOD(exer_enc_tm, senki, str_senki_e);
}

testcase decode_uo_team() runs on UO
{
  CHECK_DECODE(bxer_dec_tm, str_arany_b, Team, arany);
  CHECK_DECODE(exer_dec_tm, str_arany_e, Team, arany);

  CHECK_DECODE(bxer_dec_tm, str_ezust_b, Team, ezust);
  CHECK_DECODE(exer_dec_tm, str_ezust_e, Team, ezust);

  CHECK_DECODE(bxer_dec_tm, str_attack_b, Team, attack);
  CHECK_DECODE(exer_dec_tm, str_attack_e, Team, attack);

  CHECK_DECODE(bxer_dec_tm, str_senki_b, Team, senki);
  CHECK_DECODE(exer_dec_tm, str_senki_e, Team, senki);
  CHECK_DECODE(exer_dec_tm, str_senki_e2,Team, senki);
}

// -- -- -- -- run it -- -- -- --

control {
  execute(encode_uo());
  execute(decode_uo());
  execute(encdec_uo());

  execute(encode_uo_team());
  execute(decode_uo_team());


}

}
with {
encode "XML"
}
