#include "stdafx.h"

#include "xtr/viewmodel/vmTextEdit.h"

#include "resource.h"

#include "xtr/xtrServices.h"
#include "xtr/xtrDesign.h"

namespace xtr::viewmodel {

using ccl::lang::EntityTermContext;
using ccl::lang::Reference;

void vmTextEdit::Initialize(const EntityTermContext& cntxt) noexcept {
  context = &cntxt;
  references.SetContext(*context);
}

void vmTextEdit::UpdateData() {
  if (!outdated) {
    return;
  } else if (empty(references.get())) {
    return;
  }

  const auto positionIterator = [&textControl = this->control]
    (const auto& prev) -> std::optional<StrRange> {
    auto next = textControl.NextHighlighted(prev.finish);
    for (; next.has_value() && !design::IsReference(next->color); next = textControl.NextHighlighted(next->rng.finish)) {
      ;
    }
    if (next.has_value()) {
      return next->rng;
    } else {
      return std::nullopt;
    }
  };

  if (!references.UpdatePositions(positionIterator)) {
    throw std::runtime_error("References were corrupted!"); // TODO: consider exception mech
  } else {
    outdated = false;
  }
}

void vmTextEdit::SetRefStr(const CString refStr) {
  const auto text = mfc::ToSTL(refStr);
  control.SetText(mfc::ToMFC(references.Resolve(text)));
  for (const auto& ref : references.get()) {
    control.Highlight(HighlightItem{ ref.position, design::Color(ref.GetType()) });
  }
}

CString vmTextEdit::GetRefStr() {
  UpdateData();
  return mfc::ToMFC(references.OutputRefs(mfc::ToSTL(control.GetText())));
}

CString vmTextEdit::GetRefSubstr(StrRange cr) {
  if (cr.finish == cr.start) {
    return CString{};
  } else {
    const auto text = control.GetText();
    cr.finish = std::min(cr.finish, text.GetLength());
    UpdateData();
    return mfc::ToMFC(references.OutputRefs(mfc::ToSTL(text), cr));
  }
}

void vmTextEdit::OnEraseText(StrRange range, const BOOL lookBehind) {
  UpdateData();
  const auto text = control.GetText();
  std::optional<StrRange> erasedRange{};
  const auto wasEmpty = std::empty(range);
  if (wasEmpty) {
    if (lookBehind) {
      if (range.start == 0) {
        return;
      } else {
        --range.start;
      }
    } else {
      if (range.finish == text.GetLength()) {
        return;
      } else {
        ++range.finish;
      }
    }
  }
  erasedRange = references.EraseIn(range, wasEmpty);
  if (erasedRange.has_value()) {
    control.ReplaceRange(erasedRange.value(), L"");
  }
}

void vmTextEdit::OnInsertRef(const StrRange rWhere, Reference data) {
  UpdateData();

  const auto text = control.GetText();
  const auto erasedRng = references.EraseIn(rWhere);
  if (!erasedRng.has_value()) {
    Global::Msg().ShowMessage(IXTRE_REF_CANNOT_ADD, MB_ICONERROR);
    return;
  }

  const auto* newRef = references.Insert(std::move(data), rWhere.start);
  if (newRef == nullptr) {
    Global::Msg().ShowMessage(IXTRE_REF_CANNOT_ADD, MB_ICONERROR);
    return;
  }
  const auto refColor = design::Color(newRef->GetType());
  const auto resolvedText = mfc::ToMFC(newRef->resolvedText);

  StrRange controlRange = erasedRng.value();
  control.ReplaceRange(controlRange, resolvedText);
  controlRange.SetLength(resolvedText.GetLength());
  control.Highlight(HighlightItem{ controlRange, refColor });
}

const Reference* vmTextEdit::RepresentativeOf(const StrRange range) {
  UpdateData();
  return references.FirstIn(range);
}

} // namespace xtr::viewmodel