/******************************************************************************
 * 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 – initial implementation
 *
 ******************************************************************************/
#include "PER.hh"
#include "Integer.hh"
#include "Encdec.hh"
#include <cstdlib>
#include "Objid.hh"

const unsigned char FrontBitMask[9] = {
  0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF };

const unsigned char BackBitMask[9] = {
  0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF };

const unsigned char MiddleBitMask[8][9] = {
  { 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF },
  { 0x00, 0x40, 0x60, 0x70, 0x78, 0x7C, 0x7E, 0x7F, 0x00 },
  { 0x00, 0x20, 0x30, 0x38, 0x3C, 0x3E, 0x3F, 0x00, 0x00 },
  { 0x00, 0x10, 0x18, 0x1C, 0x1E, 0x1F, 0x00, 0x00, 0x00 },
  { 0x00, 0x08, 0x0C, 0x0E, 0x0F, 0x00, 0x00, 0x00, 0x00 },
  { 0x00, 0x04, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 },
  { 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
  { 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };

void PER_encode_opentype(TTCN_Buffer& p_main_buf, TTCN_Buffer& p_ot_buf, int p_options)
{
  size_t bit_pos = p_ot_buf.get_pos_bit() % 8;
  if (bit_pos != 0 || p_ot_buf.get_len() == 0) {
    // make sure the buffer is not empty, and that it contains a multiple of 8 bits
    unsigned char zero_bits = 0x00;
    p_ot_buf.PER_put_bits(8 - bit_pos, &zero_bits);
  }
  INTEGER length(p_ot_buf.get_len());
  int pos = 0;
  int mul_16k;
  do {
    mul_16k = length.PER_encode_length(p_main_buf, p_options);
    int encoded_octets = mul_16k == 0 ? (int)length : (mul_16k * PER_16K);
    p_main_buf.PER_put_bits(encoded_octets * 8, p_ot_buf.get_data() + pos);
    if (mul_16k > 0) {
      length = length - encoded_octets;
      pos += encoded_octets;
    }
  }
  while (mul_16k > 0);
}

void PER_decode_opentype(TTCN_Buffer& p_main_buf, TTCN_Buffer& p_ot_buf, int p_options)
{
  INTEGER length;
  int mul_16k;
  do {
    mul_16k = length.PER_decode_length(p_main_buf, p_options);
    Smart_Buffer per_buf(length);
    p_main_buf.PER_get_bits(per_buf.len * 8, per_buf.data);
    p_ot_buf.PER_put_bits(per_buf.len * 8, per_buf.data);
  }
  while (mul_16k > 0);
}

void PER_skip_opentype(TTCN_Buffer& p_main_buf, int p_options)
{
  INTEGER length;
  int mul_16k;
  do {
    mul_16k = length.PER_decode_length(p_main_buf, p_options);
    p_main_buf.PER_skip_bits(length * 8);
  }
  while (mul_16k > 0);
}

////////////////////////////////////////////////////////////////////////////////

Per_SetOf_Buffers::Per_SetOf_Buffers(int p_nof_elements)
: nof_elements(p_nof_elements)
{
  if (nof_elements > 0) {
    elements = new TTCN_Buffer*[nof_elements];
    for (int i = 0; i < nof_elements; ++i) {
      elements[i] = new TTCN_Buffer;
    }
  }
  else {
    elements = NULL;
  }
}

Per_SetOf_Buffers::~Per_SetOf_Buffers()
{
  if (elements != NULL) {
    for (int i = 0; i < nof_elements; ++i) {
      delete elements[i];
    }
    delete[] elements;
  }
}

TTCN_Buffer& Per_SetOf_Buffers::operator[](int p_index)
{
  if (p_index < 0 || p_index >= nof_elements) {
    TTCN_error("Internal error: PER set-of sorter index out of bounds");
  }
  return *elements[p_index];
}

int Per_Compare_Buffer(const void* p_left, const void* p_right)
{
  // compare two buffers bit-by-bit (both containing PER encodings);
  // if they are not of the same size, then treat the shorter one as if
  // it had zeros after its end
  const TTCN_Buffer* left_buf = *(const TTCN_Buffer* const *)p_left;
  const TTCN_Buffer* right_buf = *(const TTCN_Buffer* const *)p_right;
  size_t left_len = left_buf->get_len();
  size_t right_len = right_buf->get_len();
  size_t min_len = (left_len < right_len) ? left_len : right_len;
  const unsigned char* left_data = left_buf->get_data();
  const unsigned char* right_data = right_buf->get_data();
  for (size_t i = 0; i < min_len; ++i) {
    if (left_data[i] != right_data[i]) {
      return (int)left_data[i] - (int)right_data[i];
    }
  }
  if (left_len > min_len) {
    for (size_t i = min_len; i < left_len; ++i) {
      if (left_data[i] != 0x00) {
        return 1;
      }
    }
  }
  if (right_len > min_len) {
    for (size_t i = min_len; i < right_len; ++i) {
      if (right_data[i] != 0x00) {
        return -1;
      }
    }
  }
  return 0;
}

void Per_SetOf_Buffers::sort()
{
  qsort(elements, nof_elements, sizeof(TTCN_Buffer*), Per_Compare_Buffer);
}

////////////////////////////////////////////////////////////////////////////////

Per_Integer_Constraint::Per_Integer_Constraint(boolean p_ext)
: Per_Constraint(p_ext), setting(PER_INT_UNCONSTRAINED), val_a(NULL), val_b(NULL)
{
}

Per_Integer_Constraint::Per_Integer_Constraint(PerIntSetting p_setting, INTEGER* p_a, boolean p_ext)
: Per_Constraint(p_ext), setting(p_setting), val_a(p_a), val_b(NULL)
{
  switch (setting) {
  case PER_INT_SINGLE_VALUE:
  case PER_INT_RANGE_MINUS_INFINITY:
  case PER_INT_RANGE_PLUS_INFINITY:
    break; // OK
  default:
    TTCN_error("Internal error: Invalid PER integer constraint type: %d", (int) setting);
  }
}

Per_Integer_Constraint::Per_Integer_Constraint(INTEGER* p_a, INTEGER* p_b, boolean p_ext)
: Per_Constraint(p_ext), setting(PER_INT_RANGE_FINITE), val_a(p_a), val_b(p_b)
{
}

Per_Integer_Constraint::~Per_Integer_Constraint()
{
  switch (setting) {
  case PER_INT_RANGE_FINITE:
    delete val_b;
    // fall through
  case PER_INT_SINGLE_VALUE:
  case PER_INT_RANGE_MINUS_INFINITY:
  case PER_INT_RANGE_PLUS_INFINITY:
    delete val_a;
    break;
  default:
    break;
  }
}

INTEGER Per_Integer_Constraint::get_nof_values() const
{
  switch (setting) {
  case PER_INT_SINGLE_VALUE:
    return INTEGER(1);
  case PER_INT_RANGE_FINITE:
    return *val_b - *val_a + 1;
  default:
    return INTEGER(0);
  }
}

INTEGER Per_Integer_Constraint::get_lower_bound() const
{
  switch (setting) {
  case PER_INT_SINGLE_VALUE:
  case PER_INT_RANGE_FINITE:
  case PER_INT_RANGE_PLUS_INFINITY:
    return *val_a;
  default:
    TTCN_error("Internal error: Lower bound requested for invalid PER integer "
      "constraint type: %d", (int) setting);
  }
}

INTEGER Per_Integer_Constraint::get_upper_bound() const
{
  switch (setting) {
  case PER_INT_SINGLE_VALUE:
  case PER_INT_RANGE_MINUS_INFINITY:
    return *val_a;
  case PER_INT_RANGE_FINITE:
    return *val_b;
  default:
    TTCN_error("Internal error: Upper bound requested for invalid PER integer "
      "constraint type: %d", (int) setting);
  }
}

boolean Per_Integer_Constraint::has_lower_bound() const
{
  switch (setting) {
  case PER_INT_SINGLE_VALUE:
  case PER_INT_RANGE_FINITE:
  case PER_INT_RANGE_PLUS_INFINITY:
    return TRUE;
  default:
    return FALSE;
  }
}

boolean Per_Integer_Constraint::has_upper_bound() const
{
  switch (setting) {
  case PER_INT_SINGLE_VALUE:
  case PER_INT_RANGE_FINITE:
  case PER_INT_RANGE_MINUS_INFINITY:
    return TRUE;
  default:
    return FALSE;
  }
}

boolean Per_Integer_Constraint::is_within_extension_root(const INTEGER& x) const
{
  switch (setting) {
  case PER_INT_SINGLE_VALUE:
    return x == *val_a;
  case PER_INT_RANGE_FINITE:
    return x >= *val_a && x <= *val_b;
  case PER_INT_RANGE_PLUS_INFINITY:
    return x >= *val_a;
  case PER_INT_RANGE_MINUS_INFINITY:
    return x <= *val_a;
  default:
    return TRUE;
  }
}

////////////////////////////////////////////////////////////////////////////////

Per_Seq_Set_Constraint::Per_Seq_Set_Constraint(int* p_field_order, int p_nof_ext_adds,
                                               ext_add_info* p_ext_add_indexes, boolean p_ext)
: Per_Constraint(p_ext), field_order(p_field_order), nof_ext_adds(p_nof_ext_adds),
  ext_add_indexes(p_ext_add_indexes)
{
  if (p_field_order == NULL) {
    TTCN_error("Internal error: Missing field order for PER SEQUENCE/SET constraint");
  }
}

////////////////////////////////////////////////////////////////////////////////

Per_BitString_Constraint::Per_BitString_Constraint(boolean p_has_named_bits,
  const Per_Integer_Constraint* p_size_constraint)
: Per_Constraint(p_size_constraint->has_extension_marker()),
  named_bits(p_has_named_bits), size_constraint(p_size_constraint)
{
}

Per_BitString_Constraint::~Per_BitString_Constraint()
{
}

////////////////////////////////////////////////////////////////////////////////

static Per_String_Constraint::PER_String_Char_Set_Node NumericString_Char_Set[] = {
  { 32, FALSE }, // space
  { 48, TRUE },  // 0..
  { 57, FALSE }  // 9
};

static Per_String_Constraint::PER_String_Char_Set_Node PrintableString_Char_Set[] = {
  { 32, FALSE }, // space
  { 39, TRUE },  // ' (
  { 41, FALSE }, // )
  { 43, TRUE },  // + , - . / 0..
  { 58, FALSE }, // 9 :
  { 61, FALSE }, // =
  { 63, FALSE }, // ?
  { 65, TRUE },  // A..
  { 90, FALSE }, // Z
  { 97, TRUE },  // a..
  { 122, FALSE } // z
};

static Per_String_Constraint::PER_String_Char_Set_Node VisibleString_Char_Set[] = {
  { 32, TRUE },
  { 126, FALSE }
};

static Per_String_Constraint::PER_String_Char_Set_Node IA5String_Char_Set[] = {
  { 0, TRUE },
  { 127, FALSE }
};

static Per_String_Constraint::PER_String_Char_Set_Node BMPString_Char_Set[] = {
  { 0, TRUE },
  { 65535, FALSE }
};

static Per_String_Constraint::PER_String_Char_Set_Node UniversalString_Char_Set[] = {
  { 0, TRUE },
  { 0xFFFFFFFF, FALSE }
};

Per_String_Constraint::PER_String_Char_Set Per_String_Constraint::base_char_sets[] = {
  { sizeof(NumericString_Char_Set) / sizeof(NumericString_Char_Set[0]), NumericString_Char_Set },
  { sizeof(PrintableString_Char_Set) / sizeof(PrintableString_Char_Set[0]), PrintableString_Char_Set },
  { sizeof(VisibleString_Char_Set) / sizeof(VisibleString_Char_Set[0]), VisibleString_Char_Set },
  { sizeof(IA5String_Char_Set) / sizeof(IA5String_Char_Set[0]), IA5String_Char_Set },
  { sizeof(BMPString_Char_Set) / sizeof(BMPString_Char_Set[0]), BMPString_Char_Set },
  { sizeof(UniversalString_Char_Set) / sizeof(UniversalString_Char_Set[0]), UniversalString_Char_Set },
  { sizeof(VisibleString_Char_Set) / sizeof(VisibleString_Char_Set[0]), VisibleString_Char_Set }, // PER_GeneralizedTime
  { sizeof(VisibleString_Char_Set) / sizeof(VisibleString_Char_Set[0]), VisibleString_Char_Set } // PER_UTCTime
};

void Per_String_Constraint::init(boolean p_ext_bit)
{
  PER_String_Char_Set* current_char_set = p_ext_bit ? base_char_sets + string_type : char_set;
  // calculate the number of possible character values
  int idx = p_ext_bit ? 1 : 0;
  boolean in_interval = FALSE;
  PER_String_Char_Set_Node* nodes = current_char_set->nodes;
  for (int i = 0; i < current_char_set->nof_nodes; ++i) {
    if (in_interval) {
      nof_char_values[idx] += nodes[i].char_code - nodes[i - 1].char_code + 1;
    }
    else if (!nodes[i].starts_interval) {
      ++nof_char_values[idx];
    }
    in_interval = nodes[i].starts_interval;
  }
  // store the number of bits needed to encode all possible characters (unaligned)
  INTEGER nof_char_values_i;
  nof_char_values_i.set_long_long_val(nof_char_values[idx]);
  char_needed_bits[idx][0] = nof_char_values_i.PER_min_bits(TRUE, FALSE);
  // for the aligned version we need the lowest power of 2 bits that can encode all possible characters
  char_needed_bits[idx][1] = 1;
  while (char_needed_bits[idx][1] < char_needed_bits[idx][0]) {
    char_needed_bits[idx][1] *= 2;
  }

  // store whether to use the original character codes or 0..nof_chars-1
  INTEGER largest_char(nodes[current_char_set->nof_nodes - 1].char_code);
  int largest_char_bits_needed = largest_char.PER_min_bits(FALSE, FALSE);
  use_original_char_code[idx][0] = largest_char_bits_needed <= char_needed_bits[idx][0]; // unaligned
  use_original_char_code[idx][1] = largest_char_bits_needed <= char_needed_bits[idx][1]; // aligned

  // create the encoding and decoding tables if needed
  if (string_type != PER_UniversalString && string_type != PER_BMPString &&
      (!use_original_char_code[idx][0] || !use_original_char_code[idx][1])) {
    if (!use_original_char_code[idx][0]) { // unaligned
      encode_table[idx][0] = new unsigned long[largest_char + 1];
      decode_table[idx][0] = new unsigned long[nof_char_values[idx]];
    }
    if (!use_original_char_code[idx][1]) { // aligned
      encode_table[idx][1] = new unsigned long[largest_char + 1];
      decode_table[idx][1] = new unsigned long[nof_char_values[idx]];
    }
    unsigned long current_code = 0;
    in_interval = FALSE;
    for (int i = 0; i < current_char_set->nof_nodes; ++i) {
      if (in_interval) {
        for (unsigned long j = nodes[i - 1].char_code; j <= nodes[i].char_code; ++j) {
          if (!use_original_char_code[idx][0]) { // unaligned
            encode_table[idx][0][j] = current_code;
            decode_table[idx][0][current_code] = j;
          }
          if (!use_original_char_code[idx][1]) { // aligned
            encode_table[idx][1][j] = current_code;
            decode_table[idx][1][current_code] = j;
          }
          ++current_code;
        }
      }
      else if (!nodes[i].starts_interval) {
        if (!use_original_char_code[idx][0]) { // unaligned
          encode_table[idx][0][nodes[i].char_code] = current_code;
          decode_table[idx][0][current_code] = nodes[i].char_code;
        }
        if (!use_original_char_code[idx][1]) { // aligned
          encode_table[idx][1][nodes[i].char_code] = current_code;
          decode_table[idx][1][current_code] = nodes[i].char_code;
        }
        ++current_code;
      }
      in_interval = nodes[i].starts_interval;
    }
  }
}

Per_String_Constraint::Per_String_Constraint()
: Per_Constraint(FALSE), string_type(PER_UnknownMultiplierCharacterString),
  char_set(NULL), size_constraint(NULL)
{
}

Per_String_Constraint::Per_String_Constraint(PER_String_Type p_string_type, PER_String_Char_Set* p_char_set,
                                             const Per_Integer_Constraint* p_size_constraint)
: Per_Constraint(p_size_constraint->has_extension_marker()), string_type(p_string_type), size_constraint(p_size_constraint)
{
  switch (string_type) {
  case PER_NumericString:
  case PER_PrintableString:
  case PER_VisibleString:
  case PER_IA5String:
  case PER_BMPString:
  case PER_UniversalString:
  case PER_GeneralizedTime:
  case PER_UTCTime:
    char_set = p_char_set != NULL ? p_char_set : base_char_sets + string_type;
    init(FALSE);
    init(TRUE);
    break;
  default:
    string_type = PER_UnknownMultiplierCharacterString;
    TTCN_error("Internal error: Invalid string type in "
      "PER known-multiplier character string constraint");
    break;
  }
}

Per_String_Constraint::~Per_String_Constraint()
{
  if (string_type != PER_UnknownMultiplierCharacterString) {
    for (int i = 0; i < 2; ++i) {
      for (int j = 0; j < 2; ++j) {
        if (!use_original_char_code[i][j]) {
          delete[] encode_table[i][j];
          delete[] decode_table[i][j];
        }
      }
    }
  }
}

const char* Per_String_Constraint::get_string_type_name() const
{
  switch (string_type) {
  case PER_NumericString:
    return "NumericString";
  case PER_PrintableString:
    return "PrintableString";
  case PER_VisibleString:
    return "VisibleString";
  case PER_IA5String:
    return "IA5String";
  case PER_BMPString:
    return "BMPString";
  case PER_UniversalString:
    return "UniversalString";
  case PER_GeneralizedTime:
    return "GeneralizedTime";
  case PER_UTCTime:
    return "UTCTime";
  default:
    TTCN_error("Internal error: PER string type name requested for "
      "unknown multiplier character string");
    return NULL;
  }
}

boolean Per_String_Constraint::is_valid_char(unsigned long p_char_code, boolean p_ext_bit) const
{
  if (string_type == PER_UnknownMultiplierCharacterString) {
    TTCN_error("Internal error: PER character validation requested for "
      "unknown multiplier character string");
  }
  PER_String_Char_Set* current_char_set = p_ext_bit ? base_char_sets + string_type : char_set;
  boolean in_interval = FALSE;
  PER_String_Char_Set_Node* nodes = current_char_set->nodes;
  for (int i = 0; i < current_char_set->nof_nodes; ++i) {
    if (nodes[i].char_code == p_char_code || (in_interval &&
        p_char_code >= nodes[i - 1].char_code && p_char_code < nodes[i].char_code)) {
      return TRUE;
    }
    in_interval = nodes[i].starts_interval;
  }
  return FALSE;
}

int Per_String_Constraint::get_char_needed_bits(int p_options, boolean p_ext_bit) const
{
  if (string_type == PER_UnknownMultiplierCharacterString) {
    TTCN_error("Internal error: PER character bit count requested for "
      "unknown multiplier character string");
  }
  return char_needed_bits[p_ext_bit ? 1 : 0][(p_options & PER_ALIGNED) ? 1 : 0];
}

void Per_String_Constraint::encode(TTCN_Buffer& p_buf, unsigned long p_char_code,
                                   int p_options, boolean p_ext_bit) const
{
  if (string_type == PER_UnknownMultiplierCharacterString) {
    TTCN_error("Internal error: PER character encoding requested for "
      "unknown multiplier character string");
  }
  int a = (p_options & PER_ALIGNED) ? 1 : 0;
  int x = p_ext_bit ? 1 : 0;
  if (!use_original_char_code[x][a]) {
    switch (string_type) {
    case PER_UniversalString:
    case PER_BMPString: {
      // these don't have encode tables, because they might take up too much space;
      // go through the character set and find its position
      PER_String_Char_Set* current_char_set = p_ext_bit ? base_char_sets + string_type : char_set;
      boolean in_interval = FALSE;
      PER_String_Char_Set_Node* nodes = current_char_set->nodes;
      unsigned long current_code = 0;
      for (int i = 0; i < current_char_set->nof_nodes; ++i) {
        if (in_interval) {
          unsigned long start = nodes[i - 1].char_code;
          unsigned long end = nodes[i].char_code;
          if (p_char_code >= start && p_char_code <= end) {
            p_char_code = current_code + (p_char_code - start);
            break;
          }
          current_code += end - start + 1;
        }
        else if (!nodes[i].starts_interval) {
          if (nodes[i].char_code == p_char_code) {
            p_char_code = current_code;
            break;
          }
          ++current_code;
        }
        in_interval = nodes[i].starts_interval;
      }
      break; }
    default:
      // an encoding table has already been prepared for the rest of the types
      p_char_code = encode_table[x][a][p_char_code];
      break;
    }
  }
  INTEGER char_int;
  char_int.set_long_long_val(p_char_code);
  char_int.PER_encode_unaligned_constrained(p_buf, char_needed_bits[x][a]);
}

unsigned long Per_String_Constraint::decode(TTCN_Buffer& p_buf, int p_options, boolean p_ext_bit) const
{
  if (string_type == PER_UnknownMultiplierCharacterString) {
    TTCN_error("Internal error: PER character decoding requested for "
      "unknown multiplier character string");
  }
  int a = (p_options & PER_ALIGNED) ? 1 : 0;
  int x = p_ext_bit ? 1 : 0;
  INTEGER char_int;
  char_int.PER_decode_unaligned_constrained(p_buf, char_needed_bits[x][a], FALSE);
  unsigned long char_code = static_cast<unsigned long>(char_int.get_long_long_val());
  if (!use_original_char_code[x][a]) {
    if (static_cast<long long int>(char_code) >= nof_char_values[x]) {
      TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_INVAL_MSG,
        "Decoded invalid character in %s value (character encoded as %lu, expected 0 to %lu).",
        get_string_type_name(), char_code, static_cast<unsigned long>(nof_char_values[x] - 1));
    }
    switch (string_type) {
    case PER_UniversalString:
    case PER_BMPString: {
      // these don't have decode tables, because they might take up too much space;
      // go through the character set and find its position
      PER_String_Char_Set* current_char_set = p_ext_bit ? base_char_sets + string_type : char_set;
      boolean in_interval = FALSE;
      PER_String_Char_Set_Node* nodes = current_char_set->nodes;
      unsigned long current_code = 0;
      for (int i = 0; i < current_char_set->nof_nodes; ++i) {
        if (in_interval) {
          unsigned long start = nodes[i - 1].char_code;
          unsigned long len = nodes[i].char_code - start + 1;
          if (current_code + len > char_code) {
            char_code = start + (char_code - current_code);
            break;
          }
          current_code += len;
        }
        else if (!nodes[i].starts_interval) {
          if (current_code == char_code) {
            char_code = nodes[i].char_code;
            break;
          }
          ++current_code;
        }
        in_interval = nodes[i].starts_interval;
      }
      break; }
    default:
      // an encoding table has already been prepared for the rest of the types
      char_code = decode_table[x][a][char_code];
      break;
    }
  }
  return char_code;
}

unsigned long Per_String_Constraint::get_uchar_code(const universal_char& p_uchar)
{
  return (p_uchar.uc_group << 24) | (p_uchar.uc_plane << 16) | (p_uchar.uc_row << 8) | p_uchar.uc_cell;
}

universal_char Per_String_Constraint::get_uchar_from_code(unsigned long p_code)
{
  return { static_cast<unsigned char>(p_code >> 24), static_cast<unsigned char>((p_code >> 16) & 0xFF),
    static_cast<unsigned char>((p_code >> 8) & 0xFF), static_cast<unsigned char>(p_code & 0xFF) };
}

////////////////////////////////////////////////////////////////////////////////

Per_Embedded_Pdv_Constraint::Per_Embedded_Pdv_Constraint(PER_EPDV_Case p_case)
: Per_Constraint(FALSE), enc_case(p_case), objid1(NULL), objid2(NULL)
{
  switch (enc_case) {
  case PER_EPDV_GENERAL:
  case PER_EPDV_PREDEFINED_FIXED:
    break; // OK
  default:
    TTCN_error("Internal error: Invalid PER embedded pdv constraint type: %d", (int) enc_case);
  }
}

Per_Embedded_Pdv_Constraint::Per_Embedded_Pdv_Constraint(OBJID* p_objid1, OBJID* p_objid2)
: Per_Constraint(FALSE), enc_case(PER_EPDV_PREDEFINED_SYNTAXES), objid1(p_objid1), objid2(p_objid2)
{
}

Per_Embedded_Pdv_Constraint::~Per_Embedded_Pdv_Constraint()
{
  delete objid1;
  delete objid2;
}

////////////////////////////////////////////////////////////////////////////////

static Per_Constraint default_empty_per_cons_;
const ASN_PERdescriptor_t FLOAT_per_ = { &default_empty_per_cons_ };
const ASN_PERdescriptor_t BOOLEAN_per_ = { &default_empty_per_cons_ };
const ASN_PERdescriptor_t ASN_NULL_per_ = { &default_empty_per_cons_ };
const ASN_PERdescriptor_t OBJID_per_ = { &default_empty_per_cons_ };
const ASN_PERdescriptor_t ASN_ROID_per_ = { &default_empty_per_cons_ };
const ASN_PERdescriptor_t EXTERNAL_per_ = { &default_empty_per_cons_ };

static Per_Integer_Constraint INTEGER_per_cons_;
const ASN_PERdescriptor_t INTEGER_per_ = { &INTEGER_per_cons_ };

static Per_Integer_Constraint default_size_per_cons_(Per_Integer_Constraint::PER_INT_RANGE_PLUS_INFINITY, new INTEGER(0), FALSE);
const ASN_PERdescriptor_t OCTETSTRING_per_ = { &default_size_per_cons_ };
const ASN_PERdescriptor_t ASN_ANY_per_ = { &default_size_per_cons_ };

static Per_BitString_Constraint BITSTRING_per_cons_(FALSE, &default_size_per_cons_);
const ASN_PERdescriptor_t BITSTRING_per_ = { &BITSTRING_per_cons_ };

static Per_String_Constraint NumericString_per_cons_(Per_String_Constraint::PER_NumericString, NULL, &default_size_per_cons_);
const ASN_PERdescriptor_t NumericString_per_ = { &NumericString_per_cons_ };

static Per_String_Constraint PrintableString_per_cons_(Per_String_Constraint::PER_PrintableString, NULL, &default_size_per_cons_);
const ASN_PERdescriptor_t PrintableString_per_ = { &PrintableString_per_cons_ };

static Per_String_Constraint VisibleString_per_cons_(Per_String_Constraint::PER_VisibleString, NULL, &default_size_per_cons_);
const ASN_PERdescriptor_t VisibleString_per_ = { &VisibleString_per_cons_ };

static Per_String_Constraint IA5String_per_cons_(Per_String_Constraint::PER_IA5String, NULL, &default_size_per_cons_);
const ASN_PERdescriptor_t IA5String_per_ = { &IA5String_per_cons_ };

static Per_String_Constraint BMPString_per_cons_(Per_String_Constraint::PER_BMPString, NULL, &default_size_per_cons_);
const ASN_PERdescriptor_t BMPString_per_ = { &BMPString_per_cons_ };

static Per_String_Constraint UniversalString_per_cons_(Per_String_Constraint::PER_UniversalString, NULL, &default_size_per_cons_);
const ASN_PERdescriptor_t UniversalString_per_ = { &UniversalString_per_cons_ };

static Per_String_Constraint unknown_multiplier_character_string_per_cons_;
const ASN_PERdescriptor_t GraphicString_per_ = { &unknown_multiplier_character_string_per_cons_ };
const ASN_PERdescriptor_t UTF8String_per_ = { &unknown_multiplier_character_string_per_cons_ };
const ASN_PERdescriptor_t GeneralString_per_ = { &unknown_multiplier_character_string_per_cons_ };
const ASN_PERdescriptor_t TeletexString_per_ = { &unknown_multiplier_character_string_per_cons_ };
const ASN_PERdescriptor_t VideotexString_per_ = { &unknown_multiplier_character_string_per_cons_ };
const ASN_PERdescriptor_t ObjectDescriptor_per_ = { &unknown_multiplier_character_string_per_cons_ };

static Per_String_Constraint GeneralizedTime_per_cons_(Per_String_Constraint::PER_GeneralizedTime, NULL, &default_size_per_cons_);
const ASN_PERdescriptor_t ASN_GeneralizedTime_per_ = { &GeneralizedTime_per_cons_ };

static Per_String_Constraint UTCTTime_per_cons_(Per_String_Constraint::PER_UTCTime, NULL, &default_size_per_cons_);
const ASN_PERdescriptor_t ASN_UTCTime_per_ = { &UTCTTime_per_cons_ };

static Per_Embedded_Pdv_Constraint EMBEDDED_PDV_per_cons_(Per_Embedded_Pdv_Constraint::PER_EPDV_GENERAL);
const ASN_PERdescriptor_t EMBEDDED_PDV_per_ = { &EMBEDDED_PDV_per_cons_ };
const ASN_PERdescriptor_t CHARACTER_STRING_per_ = { &EMBEDDED_PDV_per_cons_ };