/******************************************************************************
 * Copyright (c) 2000-2021 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
 *   Baranyi, Botond
 *   Delic, Adam
 *   Forstner, Matyas
 *   Raduly, Csaba
 *   Szabo, Bence Janos
 *   Szabo, Janos Zoltan – initial implementation
 *   Zalanyi, Balazs Andor
 *
 ******************************************************************************/
#include "PatternString.hh"
#include "../../common/pattern.hh"
#include "../CompilerError.hh"
#include "../Code.hh"
#include "../../common/JSON_Tokenizer.hh"
#include "../main.hh"

#include "TtcnTemplate.hh"

namespace Ttcn {

  // =================================
  // ===== PatternString::ps_elem_t
  // =================================

  struct PatternString::ps_elem_t {
    enum kind_t {
      PSE_STR,
      PSE_REF,
      PSE_REFDSET
    } kind;
    string *str;
    Ttcn::Reference *ref;
    Type * t; // The type of the reference in the case of PSE_REFDSET
    boolean with_N; // If the reference was given as \N{ref} in the pattern
    boolean is_charstring; // \N{charstring}
    boolean is_universal_charstring; // \N{universal charstring}
    ps_elem_t(kind_t p_kind, const string& p_str);
    ps_elem_t(kind_t p_kind, Ttcn::Reference *p_ref, boolean N);
    ps_elem_t(const ps_elem_t& p);
    ~ps_elem_t();
    ps_elem_t* clone() const;
    void set_fullname(const string& p_fullname);
    void set_my_scope(Scope *p_scope);
    void chk_ref(PatternString::pstr_type_t pstr_type, Type::expected_value_t expected_value,
      namedbool class_member_init);
    void set_code_section(GovernedSimple::code_section_t p_code_section);
  };

  PatternString::ps_elem_t::ps_elem_t(kind_t p_kind, const string& p_str)
    : kind(p_kind), ref(NULL), t(NULL), with_N(FALSE), is_charstring(FALSE),
    is_universal_charstring(FALSE)
  {
    str = new string(p_str);
  }

  PatternString::ps_elem_t::ps_elem_t(kind_t p_kind, Ttcn::Reference *p_ref, boolean N)
    : kind(p_kind), str(NULL), with_N(N), is_charstring(FALSE),
    is_universal_charstring(FALSE)
  {
    if (!p_ref) FATAL_ERROR("PatternString::ps_elem_t::ps_elem_t()");
    ref = p_ref;
  }
  
  PatternString::ps_elem_t::ps_elem_t(const PatternString::ps_elem_t& p)
    : kind(p.kind), str(NULL), ref(NULL), t(NULL), with_N(FALSE),
    is_charstring(FALSE), is_universal_charstring(FALSE)
  {
    switch(kind) {
    case PSE_STR:
      str = new string(*p.str);
      break;
    case PSE_REF:
      ref = p.ref->clone();
      break;
    case PSE_REFDSET:
      FATAL_ERROR("PatternString::ps_elem_t::ps_elem_t");
    }
  }

  PatternString::ps_elem_t::~ps_elem_t()
  {
    switch(kind) {
    case PSE_STR:
      delete str;
      // fall through
    case PSE_REF:
    case PSE_REFDSET:
      delete ref;
      // do not delete t
      break;
    } // switch kind
  }

  PatternString::ps_elem_t* PatternString::ps_elem_t::clone() const
  {
    return new ps_elem_t(*this);
  }

  void PatternString::ps_elem_t::set_fullname(const string& p_fullname)
  {
    switch(kind) {
    case PSE_REF:
    case PSE_REFDSET:
      ref->set_fullname(p_fullname);
      break;
    default:
      ;
    } // switch kind
  }

  void PatternString::ps_elem_t::set_my_scope(Scope *p_scope)
  {
    switch(kind) {
    case PSE_REF:
    case PSE_REFDSET:
      ref->set_my_scope(p_scope);
      break;
    default:
      ;
    } // switch kind
  }

  void PatternString::ps_elem_t::chk_ref(PatternString::pstr_type_t pstr_type, Type::expected_value_t expected_value,
                                         namedbool class_member_init)
  {
    if (kind != PSE_REF) FATAL_ERROR("PatternString::ps_elem_t::chk_ref()");
    Value* v = 0;
    Value* v_last = 0;
    if (ref->get_id()->get_name() == "CHARSTRING") {
      is_charstring = TRUE;
      return;
    } else if (ref->get_id()->get_name() == "UNIVERSAL_CHARSTRING") {
      is_universal_charstring = TRUE;
      return;
    }
    Common::Assignment* ass = ref->get_refd_assignment();
    if (!ass)
      return;
    Ttcn::FieldOrArrayRefs* t_subrefs = ref->get_subrefs();
    Type* ref_type = ass->get_Type()->get_type_refd_last()->get_field_type(
      t_subrefs, expected_value);
    Type::typetype_t tt;
    switch (pstr_type) {
      case PatternString::CSTR_PATTERN:
        tt = Type::T_CSTR;
        if (ref_type->get_typetype() != Type::T_CSTR)
          TTCN_pattern_error("Type of the referenced %s '%s' should be "
            "'charstring'", ass->get_assname(), ref->get_dispname().c_str());
        break;
      case PatternString::USTR_PATTERN:
        tt = ref_type->get_typetype();
        if (tt != Type::T_CSTR && tt != Type::T_USTR)
          TTCN_pattern_error("Type of the referenced %s '%s' should be either "
            "'charstring' or 'universal charstring'", ass->get_assname(),
            ref->get_dispname().c_str());
        break;
      default:
        FATAL_ERROR("Unknown pattern string type");
    }
    Type* refcheckertype = Type::get_pooltype(tt);
    switch (ass->get_asstype()) {
    case Common::Assignment::A_TYPE:
      kind = PSE_REFDSET;
      t = ass->get_Type();
      break;
    case Common::Assignment::A_MODULEPAR_TEMP:
    case Common::Assignment::A_VAR_TEMPLATE: 
    case Common::Assignment::A_PAR_TEMPL_IN:
    case Common::Assignment::A_PAR_TEMPL_OUT:
    case Common::Assignment::A_PAR_TEMPL_INOUT:
      // error reporting moved up
      break;
    case Common::Assignment::A_TEMPLATE: {
      Template* templ = ass->get_Template();
      refcheckertype->chk_this_template_ref(templ);
      refcheckertype->chk_this_template_generic(templ, INCOMPLETE_ALLOWED,
        OMIT_ALLOWED, ANY_OR_OMIT_ALLOWED, SUB_CHK, NOT_IMPLICIT_OMIT, class_member_init, 0);
      switch (templ->get_templatetype()) {
      case Template::SPECIFIC_VALUE:
        v_last = templ->get_specific_value();
        break;
      case Template::TEMPLATE_CONCAT:
        if (!use_runtime_2) {
          FATAL_ERROR("PatternString::ps_elem_t::chk_ref()");
        }
        if (templ->is_Value()) {
          v = templ->get_Value();
          v->set_my_governor(refcheckertype);
          v->set_my_scope(ref->get_my_scope());
          v->set_location(*ref);
          refcheckertype->chk_this_value(v, 0, expected_value,
            INCOMPLETE_NOT_ALLOWED, OMIT_NOT_ALLOWED, SUB_CHK);
          v_last = v->get_value_refd_last();
        }
        else {
          TTCN_pattern_error("Unable to resolve referenced '%s' to character "
            "string type. Result of template concatenation is not a specific "
            "value.", ref->get_dispname().c_str());
        }
        break;
      case Template::CSTR_PATTERN: 
        if (!with_N) {
          Ttcn::PatternString* ps = templ->get_cstr_pattern();
          if (!ps->has_refs())
            v_last = ps->get_value();
          break;
        }
      case Template::USTR_PATTERN: 
        if (!with_N) {
          Ttcn::PatternString* ps = templ->get_ustr_pattern();
          if (!ps->has_refs())
            v_last = ps->get_value();
          break;
        }
      default:
        TTCN_pattern_error("Unable to resolve referenced '%s' to character "
          "string type. '%s' template cannot be used.",
          ref->get_dispname().c_str(), templ->get_templatetype_str());
        break;
      }
      break; }
    default: {
      Reference *t_ref = ref->clone();
      t_ref->set_location(*ref);
      v = new Value(Value::V_REFD, t_ref);
      v->set_my_governor(refcheckertype);
      v->set_my_scope(ref->get_my_scope());
      v->set_location(*ref);
      refcheckertype->chk_this_value(v, 0, expected_value,
        INCOMPLETE_NOT_ALLOWED, OMIT_NOT_ALLOWED, SUB_CHK,
        NOT_IMPLICIT_OMIT, NOT_STR_ELEM, class_member_init);
      v_last = v->get_value_refd_last();
    }
    }
    if (v_last && (v_last->get_valuetype() == Value::V_CSTR ||
      v_last->get_valuetype() == Value::V_USTR)) {
      // the reference points to a constant
      // substitute the reference with the known value
      if (v_last->get_valuetype() == Value::V_CSTR) {
        if (with_N && v_last->get_val_str().size() != 1) {
          ref->error("The length of the charstring must be of length one, when it is being referenced in a pattern with \\N{ref}");
        }
        str = new string(v_last->get_val_str());
      } else {
        if (with_N && v_last->get_val_ustr().size() != 1) {
          ref->error("The length of the universal charstring must be of length one, when it is being referenced in a pattern with \\N{ref}");
        }
        str = new string(v_last->get_val_ustr().get_stringRepr_for_pattern());
      }
      kind = PSE_STR;
    }
    delete v;
  }

  void PatternString::ps_elem_t::set_code_section
    (GovernedSimple::code_section_t p_code_section)
  {
    switch(kind) {
    case PSE_REF:
    case PSE_REFDSET:
      ref->set_code_section(p_code_section);
      break;
    default:
      ;
    } // switch kind
  }

  // =================================
  // ===== PatternString
  // =================================

  PatternString::PatternString(const PatternString& p)
    : Node(p), my_scope(0), cstr_value(0), pattern_type(p.pattern_type),
      nocase(p.nocase)
  {
    size_t nof_elems = p.elems.size();
    for (size_t i = 0; i < nof_elems; i++) elems.add(p.elems[i]->clone());
  }

  PatternString::ps_elem_t *PatternString::get_last_elem() const
  {
    if (elems.empty()) return 0;
    ps_elem_t *last_elem = elems[elems.size() - 1];
    if (last_elem->kind == ps_elem_t::PSE_STR) return last_elem;
    else return 0;
  }

  PatternString::~PatternString()
  {
    size_t nof_elems = elems.size();
    for (size_t i = 0; i < nof_elems; i++) delete elems[i];
    elems.clear();
    delete cstr_value;
  }

  PatternString *PatternString::clone() const
  {
    return new PatternString(*this);
  }

  void PatternString::set_fullname(const string& p_fullname)
  {
    Node::set_fullname(p_fullname);
    size_t nof_elems = elems.size();
    for(size_t i = 0; i < nof_elems; i++) elems[i]->set_fullname(p_fullname);
  }

  void PatternString::set_my_scope(Scope *p_scope)
  {
    my_scope = p_scope;
    size_t nof_elems = elems.size();
    for (size_t i = 0; i < nof_elems; i++) elems[i]->set_my_scope(p_scope);
  }

  void PatternString::set_code_section
    (GovernedSimple::code_section_t p_code_section)
  {
    size_t nof_elems = elems.size();
    for (size_t i = 0; i < nof_elems; i++)
      elems[i]->set_code_section(p_code_section);
  }

  void PatternString::addChar(char c)
  {
    ps_elem_t *last_elem = get_last_elem();
    if (last_elem) *last_elem->str += c;
    else elems.add(new ps_elem_t(ps_elem_t::PSE_STR, string(c)));
  }

  void PatternString::addString(const char *p_str)
  {
    ps_elem_t *last_elem = get_last_elem();
    if (last_elem) *last_elem->str += p_str;
    else elems.add(new ps_elem_t(ps_elem_t::PSE_STR, string(p_str)));
  }

  void PatternString::addString(const string& p_str)
  {
    ps_elem_t *last_elem = get_last_elem();
    if (last_elem) *last_elem->str += p_str;
    else elems.add(new ps_elem_t(ps_elem_t::PSE_STR, p_str));
  }
  
  void PatternString::addStringUSI(char **usi_str, const size_t size)
  {
    ustring s = ustring(const_cast<const char**>(usi_str), size);
    ps_elem_t *last_elem = get_last_elem();
    if (last_elem) *last_elem->str += s.get_stringRepr_for_pattern().c_str();
    else elems.add(new ps_elem_t(ps_elem_t::PSE_STR, s.get_stringRepr_for_pattern()));
  }

  void PatternString::addRef(Ttcn::Reference *p_ref, boolean N)
  {
    elems.add(new ps_elem_t(ps_elem_t::PSE_REF, p_ref, N));
  }

  string PatternString::get_full_str() const
  {
    string s;
    for(size_t i=0; i<elems.size(); i++) {
      ps_elem_t *pse=elems[i];
      switch(pse->kind) {
      case ps_elem_t::PSE_STR:
        s+=*pse->str;
        break;
      case ps_elem_t::PSE_REFDSET:
        s+="\\N";
        /* no break */
      case ps_elem_t::PSE_REF:
        s+='{';
        s+=pse->ref->get_dispname();
        s+='}';
      } // switch kind
    } // for
    return s;
  }

  void PatternString::set_pattern_type(pstr_type_t p_type) {
    pattern_type = p_type;
  }

  PatternString::pstr_type_t PatternString::get_pattern_type() const {
    return pattern_type;
  }

  bool PatternString::has_refs() const
  {
    for (size_t i = 0; i < elems.size(); i++) {
      switch (elems[i]->kind) {
      case ps_elem_t::PSE_REF:
      case ps_elem_t::PSE_REFDSET:
        return true;
      default:
        break;
      }
    }
    return false;
  }

  void PatternString::chk_refs(Type::expected_value_t expected_value, namedbool class_member_init)
  {
    for(size_t i=0; i<elems.size(); i++) {
      ps_elem_t *pse=elems[i];
      switch(pse->kind) {
      case ps_elem_t::PSE_STR:
        break;
      case ps_elem_t::PSE_REFDSET:
        /* actually, not supported */
        break;
      case ps_elem_t::PSE_REF:
        pse->chk_ref(pattern_type, expected_value, class_member_init);
        break;
      } // switch kind
    } // for
  }

  /** \todo implement */
  void PatternString::chk_recursions(ReferenceChain&)
  {

  }

  void PatternString::chk_pattern()
  {
    string str;
    for (size_t i = 0; i < elems.size(); i++) {
      ps_elem_t *pse = elems[i];
      if (pse->kind != ps_elem_t::PSE_STR)
	      FATAL_ERROR("PatternString::chk_pattern()");
      str += *pse->str;
    }
    char* posix_str = 0;
    switch (pattern_type) {
      case CSTR_PATTERN:
        posix_str = TTCN_pattern_to_regexp(str.c_str());
        break;
      case USTR_PATTERN:
        posix_str = TTCN_pattern_to_regexp_uni(str.c_str(), nocase);
    }
    Free(posix_str);
  }

  bool PatternString::chk_self_ref(Common::Assignment *lhs)
  {
    for (size_t i = 0, e = elems.size(); i < e; ++i) {
      ps_elem_t *pse = elems[i];
      switch (pse->kind) {
      case ps_elem_t::PSE_STR:
        break;
      case ps_elem_t::PSE_REFDSET:
        /* actually, not supported */
        break;
      case ps_elem_t::PSE_REF: {
        Ttcn::Assignment *ass = pse->ref->get_refd_assignment();
        if (ass == lhs) return true;
        break; }
      } // switch
    }
    return false;
  }

  void PatternString::join_strings()
  {
    // points to the previous string element otherwise it is NULL
    ps_elem_t *prev_str = 0;
    for (size_t i = 0; i < elems.size(); ) {
      ps_elem_t *pse = elems[i];
      if (pse->kind == ps_elem_t::PSE_STR) {
        const string& str = *pse->str;
	if (str.size() > 0) {
	  // the current element is a non-empty string
	  if (prev_str) {
	    // append str to prev_str and drop pse
	    *prev_str->str += str;
	    delete pse;
	    elems.replace(i, 1);
	    // don't increment i
	  } else {
	    // keep pse for the next iteration
	    prev_str = pse;
	    i++;
	  }
	} else {
	  // the current element is an empty string
	  // simply drop it
	  delete pse;
	  elems.replace(i, 1);
	  // don't increment i
	}
      } else {
        // pse is not a string
	// forget prev_str
	prev_str = 0;
	i++;
      }
    }
  }

  string PatternString::create_charstring_literals(Common::Module *p_mod, string& preamble)
  {
    /* The cast is there for the benefit of OPTIONAL<CHARSTRING>, because
     * it doesn't have operator+(). In most cases only the first member needs
     * the cast (the others will be automagically converted to satisfy
     * CHARSTRING::operator+(const CHARSTRING&) ) */
    string s;
    if (pattern_type == CSTR_PATTERN)
      s = "CHARSTRING_template(STRING_PATTERN, (CHARSTRING)";
    else
      s = "UNIVERSAL_CHARSTRING_template(STRING_PATTERN, (CHARSTRING)";
    size_t nof_elems = elems.size();
    if (nof_elems > 0) {
      // the pattern is not empty
    for (size_t i = 0; i < nof_elems; i++) {
      if (i > 0) s += " + ";
      ps_elem_t *pse = elems[i];
      
      // \N{charstring} and \N{universal charstring}
      if (pse->is_charstring) {
        s += p_mod->add_charstring_literal(string("?"));
        continue;
      } else if (pse->is_universal_charstring) {
        s += p_mod->add_charstring_literal(string("?"));
        continue;
      }

      switch (pse->kind) {
        // Known in compile time: string literal, const etc.
        case ps_elem_t::PSE_STR:
          s += p_mod->add_charstring_literal(*pse->str);
          break;
        // Known in compile time: string type with(out) range or list
        case ps_elem_t::PSE_REFDSET: {
          if (!pse->t)
            FATAL_ERROR("PatternString::create_charstring_literals()");
          if (!pse->t->get_sub_type()) {
            // just a string type without any restrictions (or alias)
            s += "\"?\"";
            continue;
          }
          vector<Common::SubTypeParse> * vec = pse->t->get_sub_type()->get_subtype_parsed();

          while (vec == NULL) { // go through aliases to find where the restrictions are
            if (pse->t->get_Reference()) {
              pse->t = pse->t->get_Reference()->get_refd_assignment(FALSE)->get_Type();
            } else {
              break;
            }
            if (pse->t->get_sub_type()) {
              vec = pse->t->get_sub_type()->get_subtype_parsed();
            } else {
              break;
            }
          }
          if (vec == NULL) {
            // I don't think it can happen, but to be sure...
            s += "\"?\"";
            continue;
          }
          s+= "\"";
          if (vec->size() > 1) s+= "(";
          for (size_t j = 0; j < vec->size(); j++) {
            Common::SubTypeParse* stp = (*vec)[j];
            if (j > 0) {
              s+="|\"+"; // todo what if default    
            }else {
              s+= "\"+";
            }
            switch (stp->get_selection()) {
              case SubTypeParse::STP_RANGE: // type charstring ("a" .. "z")
                s+="\"[\" + ";
                switch (stp->Min()->get_valuetype()) {
                  case Value::V_CSTR:
                    s+= p_mod->add_charstring_literal(stp->Min()->get_val_str());
                    s+= "+ \"-\" +";
                    s+= p_mod->add_charstring_literal(stp->Max()->get_val_str());
                    break;
                  case Value::V_USTR:
                    s+= p_mod->add_charstring_literal(stp->Min()->get_val_ustr().get_stringRepr_for_pattern());
                    s+= "+ \"-\" +";
                    s+= p_mod->add_charstring_literal(stp->Max()->get_val_ustr().get_stringRepr_for_pattern());
                    break;
                  default:
                    FATAL_ERROR("PatternString::create_charstring_literals()");
                }
                s+=" + \"]\"";
                break;
              case SubTypeParse::STP_SINGLE: // type charstring ("a", "b")
                switch (stp->Single()->get_valuetype()) {
                  case Value::V_CSTR:
                    s+= p_mod->add_charstring_literal(stp->Single()->get_val_str());
                    break;
                  case Value::V_USTR:
                    s+= p_mod->add_charstring_literal(stp->Single()->get_val_ustr().get_stringRepr_for_pattern());
                    break;
                  default:
                    FATAL_ERROR("PatternString::create_charstring_literals()");
                    break;
                }
                break;
              default:
                FATAL_ERROR("PatternString::create_charstring_literals()");
            }
            s+= "+\"";
          }
          if (vec->size() > 1) s+= ")";
          s+= "\"";
          break; }
        // Not known in compile time
        case ps_elem_t::PSE_REF: {
          Common::Assignment* assign = pse->ref->get_refd_assignment();
          if (use_runtime_2 && i > 0 &&
              assign->get_Type()->field_is_optional(pse->ref->get_subrefs())) {
            // in RT2 convert all operands of type OPTIONAL<CHARSTRING> to
            // CHARSTRING, not just the first one
            s += "(CHARSTRING)";
          }
          expression_struct expr;
          Code::init_expr(&expr);
          pse->ref->generate_code(&expr);
          if (expr.preamble || expr.postamble)
            FATAL_ERROR("PatternString::create_charstring_literals()");
          s += expr.expr;
          char* str = NULL;
          
          // TODO: these checks will generated each time a reference is referenced in a pattern
          // and it could be generated once
          if (pse->with_N) {
            if ((assign->get_asstype() == Common::Assignment::A_TEMPLATE
                  || assign->get_asstype() == Common::Assignment::A_MODULEPAR_TEMP
                  || assign->get_asstype() == Common::Assignment::A_VAR_TEMPLATE
                  || assign->get_asstype() == Common::Assignment::A_PAR_TEMPL_IN
                  || assign->get_asstype() == Common::Assignment::A_PAR_TEMPL_OUT
                  || assign->get_asstype() == Common::Assignment::A_PAR_TEMPL_INOUT))
            {
            string value_literal = p_mod->add_charstring_literal(string("value"));
            str = mputprintf(str,
              "if (%s.get_istemplate_kind(%s) == FALSE) {\n"
              "TTCN_error(\"Only specific value template allowed in pattern reference with \\\\N{ref}\");\n"
              "}\n"
              , expr.expr
              , value_literal.c_str());
            }
            
            str = mputprintf(str,
              "if (%s.lengthof() != 1)\n"
              "{\n"
              "TTCN_error(\"The length of the %scharstring must be of length one, when it is being referenced in a pattern with \\\\N{ref}\");\n"
              "}\n"
              , expr.expr
              , assign->get_Type()->get_typetype() == Type::T_USTR ? "universal " : "");
            preamble += str;
            Free(str);
          }
          if ((assign->get_asstype() == Common::Assignment::A_TEMPLATE
            || assign->get_asstype() == Common::Assignment::A_MODULEPAR_TEMP
            || assign->get_asstype() == Common::Assignment::A_VAR_TEMPLATE
            || assign->get_asstype() == Common::Assignment::A_PAR_TEMPL_IN
            || assign->get_asstype() == Common::Assignment::A_PAR_TEMPL_OUT
            || assign->get_asstype() == Common::Assignment::A_PAR_TEMPL_INOUT))
          {
            if ((assign->get_Type()->get_typetype() == Type::T_CSTR
              || assign->get_Type()->get_typetype() == Type::T_USTR) && !pse->with_N) {
              s += ".get_single_value()";
            }
            else if (assign->get_Type()->get_typetype() == Type::T_USTR && pse->with_N) {
              s += ".valueof().get_stringRepr_for_pattern()";
            } else {
              s += ".valueof()";
            }
          } else if ((assign->get_asstype() == Common::Assignment::A_MODULEPAR
            || assign->get_asstype() == Common::Assignment::A_VAR
            || assign->get_asstype() == Common::Assignment::A_EXCEPTION
            || assign->get_asstype() == Common::Assignment::A_PAR_VAL
            || assign->get_asstype() == Common::Assignment::A_PAR_VAL_IN
            || assign->get_asstype() == Common::Assignment::A_PAR_VAL_OUT
            || assign->get_asstype() == Common::Assignment::A_PAR_VAL_INOUT)
            && assign->get_Type()->get_typetype() == Type::T_USTR) {
            s += ".get_stringRepr_for_pattern()";
          }

          Code::free_expr(&expr);
          break; }
        } // switch kind
      } // for
    } else {
      // empty pattern: create an empty string literal for it
      s += p_mod->add_charstring_literal(string());
    }
    s += ", ";
    s += nocase ? "TRUE" : "FALSE";
    s += ')';
    return s;
  }

  void PatternString::dump(unsigned level) const
  {
    if (nocase) {
      DEBUG(level, "@nocase");
    }
    DEBUG(level, "%s", get_full_str().c_str());
  }

  Common::Value* PatternString::get_value() {
    if (!cstr_value && !has_refs())
      cstr_value = new Common::Value(Common::Value::V_CSTR,
        new string(get_full_str()));
    return cstr_value;
  }
  
  char* PatternString::convert_to_json()
  {
    string pstr = get_value()->get_val_str();
    
    // convert the pattern into an extended regular expression
    char* regex_str = NULL;
    if (CSTR_PATTERN == pattern_type) {
      regex_str = TTCN_pattern_to_regexp(pstr.c_str());
    }
    else { // USTR_PATTERN
      // handle the unicode characters in \q{g,p,r,c} format
      string utf8str;
      for (size_t i = 0; i < pstr.size(); ++i) {
        if ('\\' == pstr[i]) {
          if ('q' == pstr[i + 1]) {
            // extract the unicode character
            unsigned int group, plane, row, cell;
            i = pstr.find('{', i + 1);
            sscanf(pstr.c_str() + i + 1, "%u", &group);
            i = pstr.find(',', i + 1);
            sscanf(pstr.c_str() + i + 1, "%u", &plane);
            i = pstr.find(',', i + 1);
            sscanf(pstr.c_str() + i + 1, "%u", &row);
            i = pstr.find(',', i + 1);
            sscanf(pstr.c_str() + i + 1, "%u", &cell);
            i = pstr.find('}', i + 1);
            
            // convert the character to UTF-8 format
            utf8str += ustring_to_uft8(ustring((unsigned char)group, (unsigned char)plane, (unsigned char)row, (unsigned char)cell));
            continue;
          }
          else if ('\\' == pstr[i + 1]) {
            // must be handled separately, so we don't confuse \\q with \q
            ++i;
            utf8str += '\\';
          }
        }
        utf8str += pstr[i];
      }
      
      // use the pattern converter for charstrings, the pattern should be in UTF-8
      // format now (setting the 2nd parameter will make sure that no error
      // messages are displayed for extended ASCII characters)
      regex_str = TTCN_pattern_to_regexp(utf8str.c_str(), true);
    }
    
    char* json_str = convert_to_json_string(regex_str);
    Free(regex_str);
    return json_str;
  }

} // namespace Ttcn

  // =================================
  // ===== TTCN_pattern_XXXX
  // =================================

/* These functions are used by common charstring pattern parser. */

void TTCN_pattern_error(const char *fmt, ...)
{
  char *msg=mcopystr("Charstring pattern: ");
  msg=mputstr(msg, fmt);
  va_list args;
  va_start(args, fmt);
  Common::Error_Context::report_error(0, msg, args);
  va_end(args);
  Free(msg);
}

void TTCN_pattern_warning(const char *fmt, ...)
{
  char *msg=mcopystr("Charstring pattern: ");
  msg=mputstr(msg, fmt);
  va_list args;
  va_start(args, fmt);
  Common::Error_Context::report_warning(0, msg, args);
  va_end(args);
  Free(msg);
}
