/******************************************************************************
 * 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:
 *   Baranyi, Botond
 *
 ******************************************************************************/
module oop3 {

type component CT {}

type class C0 { }

type class C2 extends C1, T1 {
  public var float @property p_float;
  public function f_one(in integer x) return integer {
    return x + 1;
  }
  public function f_abs(in boolean x) return integer {
    if (x) { return 1; }
    else { return 0; }
  }
}

type class @trait T1 {
  public function @abstract f_abs(in boolean x) return integer;
}

type class C1 {
  public var integer @property p_int;
  private var charstring v_str;
  public var charstring @property p_str {
    @get => v_str;
    @set => v_str := value;
  }
  public function f_one(in integer x) return integer {
    return x;
  }
  public function f_two(in integer x, in charstring y) return charstring {
    return int2str(x) & "-" & y;
  }
}

testcase tc_object_template() runs on CT {
  var C0 c0 := C0.create;
  var template C0 vt0 := {
    toString() := "Object"
  }
  if (not match(c0, vt0)) {
    setverdict(fail, "#0: ", match(c0, vt0));
  }
  log(match(c0, vt0));
  
  var C1 c1 := C1.create(2, "a");
  var template C1 vt1 := {
    p_int := (0..infinity),
    p_str := ? length (0..2),
    f_one(2) := 2,
    f_two(12, "abc") := pattern "??-abc"
  }
  if (not match(c1, vt1)) {
    setverdict(fail, "#1: ", match(c1, vt1));
  }
  log(match(c1, vt1));
  
  var C2 c2 := C2.create(2, "a", 0.0);
  if (match(c2, vt1)) {
    setverdict(fail, "#2: ", match(c2, vt1));
  }
  log(match(c2, vt1));
  
  var template T1 vt2 := {
    f_abs(true) := (1..infinity),
    f_abs(false) := 0
  }
  if (not match(c2, vt2)) {
    setverdict(fail, "#3: ", match(c2, vt2));
  }
  log(match(c2, vt2));
  
  template C1 t1 := {
    p_int := (-10..10),
    p_str := ? length (0..2),
    f_one(2) := 2
  }
  template C1 t2 modifies t1 := {
    p_int := (0..infinity),
    f_two(12, "abc") := pattern "??-abc"
  }
  if (log2str(t2) != log2str(vt1)) {
    setverdict(fail, "#4: ", t2, " != ", vt1);
  }
  
  template T1 t3 := {
    f_abs(true) := (1..infinity)
  }
  template T1 t4 modifies t3 := {
    f_abs(false) := 0
  }
  if (log2str(t4) != log2str(vt2)) {
    setverdict(fail, "#5: ", t4, " != ", vt2);
  }
  
  var template C2 vt3 := ?;
  if (not match(c2, vt3)) {
    setverdict(fail, "#6: ", match(c2, vt3));
  }
  log(match(c2, vt3));
  
  var template C1 vt4 := (
    { p_int := 2, f_two(3, "x") := ? length (3) },
    vt1
  );
  if (not match(c2, vt4)) {
    setverdict(fail, "#7: ", match(c2, vt4));
  }
  log(match(c2, vt4));
  
  vt1 := {
    f_one(4) := 5
  }
  if (not match(c2, vt1)) {
    setverdict(fail, "#8: ", match(c2, vt1));
  }
  log(match(c2, vt1));
  
  if (isvalue(vt0)) {
    setverdict(fail, "#9");
  }
  
  if (not ispresent(vt1)) {
    setverdict(fail, "#10");
  }
  
  if (not isbound(vt2)) {
    setverdict(fail, "#11");
  }
  
  setverdict(pass);
}

type record ClassRec {
  integer i,
  C1 c,
  C0 c0 optional
}

type record of T1 TraitList;

type union ClassUni {
  integer i,
  C2 c
}

template ClassUni t_pard(template C2 par) := { c := par }

testcase tc_object_template_embedded() runs on CT {
  var ClassRec v1 := { 10, C2.create(1, "a", -1.0), omit }
  var template ClassRec vt1 := {
    i := (2, 4, 6, 8, 10),
    c := {
      p_int := @dynamic { return value rem 2 == 0; },
      f_two(12, "abc") := ? length (5..infinity)
    },
    c0 := omit
  }
  if (match(v1, vt1)) {
    setverdict(fail, "#1: ", match(v1, vt1));
  }
  log(match(v1, vt1));
  
  v1.c.p_int := 4;
  if (not match(v1, vt1)) {
    setverdict(fail, "#2: ", match(v1, vt1));
  }
  log(match(v1, vt1));
  
  var TraitList v2 := { C2.create(2, "b", -2.0), C2.create(3, "c", -3.0) }
  var template TraitList vt2 := {
    { },
    { f_abs(true) := 1 }
  }
  if (not match(v2, vt2)) {
    setverdict(fail, "#3: ", match(v2, vt2));
  }
  log(match(v2, vt2));
  
  vt1.c0 := {
    toString() := ? length (1..10)
  } ifpresent;
  if (not match(v1, vt1)) {
    setverdict(fail, "#4: ", match(v1, vt1));
  }
  log(match(v1, vt1));
  
  var ClassUni v3 := { c := C2.create(4, "d", -4.0) }
  var template ClassUni vt3 := t_pard(@dynamic { return value.p_float == int2float(value.p_int) * -1.0; });
  if (not match(v3, vt3)) {
    setverdict(fail, "#5: ", match(v3, vt3));
  }
  log(match(v3, vt3));
  
  if (isvalue(vt1)) {
    setverdict(fail, "#6");
  }
  
  if (not ispresent(vt1)) {
    setverdict(fail, "#7");
  }
  
  if (ispresent(vt1.c0)) {
    setverdict(fail, "#8");
  }
  
  if (not isbound(vt3)) {
    setverdict(fail, "#9");
  }
  
  if (sizeof(vt1) != 2) {
    setverdict(fail, "#10: ", sizeof(vt1));
  }
  
  setverdict(pass);
}


type record ClassRec2 {
  C1 c optional
}

testcase tc_object_template_restriction_check() runs on CT {
  var template C1 vt1 := {
    p_int := (0..infinity),
    f_one(2) := 2
  }
  var template C1 vt2 := ? ifpresent;
  var template C1 vt3 := omit;
  
  var template (present) C1 vt_present1 := vt1;
  @try {
    var template (present) C1 vt_present2 := vt2;
    setverdict(fail, "Expected error on 'present' restriction check");
  }
  @catch (msg) {
    var template charstring exp := pattern "*Restriction `present' on template of type C1 violated.";
    if (not match(msg, exp)) {
      setverdict(fail, "Incorrect error message: ", msg, ", expected: ", exp);
    }
  }
  
  var template (value) ClassRec vt_value1 := { c := vt3 }
  @try {
    var template (value) ClassRec vt_value2 := { c := vt1 }
    setverdict(fail, "Expected error on 'value' restriction check");
  }
  @catch (msg) {
    var template charstring exp := pattern "*Restriction `value' on template of type @oop3.ClassRec violated.";
    if (not match(msg, exp)) {
      setverdict(fail, "Incorrect error message: ", msg, ", expected: ", exp);
    }
  }
  
  var template (omit) C1 vt_omit1 := vt3;
  @try {
    var template (omit) C1 vt_omit2 := vt1;
    setverdict(fail, "Expected error on 'omit' restriction check");
  }
  @catch (msg) {
    var template charstring exp := pattern "*Restriction `omit' on template of type C1 violated.";
    if (not match(msg, exp)) {
      setverdict(fail, "Incorrect error message: ", msg, ", expected: ", exp);
    }
  }
  
  setverdict(pass);
}


control {
  execute(tc_object_template());
  execute(tc_object_template_embedded());
  execute(tc_object_template_restriction_check());
}

}
