#include "stdafx.h"

#include "xtr/cclDescriptor.h"

#include "resource.h"

#include "xtr/mfcHelper.h"
#include "xtr/core/rslIndex.hpp"
#include "xtr/ExteorOptions.h"
#include "ccl/rslang/RSGenerator.h"
#include "ccl/semantic/CstFilters.hpp"

using ccl::rslang::AST2String;

namespace xtr::info {

#pragma warning( push )
#pragma warning( disable : 26485 ) // do not complain about array to pointer decay for strings

using ccl::semantic::RSCore;
using ccl::semantic::RSForm;
using ccl::semantic::RSModel;
using ccl::semantic::EvalStatus;
using ccl::semantic::ParsingStatus;
using ccl::object::StructuredData;
using ccl::rslang::ExpressionType;

namespace {

[[nodiscard]] ErrorType ResolveErrorType(const uint32_t error) noexcept {
  static constexpr auto ERRCODE_LEXER_MASK = 0x0200U;
  static constexpr auto ERRCODE_PARSER_MASK = 0x0400U;
  static constexpr auto ERRCODE_TYPE_MASK = 0x0800U;
  static constexpr auto ERRCODE_VALUE_MASK = 0x0A00U;

  if ((error & ERRCODE_VALUE_MASK) == ERRCODE_VALUE_MASK) {
    return ErrorType::VALUE;
  } else if ((error & ERRCODE_LEXER_MASK) != 0) {
    return ErrorType::LEXER;
  } else if ((error & ERRCODE_PARSER_MASK) != 0) {
    return ErrorType::PARSER;
  } else if ((error & ERRCODE_TYPE_MASK) != 0) {
    return ErrorType::SEMANTIC;
  } else {
    return ErrorType::UNKNOWN;
  }
}

[[nodiscard]] constexpr uint32_t MsgIDFor(const ccl::rslang::LexerEID error) noexcept {
  switch (error) {
  default:
  case ccl::rslang::LexerEID::unknownSymbol: return IXTRS_LEX_UNKNOWN_SYM;
  }
}

[[nodiscard]] constexpr uint32_t MsgIDFor(const ccl::rslang::ParseEID error) noexcept {
  using ccl::rslang::ParseEID;
  switch (error) {
  default:
  case ParseEID::syntax: return IXTRS_PAR_SYNTHAXERROR;
  case ParseEID::missingParenthesis: return IXTRS_PAR_EXPECTED_RP;
  case ParseEID::missingCurlyBrace: return IXTRS_PAR_EXPECTED_RC;
  case ParseEID::invalidQuantifier: return IXTRS_PAR_QUANTDECL;
  case ParseEID::invalidImperative: return IXTRS_PAR_IMPERATIVE;
  case ParseEID::expectedDeclaration: return IXTRS_PAR_EXPECTED_ARGDECL;
  case ParseEID::expectedLocal: return IXTRS_PAR_EXPECTED_VAR;
  }
}

[[nodiscard]] constexpr uint32_t MsgIDFor(const ccl::rslang::SemanticEID error) noexcept {
  using ccl::rslang::SemanticEID;
  switch (error) {
  default:
  case SemanticEID::localUndeclared: return IXTRS_TYPE_NAME_UKNOWN;
  case SemanticEID::localShadowing: return IXTRS_TYPE_NAME_DECLARED;
  case SemanticEID::globalFuncMissing: return IXTRS_TYPE_UNKNOWN_FUNCTION;
  case SemanticEID::globalFuncWithoutArgs: return IXTRS_TYPE_FUNCTION_AS_GLOBAL;
  case SemanticEID::typesNotEqual: return IXTRS_TYPE_WRONG_TYPE;
  case SemanticEID::globalNotTyped: return IXTRS_TYPE_GLOBAL_NOTYPE;
  case SemanticEID::invalidDecart: return IXTRS_TYPE_CART;
  case SemanticEID::localDoubleDeclare: return IXTRS_TYPE_NAME_REDECLARED;
  case SemanticEID::invalidBoolean: return IXTRS_TYPE_BOOL;
  case SemanticEID::invalidTypeOperation: return IXTRS_TYPE_SET_OPERATOR;
  case SemanticEID::invalidCard: return IXTRS_TYPE_CARD;
  case SemanticEID::invalidDebool: return IXTRS_TYPE_DEBOOL;
  case SemanticEID::invalidReduce: return IXTRS_TYPE_RED;
  case SemanticEID::invalidProjectionTuple: return IXTRS_TYPE_PR_ERROR;
  case SemanticEID::invalidProjectionSet: return IXTRS_TYPE_BIGPR;
  case SemanticEID::invalidEnumeration: return IXTRS_TYPE_ENUMERATION;
  case SemanticEID::invalidBinding: return IXTRS_TYPE_DECLARE_CORTEGE;
  case SemanticEID::localOutOfScope: return IXTRS_TYPE_NAME_SCOPE;
  case SemanticEID::invalidElementPredicate: return IXTRS_TYPE_EL_WRONG_TYPE;
  case SemanticEID::localNotUsed: return IXTRS_TYPE_NAME_NOT_USED;
  case SemanticEID::invalidArgsArity: return IXTRS_TYPE_FUNC_ARGNUM;
  case SemanticEID::invalidArgumentType: return IXTRS_TYPE_FUNC_WRONG_ARG;
  case SemanticEID::globalStructure: return IXTRS_TYPE_STRUCTURE;
  case SemanticEID::globalExpectedFunction: return IXTRS_TYPE_FUNCTION;
  case SemanticEID::radicalUsage: return IXTRS_USE_RADICAL;
  case SemanticEID::invalidFilterArgumentType: return IXTRS_FILTER_ARGUMENT;
  case SemanticEID::invalidFilterArity: return IXTRS_FILTER_PARAM_ARITY;
  case SemanticEID::arithmeticNotSupported: return IXTRS_NO_ARITHMETIC;
  case SemanticEID::typesNotCompatible: return IXTRS_INCOMPATIBLE_TYPES;
  case SemanticEID::orderingNotSupported: return IXTRS_NO_ORDERING;

  case SemanticEID::globalNoValue: return IXTRS_GLOBAL_NO_VALUE_CLASS;
  case SemanticEID::invalidPropertyUsage: return IXTRS_PROXY_AS_VALUE;
  case SemanticEID::globalMissingAST: return IXTRS_GLOBAL_NO_AST;
  case SemanticEID::globalFuncNoInterpretation: return IXTRS_FUNCTION_CALL_INVALID;
  }
}

[[nodiscard]] constexpr uint32_t MsgIDFor(const ccl::rslang::ValueEID error) noexcept {
  using ccl::rslang::ValueEID;
  switch (error) {
  default:
  case ValueEID::unknownError: return IXTRS_VALUE_UNKNOWN_ERROR;
  case ValueEID::typedOverflow: return IXTRS_VALUE_SET_LIMIT;
  case ValueEID::booleanLimit: return IXTRS_VALUE_BOOL_LIMIT;
  case ValueEID::globalMissingValue: return IXTRS_VALUE_VAR_INCALC;
  case ValueEID::iterationsLimit: return IXTRS_VALUE_ITER_LIMIT;
  case ValueEID::invalidDebool: return IXTRS_VALUE_DEBOOL;
  case ValueEID::iterateInfinity: return IXTRS_ITERATE_INFINITY;
  }
}

[[nodiscard]] CString ErrorMessage(const uint32_t errorCode, const std::vector<std::string>& params) {
  const auto errType = ResolveErrorType(errorCode);
  auto prefix = Label(errType);
  prefix.AppendFormat(LR"(%04X)", errorCode);
  prefix += LR"(: )";
  const uint32_t msgID = [&]() noexcept -> uint32_t {
    switch (errType) {
    default:
    case ErrorType::UNKNOWN:  return 0;
    case ErrorType::LEXER:  return MsgIDFor(static_cast<ccl::rslang::LexerEID>(errorCode));
    case ErrorType::PARSER:  return MsgIDFor(static_cast<ccl::rslang::ParseEID>(errorCode));
    case ErrorType::SEMANTIC: return MsgIDFor(static_cast<ccl::rslang::SemanticEID>(errorCode));
    case ErrorType::VALUE: return MsgIDFor(static_cast<ccl::rslang::ValueEID>(errorCode));
    }
  }();
  if (msgID == 0) {
    return prefix;
  } else {
    switch (std::size(params)) {
    default: return prefix + mfc::LoadSID(msgID);
    case 1: return prefix + mfc::FormatSID(msgID,
                                           mfc::ToMFC(params.at(0)).GetString());
    case 2: return prefix + mfc::FormatSID(msgID,
                                           mfc::ToMFC(params.at(0)).GetString(),
                                           mfc::ToMFC(params.at(1)).GetString());
    case 3: return prefix + mfc::FormatSID(msgID,
                                           mfc::ToMFC(params.at(0)).GetString(),
                                           mfc::ToMFC(params.at(1)).GetString(),
                                           mfc::ToMFC(params.at(2)).GetString());
    }
  }
}

} // namespace

CoreStatus StatusOf(const ccl::semantic::RSCore& core) {
  if (core.RSLang().Graph().HasLoop()) {
    return CoreStatus::INCORRECT;
  } else if (core.Texts().TermGraph().HasLoop()) {
    return CoreStatus::INCORRECT;
  } else if (std::any_of(std::begin(core), std::end(core),
                         [&parse = core](const auto uid) {
                          return parse.GetParse(uid).status != ParsingStatus::VERIFIED;
    })) {
    return CoreStatus::INCORRECT;
  } else if (std::any_of(std::begin(core), std::end(core), [&core](const auto uid) { 
                          return core.GetRS(uid).IsEmpty() && core.GetText(uid).IsEmpty(); })) {
    return CoreStatus::WARNING;
  } else if (core.Texts().HasHomonyms()) {
    return CoreStatus::WARNING;
  } else {
    return CoreStatus::CORRECT;
  }
}

ModelStatus StatusOf(const ccl::semantic::RSModel& target) {
  if (target.Calculations().Count(EvalStatus::INCALCULABLE) > 0 || 
      target.Calculations().Count(EvalStatus::NEVER_CALCULATED) > 0) {
    return ModelStatus::INCORRECT;
  } else if (target.Calculations().Count(EvalStatus::AXIOM_FAIL) > 0) {
    return ModelStatus::WARNING;
  } else {
    return ModelStatus::CORRECT;
  }
}

CString ValueStatus(const EntityUID target, const RSModel& model) {
  static const CString strTrue = L"T";
  static const CString strFalse = L"F";
  static const CString strEmptySet = L"0";
  static const CString strNoValue = LR"(N/A)";
  static const CString strSingleValue = L"S";

  if (!model.Contains(target)) {
    return CString{};
  } else if (const auto status = model.Calculations()(target); status <= EvalStatus::INCALCULABLE) {
    return strNoValue;
  } else if (!ccl::semantic::IsRSObject(model.GetRS(target).type)) {
    const auto logicVal = model.Values().StatementFor(target);
    return logicVal.has_value() && logicVal.value() ? strTrue : strFalse;
  } else if (status == EvalStatus::EMPTY) {
    return strEmptySet;
  } else if (auto val = model.Values().SDataFor(target); !val.has_value()) {
    return strNoValue;
  } else if (!val->IsCollection()) {
    return strSingleValue;
  } else {
    CString result{};
    result.Format(L"%d", static_cast<int32_t>(val->B().Cardinality()));
    return result;
  }
}

CString Label(const ccl::ops::Status status) {
  using ccl::ops::Status;
  switch (status) {
  default: return mfc::LoadSID(IXTRS_PICT_DEFAULT);
  case Status::undefined: return mfc::LoadSID(IXTRS_PICT_UNDEFINED);
  case Status::defined: return mfc::LoadSID(IXTRS_PICT_DEFINED);
  case Status::done: return mfc::LoadSID(IXTRS_PICT_DONE);
  case Status::outdated: return mfc::LoadSID(IXTRS_PICT_REDO);
  case Status::broken: return mfc::LoadSID(IXTRS_PICT_BROKEN);
  }
}

CString DrawableLabel(const ccl::ops::Status status) {
  using ccl::ops::Status;
  switch (status) {
  default: return mfc::LoadSID(IXTRS_PICT_DEFAULT);
  case Status::undefined: return mfc::LoadSID(IXTRS_DRAW_UNDEFINED);
  case Status::defined: return mfc::LoadSID(IXTRS_DRAW_DEFINED);
  case Status::done: return mfc::LoadSID(IXTRS_DRAW_DONE);
  case Status::outdated: return mfc::LoadSID(IXTRS_DRAW_REDO);
  case Status::broken: return mfc::LoadSID(IXTRS_DRAW_BROKEN);
  }
}

CString LabelBool(const bool flag) {
  return mfc::LoadSID(flag ? IXTRS_YES : IXTRS_NO);
}

CString Label(const ccl::ops::Type type) {
  switch (type) {
  default: return mfc::LoadSID(IXTRS_OP_NOT_DEF);
  case ccl::ops::Type::rsMerge:   return mfc::LoadSID(IXTRS_OPLABEL_MERGE);
  case ccl::ops::Type::rsSynt:   return mfc::LoadSID(IXTRS_OPLABEL_SYNT);
  }
}

CString Label(const ccl::src::SrcType type) {
  using ccl::src::SrcType;
  switch (type) {
  case SrcType::tba:
  default: return mfc::LoadSID(IXTRS_SRC_TYPE_TBA);
  case SrcType::rsDoc: return mfc::LoadSID(IXTRS_SRC_TYPE_TRS);
  }
}

CString Label(ccl::lang::Grammem gram) {
  using ccl::lang::Grammem;
  switch (gram) {
  default:
  case Grammem::nomn: return mfc::LoadSID(IXTRS_CASE_NOMN);
  case Grammem::gent: return mfc::LoadSID(IXTRS_CASE_GENT);
  case Grammem::datv: return mfc::LoadSID(IXTRS_CASE_DATV);
  case Grammem::accs: return mfc::LoadSID(IXTRS_CASE_ACCS);
  case Grammem::ablt: return mfc::LoadSID(IXTRS_CASE_ABLT);
  case Grammem::loct: return mfc::LoadSID(IXTRS_CASE_LOCT);

  case Grammem::sing: return mfc::LoadSID(IXTRS_PLUR_SINGULAR);
  case Grammem::plur: return mfc::LoadSID(IXTRS_PLUR_PLURAL);

  case Grammem::masc: return mfc::LoadSID(IXTRS_GENDER_MASC);
  case Grammem::femn: return mfc::LoadSID(IXTRS_GENDER_FEMN);
  case Grammem::neut: return mfc::LoadSID(IXTRS_GENDER_NEUT);
  }
}

CString DescriptionOf(const ccl::lang::Grammem gram) {
  using ccl::lang::Grammem;
  switch (gram) {
  default:
  case Grammem::nomn: return mfc::LoadSID(IXTRS_CASEQ_NOMN);
  case Grammem::gent: return mfc::LoadSID(IXTRS_CASEQ_GENT);
  case Grammem::datv: return mfc::LoadSID(IXTRS_CASEQ_DATV);
  case Grammem::accs: return mfc::LoadSID(IXTRS_CASEQ_ACCS);
  case Grammem::ablt: return mfc::LoadSID(IXTRS_CASEQ_ABLT);
  case Grammem::loct: return mfc::LoadSID(IXTRS_CASEQ_LOCT);
  }
}

CString Label(const CoreStatus status) {
  switch (status) { 
  case CoreStatus::CORRECT: return mfc::LoadSID(IXTRS_CORE_CORRECT);
  case CoreStatus::WARNING: return mfc::LoadSID(IXTRS_CORE_WARNING);
  default:
  case CoreStatus::INCORRECT: return mfc::LoadSID(IXTRS_CORE_INCORRECT);
  }
}

CString Label(const ModelStatus status) {
  switch (status) {
  case ModelStatus::CORRECT: return mfc::LoadSID(IXTRS_MODEL_CORRECT);
  case ModelStatus::WARNING: return mfc::LoadSID(IXTRS_MODEL_WARNING);
  default:
  case ModelStatus::INCORRECT: return mfc::LoadSID(IXTRS_MODEL_INCORRECT);
  }
}

CString IndicatorFor(const RSCore& core) {
  CString message{};
  const auto invalidCount = std::count_if(
    std::begin(core), std::end(core),
    [&parse = core](const auto uid) {
      return parse.GetParse(uid).status != ParsingStatus::VERIFIED;
    }
  );
  message.Format(LR"(%d | %d)", static_cast<int>(std::ssize(core)), static_cast<int>(invalidCount));
  return message;
}

CString BasicDescriptionOf(const RSCore& core) {
  const auto invalidCount = std::count_if(
    std::begin(core), std::end(core),
    [&parse = core](const auto uid) {
      return parse.GetParse(uid).status != ParsingStatus::VERIFIED;
    }
  );
  const auto statusLabel = Label(StatusOf(core));
  return mfc::FormatSID(IXTRS_DESCRIPTION_CORE, statusLabel.GetString(), std::size(core), invalidCount);
}

CString DescriptionOf(const RSCore& core) {
  auto message = BasicDescriptionOf(core);
  if (const auto emptyCst = ccl::semantic::FilterEmpty(core); !std::empty(emptyCst)) {
    message += mfc::LoadSID(IXTRS_EMPTY_CST_FOUND);
    for (const auto uid : emptyCst) {
      message += mfc::ToMFC(core.GetRS(uid).alias);
      message += L" ";
    }
  }

  if (core.RSLang().Graph().HasLoop()) {
    message += mfc::LoadSID(IXTRS_FORMULA_CYCLE_FOUND);
    message += GenerateLoopStr(core, core.RSLang().Graph().GetAllLoopsItems());
  }
  if (core.Texts().TermGraph().HasLoop()) {
    message += mfc::LoadSID(IXTRS_REFERENCE_CYCLE_FOUND);
    message += GenerateLoopStr(core, core.Texts().TermGraph().GetAllLoopsItems());
  }
  if (const auto homonyms = core.Texts().GetHomonyms(); !std::empty(homonyms)) {
    message += mfc::LoadSID(IXTRS_SYNONYMS_FOUND);
    message += DescriptionOf(homonyms);
  }
  return message;
}

CString DescriptionOf(const ccl::StrSubstitutes& substitutes) {
  CString info{};
  for (const auto& [key, value] : substitutes) {
    if (!info.IsEmpty()) {
      info += L" ";
    }
    info += mfc::ToMFC(key);
    info += L" => ";
    info += mfc::ToMFC(value);
    info += ';';
  }
  return info;
}

CString IndicatorFor(const RSModel& model) {
  CString message{};
  message.Format(LR"(%d | %d | %d | %d)",
                 model.Calculations().Count(EvalStatus::NEVER_CALCULATED),
                 model.Calculations().Count(EvalStatus::INCALCULABLE),
                 model.Calculations().Count(EvalStatus::AXIOM_FAIL),
                 model.Calculations().Count(EvalStatus::EMPTY));
  return message;
}

CString BasicDescriptionOf(const RSModel& model) {
  return mfc::FormatSID(IXTRS_DESCRIPTION_MODEL, 
                        Label(StatusOf(model)).GetString(), 
                        model.Calculations().Count(EvalStatus::NEVER_CALCULATED),
                        model.Calculations().Count(EvalStatus::INCALCULABLE),
                        model.Calculations().Count(EvalStatus::AXIOM_FAIL),
                        model.Calculations().Count(EvalStatus::EMPTY));
}

CString DescriptionOf(const RSModel& model) {
  auto message = BasicDescriptionOf(model);
  if (const auto emptyCst = ccl::semantic::FilterEmpty(model.Core()); !std::empty(emptyCst)) {
    message += mfc::LoadSID(IXTRS_EMPTY_CST_FOUND);
    for (const auto uid : emptyCst) {
      message += mfc::ToMFC(model.GetRS(uid).alias);
      message += L" ";
    }
  }

  if (model.Core().RSLang().Graph().HasLoop()) {
    message += mfc::LoadSID(IXTRS_FORMULA_CYCLE_FOUND);
    message += GenerateLoopStr(model.Core(), model.RSLang().Graph().GetAllLoopsItems());
  }

  if (model.Core().Texts().TermGraph().HasLoop()) {
    message += mfc::LoadSID(IXTRS_REFERENCE_CYCLE_FOUND);
    message += GenerateLoopStr(model.Core(), model.Texts().TermGraph().GetAllLoopsItems());
  }

  if (const auto homonyms = model.Core().Texts().GetHomonyms(); !std::empty(homonyms)) {
    message += mfc::LoadSID(IXTRS_SYNONYMS_FOUND);
    message += DescriptionOf(homonyms);
  }
  return message;
}

CString BasicDescriptionOf(const ccl::oss::PictID pict, ccl::oss::OSSchema& oss) {
  CString infoStr{};
  if (const auto* schema = ccl::src::CastData<RSForm>(oss.Src().DataFor(pict)); schema != nullptr) {
    infoStr = info::BasicDescriptionOf(schema->Core());
  } else if (const auto& srcName = oss.Src()(pict)->desc.name; !empty(srcName)) {
    infoStr = mfc::FormatSID(IXTRS_PICTINFO_NO_ACCESS, mfc::ToMFC(srcName).GetString());
  } else {
    infoStr = mfc::LoadSID(IXTRS_PICTINFO_TBA);
  }

  if (!std::empty(oss(pict)->comment)) {
    infoStr += L"\n";
    infoStr += mfc::ToMFC(oss(pict)->comment);
  }
  return infoStr;
}

CString DescriptionOf(const ccl::rslang::Auditor& analytics, const BOOL showAST) {
  CString msgLog{};
  const ccl::rslang::ExpressionType* type = &analytics.GetType();
  if (!analytics.isParsed) {
    msgLog += mfc::LoadSID(IXTRS_PAR_FAILURE);
  } else if (!analytics.isTypeCorrect) {
    type = nullptr;
    msgLog += mfc::LoadSID(IXTRS_TYPECHECK_FAILURE);
  } else if (!analytics.isValueCorrect) {
    msgLog += mfc::LoadSID(IXTRS_VALUECHECK_FAILURE);
  } else {
    msgLog += mfc::LoadSID(IXTRS_RS_CHECK_SUCCESS);
  }

  msgLog += DescriptionOf(analytics.Errors());

  msgLog += '\n';
  msgLog += mfc::FormatSID(IXTRS_REPORT_TYPIFICATION, Label(type).GetString());

  msgLog += '\n';
  msgLog += mfc::FormatSID(IXTRS_REPORT_VALUE_CLASS, Label(analytics.GetValueClass()).GetString());
  
  if (showAST && analytics.isParsed) {
    msgLog += '\n';
    msgLog += mfc::FormatSID(IXTRS_REPORT_AST, mfc::ToMFC(AST2String::Apply(analytics.parser.AST())).GetString());
  }

  return msgLog;
}

CString DescriptionOf(const ccl::rslang::Interpreter& calculator,
                      const std::optional<ccl::rslang::ExpressionValue>& result, const BOOL showAST) {
  using ccl::rslang::Generator;
  CString msgLog{};
  const auto calculateOk = !calculator.Errors().HasCriticalErrors();
  msgLog += mfc::LoadSID(calculateOk ? IXTRS_VALUE_SUCCESS : IXTRS_VALUE_FAILURE);
  
  msgLog += '\n';
  msgLog += DescriptionOf(calculator.Errors());
  
  msgLog += '\n';
  msgLog += mfc::FormatSID(IXTRS_REPORT_ITERATIONS, calculator.Iterations());

  msgLog += '\n'; 
  msgLog += mfc::FormatSID(IXTRS_REPORT_NORMALIZED_EXPR, 
                           mfc::ToMFC(Generator::FromTree(calculator.NormalizedParseTree())).GetString());
  if (showAST) {
    msgLog += '\n';
    msgLog += mfc::FormatSID(IXTRS_REPORT_NORMALIZED_AST, 
                             mfc::ToMFC(AST2String::Apply(calculator.NormalizedParseTree())).GetString());
  }
  if (result.has_value()) {
    msgLog += '\n';
    msgLog += std::visit(ccl::meta::Overloads{
      [&](const StructuredData& data) {
        if (data.IsCollection()) {
          return mfc::FormatSID(IXTRS_REPORT_SET_VALUE,
                                   data.B().Cardinality(),
                                   mfc::ToMFC(data.ToString()).GetString());
        } else {
          return mfc::FormatSID(IXTRS_REPORT_VALUE, mfc::ToMFC(data.ToString()).GetString());
        }
      },
      [](const bool& data) { return data ? CString{L"TRUE"} : CString{L"FALSE"}; },
      [](const auto& /*data*/) { return CString{L"ERROR"}; }
    }, result.value());
  }
  return msgLog;
}

CString DescriptionOf(const ccl::rslang::ErrorLogger& log) {
  using ccl::rslang::ErrorStatus;
  CString msgLog{};
  if (!empty(log.All())) {
    for (const auto& err : log.All()) {
      msgLog += '\n';
      msgLog += DescriptionOf(err);
    }
    msgLog += '\n';
    msgLog += mfc::FormatSID(IXTRS_REPORT_ERR_COUNT, 
                             log.Count(ErrorStatus::WARNING), 
                             log.Count(ErrorStatus::CRITICAL));
  }
  return msgLog;
}

CString DescriptionOf(const ccl::rslang::Error& error) {
  return ErrorMessage(error.eid, error.params);
}

CString Label(const ErrorType type) {
  switch (type) {
  default:
  case ErrorType::UNKNOWN:  return L"U";
  case ErrorType::LEXER:  return L"L";
  case ErrorType::PARSER:  return "P";
  case ErrorType::SEMANTIC: return "S";
  case ErrorType::VALUE: return "V";
  }
}

CString DescriptionOf(const ccl::lang::Reference& ref) {
  using ccl::lang::ReferenceType;
  switch (ref.GetType()) {
  case ReferenceType::entity: {
    const auto& form = ref.GetForm();
    return mfc::FormatSID(IXTRS_DESCRIPTION_REFERENCE,
      mfc::ToMFC(ref.GetEntity()).GetString(),
      Label(form.GetCase()).GetString(),
      Label(form.GetNumber()).GetString(),
      mfc::ToMFC(ref.ToString()).GetString());
  }
  case ReferenceType::collaboration: {
    return mfc::FormatSID(IXTRS_DESCRIPTION_DEPENDANT_WORD,
      mfc::ToMFC(ref.GetNominal()).GetString(),
      mfc::ToMFC(std::to_string(ref.GetOffset())).GetString(),
      mfc::ToMFC(ref.ToString()).GetString());
  }
  default:
  case ReferenceType::invalid: return L"DESCRIPTION NOT AVAILABLE";
  }
}

CString DescriptionOf(ccl::oss::ossOperationsFacet& ops, const ccl::oss::PictID pid) {
  return mfc::FormatSID(IXTRS_DESCRIPTION_OPERATION,
                        LabelBool(ops.IsExecutable(pid)).GetString(),
                        LabelBool(ops.IsTranslatable(pid)).GetString());
}

CString GenerateLoopStr(const RSCore& schema, const std::vector<SetOfEntities>& loops) {
  if (!empty(loops)) {
    auto i = 0;
    auto str = mfc::FormatSID(IXTRS_SCC_INFO, size(loops));
    for (const auto& loop : loops) {
      str += mfc::FormatSID(IXTRS_SCC_INFO_COUNT, i + 1);
      for (const auto item : loop) {
        str += mfc::ToMFC(schema.GetRS(item).alias) + ' ';
      }
      ++i;
    }
    return str;
  } else {
    return CString{};
  }
}

CString Label(const ExpressionType* type) {
  if (type == nullptr) {
    return LR"(N/A)";
  } else {
    return std::visit(ccl::meta::Overloads{
      [](const ccl::rslang::Typification& t) {
        if (t == ccl::rslang::Typification::Integer()) {
          return mfc::LoadSID(IXTRS_TYPE_INTEGRAL);
        } else {
          return mfc::ToMFC(t.ToString());
        }
      },
      [](const ccl::rslang::LogicT& /*t*/) { return mfc::LoadSID(IXTRS_TYPE_BOOLEAN); }
    }, *type);
  }
}

CString Label(const ccl::rslang::ValueClass cls) {
  using ccl::rslang::ValueClass;
  switch (cls) {
  default:
  case ValueClass::value:  return mfc::LoadSID(IXTRS_VC_VALUE);
  case ValueClass::props: return mfc::LoadSID(IXTRS_VC_PROPERTY);
  case ValueClass::invalid: return mfc::LoadSID(IXTRS_VC_INVALID);
  }
}

} // namespace xtr::info

#pragma warning( pop )