/******************************************************************************
 * 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 portReference {

type port PT message {
  inout integer;
} with { extension "internal"; }

type component CT {
  port PT p1;
  port PT p2;
  const PT cc := null;
  var PT cv; // unbound
}

const PT gc := null;

function f_send(PT p) {
  p.send(2);
}

function f_receive(PT p) {
  timer tmr := 0.5;
  tmr.start;
  alt {
    [] p.receive(2) { setverdict(pass); }
    [] p.receive(?) { setverdict(fail, "#5: incorrect message"); }
    [] tmr.timeout { setverdict(fail, "#5: timeout"); }
  }
  tmr.stop;
}

testcase tc_port_reference_usage() runs on CT {
  connect(self:p1, self:p2);
  
  var PT v1 := p1;
  var PT v2;
  v2 := p2;
  
  v1.send(2);
  timer tmr := 0.5;
  tmr.start;
  alt {
    [] v1.receive(?) { setverdict(fail, "#1: incorrect port"); }
    [] v2.receive(2) { setverdict(pass); }
    [] v2.receive(?) { setverdict(fail, "#1: incorrect message"); }
    [] tmr.timeout { setverdict(fail, "#1: timeout"); }
  }
  tmr.stop;
  
  v2.send(2);
  tmr.start;
  alt {
    [] v2.receive(?) { setverdict(fail, "#2: incorrect port"); }
    [] v1.receive(2) { setverdict(pass); }
    [] v1.receive(?) { setverdict(fail, "#2: incorrect message"); }
    [] tmr.timeout { setverdict(fail, "#2: timeout"); }
  }
  tmr.stop;
  
  @try {
    cc.send(2);
    setverdict(fail, "#3: expected error");
  }
  @catch (msg) {
    var template charstring exp := pattern "*Accessing a null port reference.";
    if (not match(msg, exp)) {
      setverdict(fail, "#3: unexpected message: ", msg);
    }
  }
  
  @try {
    cv.receive(?);
    setverdict(fail, "#4: expected error");
  }
  @catch (msg) {
    var template charstring exp := pattern "*Accessing an unbound port reference.";
    if (not match(msg, exp)) {
      setverdict(fail, "#4: unexpected message: ", msg);
    }
  }
  
  f_send(v1);
  f_receive(v2);
  
  disconnect(self:p1, self:p2);
}

function f_equality(PT p) runs on CT {
  cv := p1;
  if (p == null) { setverdict(fail, "#16"); }
  if (null == p) { setverdict(fail, "#17"); }
  if (p != p1) { setverdict(fail, "#18"); }
  if (p2 == p) { setverdict(fail, "#19"); }
  if (p != cv) { setverdict(fail, "#20"); }
  if (cv != p) { setverdict(fail, "#21"); }
}

testcase tc_port_reference_equality() runs on CT {
  var PT v1 := p1;
  var PT v2;
  v2 := p2;
  
  if (cc == v1) { setverdict(fail, "#1"); }
  if (v1 == cc) { setverdict(fail, "#2"); }
  if (cc != gc) { setverdict(fail, "#3"); }
  if (gc == p1) { setverdict(fail, "#4"); }
  if (p1 == null) { setverdict(fail, "#5"); }
  if (v1 == v2) { setverdict(fail, "#6"); }
  if (p1 == p2) { setverdict(fail, "#7"); }
  if (p1 != v1) { setverdict(fail, "#8"); }
  if (v2 == p1) { setverdict(fail, "#9"); }
  
  @try {
    var boolean vb := cc == cv;
    setverdict(fail, "#10: expected error");
  }
  @catch (msg) {
    var template charstring exp := pattern "*Unbound right operand of port reference comparison.";
    if (not match(msg, exp)) {
      setverdict(fail, "#10: unexpected message: ", msg);
    }
  }
  
  @try {
    var boolean vb := cv == cc;
    setverdict(fail, "#11: expected error");
  }
  @catch (msg) {
    var template charstring exp := pattern "*Unbound left operand of port reference comparison.";
    if (not match(msg, exp)) {
      setverdict(fail, "#11: unexpected message: ", msg);
    }
  }
  
  @try {
    var boolean vb := p1 == cv;
    setverdict(fail, "#12: expected error");
  }
  @catch (msg) {
    var template charstring exp := pattern "*Unbound right operand of port reference comparison.";
    if (not match(msg, exp)) {
      setverdict(fail, "#12: unexpected message: ", msg);
    }
  }
  
  @try {
    var boolean vb := cv == p1;
    setverdict(fail, "#13: expected error");
  }
  @catch (msg) {
    var template charstring exp := pattern "*Unbound left operand of port reference comparison.";
    if (not match(msg, exp)) {
      setverdict(fail, "#13: unexpected message: ", msg);
    }
  }
  
  @try {
    var boolean vb := v2 == cv;
    setverdict(fail, "#14: expected error");
  }
  @catch (msg) {
    var template charstring exp := pattern "*Unbound right operand of port reference comparison.";
    if (not match(msg, exp)) {
      setverdict(fail, "#14: unexpected message: ", msg);
    }
  }
  
  @try {
    var boolean vb := cv == v2;
    setverdict(fail, "#15: expected error");
  }
  @catch (msg) {
    var template charstring exp := pattern "*Unbound left operand of port reference comparison.";
    if (not match(msg, exp)) {
      setverdict(fail, "#15: unexpected message: ", msg);
    }
  }
  
  f_equality(v1);
  
  setverdict(pass);
}

testcase tc_port_reference_other() runs on CT {
  var PT v1 := p1;
  var PT v2;
  v2 := p2;
  var PT v3 := null;
  
  // logging
  if (log2str(v1) != log2str(p1)) {
    setverdict(fail, "#1: ", v1);
  }
  if (log2str(v3) != "null") {
    setverdict(fail, "#2: ", v3);
  }
  var integer v_unbound;
  if (log2str(cv) != log2str(v_unbound)) {
    setverdict(fail, "#3: ", cv);
  }
  
  // isbound
  if (not isbound(v1)) { setverdict(fail, "#4"); }
  if (not isbound(v3)) { setverdict(fail, "#5"); }
  if (isbound(cv)) { setverdict(fail, "#6"); }
  
  // isvalue
  if (not isvalue(v1)) { setverdict(fail, "#7"); }
  if (not isvalue(v3)) { setverdict(fail, "#8"); }
  if (isvalue(cv)) { setverdict(fail, "#9"); }
  
  // ispresent
  if (not ispresent(v1)) { setverdict(fail, "#10"); }
  if (ispresent(v3)) { setverdict(fail, "#11"); }
  if (ispresent(cv)) { setverdict(fail, "#12"); }
  
  setverdict(pass);
}

control {
  execute(tc_port_reference_usage());
  execute(tc_port_reference_equality());
  execute(tc_port_reference_other());
}

}
