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

#include "TTCN3.hh"

void OBJECT_template::copy_template(const OBJECT_template& other_value)
{
set_selection(other_value);
switch (other_value.template_selection) {
case SPECIFIC_VALUE:
single_value = new single_value_struct;
single_value->n_assignments = other_value.single_value->n_assignments;
single_value->assignment_list = (single_value->n_assignments == 0) ? NULL :
  (single_value_struct::assignment_struct*)Malloc(
   sizeof(single_value_struct::assignment_struct) * single_value->n_assignments);
single_value->n_pos = 0;
for (unsigned int i = 0; i < single_value->n_assignments; ++i) {
switch (other_value.single_value->assignment_list[i].assignment_selection) {
case single_value_struct::toString:
toString() = *other_value.single_value->assignment_list[i].toString;
break;
default:
TTCN_error("Internal error: invalid OBJECT object template assignment type");
break;
}
}
break;
case OMIT_VALUE:
case ANY_VALUE:
case ANY_OR_OMIT:
break;
case VALUE_LIST:
case COMPLEMENTED_LIST:
case CONJUNCTION_MATCH:
value_list.n_values = other_value.value_list.n_values;
value_list.list_value = new OBJECT_template[value_list.n_values];
for (unsigned int list_count = 0; list_count < value_list.n_values; list_count++)
value_list.list_value[list_count].copy_template(other_value.value_list.list_value[list_count]);
break;
case IMPLICATION_MATCH:
implication_.precondition = new OBJECT_template(*other_value.implication_.precondition);
implication_.implied_template = new OBJECT_template(*other_value.implication_.implied_template);
break;
case DYNAMIC_MATCH:
dyn_match = other_value.dyn_match;
dyn_match->ref_count++;
break;
default:
TTCN_error("Copying an uninitialized/unsupported template of type OBJECT.");
break;
}
}

OBJECT_template::OBJECT_template()
{
}

OBJECT_template::OBJECT_template(template_sel other_value)
 :
#ifdef TITAN_RUNTIME_2
Base_Class_Template
#else
Base_Template
#endif
(other_value)
{
check_single_selection(other_value);
}

OBJECT_template::OBJECT_template(OBJECT_template* p_precondition, OBJECT_template* p_implied_template)
 :
#ifdef TITAN_RUNTIME_2
Base_Class_Template
#else
Base_Template
#endif
(IMPLICATION_MATCH)
{
implication_.precondition = p_precondition;
implication_.implied_template = p_implied_template;
}

OBJECT_template::OBJECT_template(Dynamic_Match_Interface< OBJECT_REF<OBJECT> >* p_dyn_match)
 :
#ifdef TITAN_RUNTIME_2
Base_Class_Template
#else
Base_Template
#endif
(DYNAMIC_MATCH)
{
dyn_match = new dynmatch_struct< OBJECT_REF<OBJECT> >;
dyn_match->ptr = p_dyn_match;
dyn_match->ref_count = 1;
}

OBJECT_template::OBJECT_template(const OBJECT_template& other_value)
:
#ifdef TITAN_RUNTIME_2
Base_Class_Template
#else
Base_Template
#endif
()
{
copy_template(other_value);
}

OBJECT_template::~OBJECT_template()
{
clean_up();
}

OBJECT_template& OBJECT_template::operator=(template_sel other_value)
{
check_single_selection(other_value);
clean_up();
set_selection(other_value);
return *this;
}

OBJECT_template& OBJECT_template::operator=(const OBJECT_template& other_value)
{
if (&other_value != this) {
clean_up();
copy_template(other_value);
}
return *this;
}

boolean OBJECT_template::match(OBJECT_REF<OBJECT> other_value, boolean legacy) const
{
if (!other_value.is_present()) return FALSE;
switch (template_selection) {
case ANY_VALUE:
case ANY_OR_OMIT:
return TRUE;
case OMIT_VALUE:
return FALSE;
case SPECIFIC_VALUE:
for (unsigned int i = 0; i < single_value->n_assignments; ++i) {
switch (single_value->assignment_list[i].assignment_selection) {
case single_value_struct::toString:
if (!single_value->assignment_list[i].toString->match(other_value->toString(), legacy)) return FALSE;
break;
default:
TTCN_error("Internal error: invalid OBJECT object template assignment type");
}
}
return TRUE;
case VALUE_LIST:
case COMPLEMENTED_LIST:
for (unsigned int list_count = 0; list_count < value_list.n_values; ++list_count)
if (value_list.list_value[list_count].match(other_value, legacy)) return template_selection == VALUE_LIST;
return template_selection == COMPLEMENTED_LIST;
case CONJUNCTION_MATCH:
for (unsigned int i = 0; i < value_list.n_values; i++) {
if (!value_list.list_value[i].match(other_value)) {
return FALSE;
}
}
return TRUE;
case IMPLICATION_MATCH:
return !implication_.precondition->match(other_value) || implication_.implied_template->match(other_value);
case DYNAMIC_MATCH:
return dyn_match->ptr->match(other_value);
default:
TTCN_error("Matching an uninitialized/unsupported template of type OBJECT.");
}
return FALSE;
}

boolean OBJECT_template::is_bound() const
{
return template_selection != UNINITIALIZED_TEMPLATE || is_ifpresent;
}

boolean OBJECT_template::is_value() const
{
return FALSE;
}

void OBJECT_template::clean_up()
{
switch (template_selection) {
case SPECIFIC_VALUE:
for (unsigned int i = 0; i < single_value->n_assignments; ++i) {
switch (single_value->assignment_list[i].assignment_selection) {
case single_value_struct::toString:
delete single_value->assignment_list[i].toString;
break;
default:
TTCN_error("Internal error: invalid OBJECT object template assignment type");
}
}
Free(single_value->assignment_list);
delete single_value;
break;
case VALUE_LIST:
case COMPLEMENTED_LIST:
case CONJUNCTION_MATCH:
delete [] value_list.list_value;
break;
case IMPLICATION_MATCH:
delete implication_.precondition;
delete implication_.implied_template;
break;
case DYNAMIC_MATCH:
dyn_match->ref_count--;
if (dyn_match->ref_count == 0) {
delete dyn_match->ptr;
delete dyn_match;
}
break;
default:
break;
}
template_selection = UNINITIALIZED_TEMPLATE;
}

void OBJECT_template::set_type(template_sel template_type, unsigned int list_length)
{
if (template_type != VALUE_LIST && template_type != COMPLEMENTED_LIST&& template_type != CONJUNCTION_MATCH)
TTCN_error("Setting an invalid list for a template of type OBJECT.");
clean_up();
set_selection(template_type);
value_list.n_values = list_length;
value_list.list_value = new OBJECT_template[list_length];
}

void OBJECT_template::set_size(unsigned int new_size, boolean extend_)
{
if (!extend_ || template_selection != SPECIFIC_VALUE) {
clean_up();
set_selection(SPECIFIC_VALUE);
single_value = new single_value_struct;
single_value->assignment_list = (single_value_struct::assignment_struct*)Malloc(sizeof(single_value_struct::assignment_struct) * new_size);
single_value->n_assignments = new_size;
single_value->n_pos = 0;
} else {
single_value->n_assignments += new_size;
single_value->assignment_list = (single_value_struct::assignment_struct*)Realloc(single_value->assignment_list, sizeof(single_value_struct::assignment_struct) * single_value->n_assignments);
}
}

OBJECT_template& OBJECT_template::list_item(unsigned int list_index) const
{
if (template_selection != VALUE_LIST && template_selection != COMPLEMENTED_LIST&& template_selection != CONJUNCTION_MATCH)
TTCN_error("Accessing a list element of a non-list template of type OBJECT.");
if (list_index >= value_list.n_values)
TTCN_error("Index overflow in a value list template of type OBJECT.");
return value_list.list_value[list_index];
}

UNIVERSAL_CHARSTRING_template& OBJECT_template::toString()
{
if (template_selection != SPECIFIC_VALUE) TTCN_error("Attempting to add a function assignment in a non-specific template of type OBJECT.");
single_value->assignment_list[single_value->n_pos].assignment_selection = single_value_struct::toString;
single_value->assignment_list[single_value->n_pos].toString = new UNIVERSAL_CHARSTRING_template;
return *single_value->assignment_list[single_value->n_pos++].toString;
}

void OBJECT_template::log() const
{
switch (template_selection) {
case SPECIFIC_VALUE:
TTCN_Logger::log_event_str("{ ");
for (unsigned int i = 0; i < single_value->n_assignments; ++i) {
if (i > 0) TTCN_Logger::log_event_str(", ");
switch (single_value->assignment_list[i].assignment_selection) {
case single_value_struct::toString:
TTCN_Logger::log_event_str("toString() := ");
single_value->assignment_list[i].toString->log();
break;
default:
TTCN_error("Internal error: invalid OBJECT object template assignment type");
}
}
TTCN_Logger::log_event_str(" }");
break;
case COMPLEMENTED_LIST:
TTCN_Logger::log_event_str("complement");
case CONJUNCTION_MATCH:
if (template_selection == CONJUNCTION_MATCH) {
TTCN_Logger::log_event_str("conjunct");
}
case VALUE_LIST:
TTCN_Logger::log_char('(');
for (unsigned int list_count = 0; list_count < value_list.n_values; list_count++) {
if (list_count > 0) TTCN_Logger::log_event_str(", ");
value_list.list_value[list_count].log();
}
TTCN_Logger::log_char(')');
break;
case IMPLICATION_MATCH:
implication_.precondition->log();
TTCN_Logger::log_event_str(" implies ");
implication_.implied_template->log();
break;
case DYNAMIC_MATCH:
TTCN_Logger::log_event_str("@dynamic template");
break;
default:
log_generic();
}
log_ifpresent();
}

void OBJECT_template::log_match(OBJECT_REF<OBJECT> match_value, boolean legacy) const
{
if (TTCN_Logger::VERBOSITY_COMPACT == TTCN_Logger::get_matching_verbosity()) {
if (match(match_value, legacy)) {
TTCN_Logger::print_logmatch_buffer();
TTCN_Logger::log_event_str(" matched");
} else {
if (template_selection == SPECIFIC_VALUE) {
size_t previous_size = TTCN_Logger::get_logmatch_buffer_len();
for (unsigned int i = 0; i < single_value->n_assignments; ++i) {
switch (single_value->assignment_list[i].assignment_selection) {
case single_value_struct::toString: {
UNIVERSAL_CHARSTRING match_ret_val(match_value->toString());
if (!single_value->assignment_list[i].toString->match(match_ret_val, legacy)) {
TTCN_Logger::log_logmatch_info(".toString()");
single_value->assignment_list[i].toString->log_match(match_ret_val, legacy);
TTCN_Logger::set_logmatch_buffer_len(previous_size);
}
break; }
default:
TTCN_error("Internal error: invalid OBJECT object template assignment type");
}
}
} else {
TTCN_Logger::print_logmatch_buffer();
match_value.log();
TTCN_Logger::log_event_str(" with ");
log();
TTCN_Logger::log_event_str(" unmatched");
}
}
return;
}
if (template_selection == SPECIFIC_VALUE) {
TTCN_Logger::log_event_str("{ ");
for (unsigned int i = 0; i < single_value->n_assignments; ++i) {
if (i > 0) TTCN_Logger::log_event_str(", ");
switch (single_value->assignment_list[i].assignment_selection) {
case single_value_struct::toString:
TTCN_Logger::log_event_str("toString() := ");
single_value->assignment_list[i].toString->log_match(match_value->toString(), legacy);
break;
default:
TTCN_error("Internal error: invalid OBJECT object template assignment type");
}
}
TTCN_Logger::log_event_str(" }");
} else {
match_value.log();
TTCN_Logger::log_event_str(" with ");
log();
if (match(match_value, legacy)) TTCN_Logger::log_event_str(" matched");
else TTCN_Logger::log_event_str(" unmatched");
}
}

void OBJECT_template::check_restriction(template_res t_res, const char* t_name, boolean legacy) const
{
if (template_selection == UNINITIALIZED_TEMPLATE) return;
switch ((t_name != NULL && t_res == TR_VALUE) ? TR_OMIT : t_res) {
case TR_OMIT:
if (template_selection == OMIT_VALUE) return;
break;
case TR_VALUE:
break;
case TR_PRESENT:
if (!match_omit(legacy)) return;
break;
default:
return;
}
TTCN_error("Restriction `%s' on template of type %s violated.", get_res_name(t_res), t_name ? t_name : "OBJECT");
}

boolean OBJECT_template::is_present(boolean legacy) const
{
if (template_selection == UNINITIALIZED_TEMPLATE) return FALSE;
return !match_omit(legacy);
}

boolean OBJECT_template::match_omit(boolean legacy) const
{
if (is_ifpresent) return TRUE;
switch (template_selection) {
case OMIT_VALUE:
case ANY_OR_OMIT:
return TRUE;
case IMPLICATION_MATCH:
return !implication_.precondition->match_omit() || implication_.implied_template->match_omit();
case VALUE_LIST:
case COMPLEMENTED_LIST:
if (!legacy) return FALSE;
for (unsigned int l_idx = 0; l_idx < value_list.n_values; l_idx++)
if (value_list.list_value[l_idx].match_omit())
return template_selection == VALUE_LIST;
return template_selection == COMPLEMENTED_LIST;
return FALSE;
default:
return FALSE;
}
return FALSE;
}
