<commit_before>/*  ____    __    ____  ____  ____  ___     ____  __  __  ____
 * (  _ \  /__\  (_   )(_  _)( ___)/ __)   (  _ \(  )(  )(  _ \           Read
 *  )   / /(__)\  / /_  _)(_  )__) \__ \    )___/ )(__)(  ) _ <     README.txt
 * (_)\_)(__)(__)(____)(____)(____)(___/   (__)  (______)(____/    LICENSE.txt
 */
package razie.diesel.engine

import org.bson.types.ObjectId
import razie.audit.Audit
import razie.js
import razie.diesel.Diesel
import razie.diesel.dom.RDOM.P
import razie.diesel.dom.{RDomain, WTypes}
import razie.diesel.engine.RDExt._
import razie.diesel.engine.exec.{EApplicable, Executors}
import razie.diesel.engine.nodes.StoryNode
import razie.diesel.engine.nodes._
import razie.diesel.expr._
import razie.diesel.model.DieselMsg
import razie.diesel.model.DieselMsg.ENGINE._
import razie.hosting.Website
import razie.tconf.DSpec
import razie.wiki.Config
import scala.Option.option2Iterable
import scala.collection.mutable.{HashMap, ListBuffer}
import scala.util.Try

/** the actual engine implementation
  *
  * we expand and execute the known nodes
  */
class DomEngineV1(
  dom: RDomain,
  root: DomAst,
  settings: DomEngineSettings,
  pages: List[DSpec],
  description: String,
  correlationId: Option[String] = None,
  id: String = new ObjectId().toString)
    extends DomEngine(
      dom, root, settings, pages, description, correlationId, id
    ) {

  assert(settings.realm.isDefined, "need realm defined for engine settings")

  /** transform one element / one step
    *
    * 1. you can collect children of the current node, like info nodes etc
    * 2. return continuations, including processing through the children
    *
    * @param a       node to decompose
    * @param recurse should recurse
    * @param level
    * @return continuations for this branch, if any
    */
  protected override def expand(a: DomAst, recurse: Boolean = true, level: Int): List[DEMsg] = {
    var msgs : List[DEMsg] = Nil // continuations from this cycle

    if (level >= maxLevels) {
      evAppChildren(a,
        DomAst(
          TestResult(
            "fail: maxLevels!", "You have a recursive rule generating this branch..."),
          "error"))
      return Nil
    }

    if(this.curExpands > maxExpands) {
      // THIS IS OK: append direct to children
      a append DomAst(
        TestResult(
          "fail: maxExpands!", s"You have expanded too many nodes (>$maxExpands)..."), "error")
      return Nil //stopNow
    }

    // link the spec - some messages get here without a spec, because the DOM is not available when created
    if(a.value.isInstanceOf[EMsg] && a.value.asInstanceOf[EMsg].spec.isEmpty) {
      val m = a.value.asInstanceOf[EMsg]
      dom.moreElements.collect {
        case s: EMsg if s.entity == m.entity && s.met == m.met => m.withSpec(Some(s))
      }.headOption
    }

    a.value match {

      case stop: EEngStop => {
        // stop engine
        evAppChildren(a, DomAst(EInfo(s"""Stopping engine $href""")))//.withPos((m.get.pos)))
        stopNow
      }

      case stop: EEngSuspend => {
        // suspend and wait for a continuation async
        evAppChildren(a, DomAst(EInfo(s"""Suspending engine $href""")))
        try {
          stop.onSuspend.foreach(_.apply(this, a, level))
        } catch {
          case e: Exception => {
            msgs = rep(a, recurse, level, List(
              DomAst(new EError("While calling suspend callback", e), AstKinds.ERROR)
            ))
          }
        }
      }

      case stop: EEngComplete => {
        evAppChildren(a, DomAst(EInfo(s"""Complete suspended node... """)))
      }

      case next@ENext(m, arrow, cond, _, _) if "==>" == arrow || "<=>" == arrow => {
        // forking new engine / async branch

        implicit val ctx = a.withCtx(mkPassthroughMsgContext(
          next.parent,
          next.parent.map(_.attrs).getOrElse(Nil),
          this.ctx, // todo probably use a.getCtx.get not this.ctx
          a))

        // start new engine/process - evaluate this message as the root of new engine
        // todo should find settings for target service  ?
        val spawnedNodes = ListBuffer(DomAst(next.evaluateMsgCall))
        var correlationId = this.id

        if ("<=>" == arrow) {
          val suspend =
            DomAst(EEngSuspend("spawn <=>", "Waiting for spawned engine", Some((e, a, l) => {
            })))

          // make sure the other engine knows it has to notify me
          correlationId = correlationId + "." + suspend.id

          msgs = rep(a, recurse, level, List(suspend))
        }

        // create new engine
        val eng = spawn(spawnedNodes.toList, Some(correlationId))

        eng.process // start it up in the background

        // todo use mkLink for asset, not hardcode url
        evAppChildren(a, DomAst(EInfo(
          s"""Spawn $arrow engine ${eng.href}""")))//.withPos((m.get.pos)))

        if ("<=>" == arrow) {
        }

        // else no need to return anything - children have been decomposed
      }

      case n1@ENext(m, ar, cond, _, _) if "-" == ar || "=>" == ar => {
        // message executed later

        // todo bubu: static parent parms overwrite side effects updated in this.ctx

        implicit val ctx = a.withCtx(mkPassthroughMsgContext(
          n1.parent,
          n1.parent.map(_.attrs).getOrElse(Nil),
          a.getCtx.get, //RAZ2 this.ctx,
          a))

        if (n1.test(a)) {
          val newmsg = n1.evaluateMsgCall
          val newnode = DomAst(newmsg, AstKinds.kindOf(newmsg.arch))

          // old node may have had children already
          newnode.moveAllNoEvents(a)

          msgs = rep(a, recurse, level, List(newnode))
        } else {
          a.parent.foreach { p =>
            evAppChildren(p,
              DomAst(EInfo("$if failed: " + n1.cond.mkString), AstKinds.DEBUG))
          }
        }
        // message will be evaluate() later
      }

      // simple assignment
      case n1@ENextPas(m, ar, cond, _, _) if "-" == ar || "=>" == ar => {

        // ctx needed for the test below
        implicit val ctx = a.getCtx.get

        if (n1.test(a)) {
          // run in parent's context - no need for a local
          appendValsPas(a, m.pos, Some(m), m.attrs, a.getCtx.get)
        } else {
          a.parent.foreach { p =>
            evAppChildren(p,
              DomAst(EInfo("$if failed: " + n1.cond.mkString), AstKinds.DEBUG))
          }<commit_msg>nice msg<commit_after>/*  ____    __    ____  ____  ____  ___     ____  __  __  ____
 * (  _ \  /__\  (_   )(_  _)( ___)/ __)   (  _ \(  )(  )(  _ \           Read
 *  )   / /(__)\  / /_  _)(_  )__) \__ \    )___/ )(__)(  ) _ <     README.txt
 * (_)\_)(__)(__)(____)(____)(____)(___/   (__)  (______)(____/    LICENSE.txt
 */
package razie.diesel.engine

import org.bson.types.ObjectId
import razie.audit.Audit
import razie.js
import razie.diesel.Diesel
import razie.diesel.dom.RDOM.P
import razie.diesel.dom.{RDomain, WTypes}
import razie.diesel.engine.RDExt._
import razie.diesel.engine.exec.{EApplicable, Executors}
import razie.diesel.engine.nodes.StoryNode
import razie.diesel.engine.nodes._
import razie.diesel.expr._
import razie.diesel.model.DieselMsg
import razie.diesel.model.DieselMsg.ENGINE._
import razie.hosting.Website
import razie.tconf.DSpec
import razie.wiki.Config
import scala.Option.option2Iterable
import scala.collection.mutable.{HashMap, ListBuffer}
import scala.util.Try

/** the actual engine implementation
  *
  * we expand and execute the known nodes
  */
class DomEngineV1(
  dom: RDomain,
  root: DomAst,
  settings: DomEngineSettings,
  pages: List[DSpec],
  description: String,
  correlationId: Option[String] = None,
  id: String = new ObjectId().toString)
    extends DomEngine(
      dom, root, settings, pages, description, correlationId, id
    ) {

  assert(settings.realm.isDefined, "need realm defined for engine settings")

  /** transform one element / one step
    *
    * 1. you can collect children of the current node, like info nodes etc
    * 2. return continuations, including processing through the children
    *
    * @param a       node to decompose
    * @param recurse should recurse
    * @param level
    * @return continuations for this branch, if any
    */
  protected override def expand(a: DomAst, recurse: Boolean = true, level: Int): List[DEMsg] = {
    var msgs : List[DEMsg] = Nil // continuations from this cycle

    if (level >= maxLevels) {
      evAppChildren(a,
        DomAst(
          TestResult(
            "fail: maxLevels!", "You have a recursive rule generating this branch..."),
          "error"))
      return Nil
    }

    if(this.curExpands > maxExpands) {
      // THIS IS OK: append direct to children
      a append DomAst(
        TestResult(
          "fail: maxExpands!", s"You have expanded too many nodes (>$maxExpands)..."), "error")
      return Nil //stopNow
    }

    // link the spec - some messages get here without a spec, because the DOM is not available when created
    if(a.value.isInstanceOf[EMsg] && a.value.asInstanceOf[EMsg].spec.isEmpty) {
      val m = a.value.asInstanceOf[EMsg]
      dom.moreElements.collect {
        case s: EMsg if s.entity == m.entity && s.met == m.met => m.withSpec(Some(s))
      }.headOption
    }

    a.value match {

      case stop: EEngStop => {
        // stop engine
        evAppChildren(a, DomAst(EInfo(s"""Stopping engine $href""")))//.withPos((m.get.pos)))
        stopNow
      }

      case stop: EEngSuspend => {
        // suspend and wait for a continuation async
        evAppChildren(a, DomAst(EInfo(s"""Suspending engine $href""")))
        try {
          stop.onSuspend.foreach(_.apply(this, a, level))
        } catch {
          case e: Exception => {
            msgs = rep(a, recurse, level, List(
              DomAst(new EError("While calling suspend callback", e), AstKinds.ERROR)
            ))
          }
        }
      }

      case stop: EEngComplete => {
        evAppChildren(a, DomAst(EInfo(s"""Complete suspended node... """)))
      }

      case next@ENext(m, arrow, cond, _, _) if "==>" == arrow || "<=>" == arrow => {
        // forking new engine / async branch

        implicit val ctx = a.withCtx(mkPassthroughMsgContext(
          next.parent,
          next.parent.map(_.attrs).getOrElse(Nil),
          this.ctx, // todo probably use a.getCtx.get not this.ctx
          a))

        // start new engine/process - evaluate this message as the root of new engine
        // todo should find settings for target service  ?
        val spawnedNodes = ListBuffer(DomAst(next.evaluateMsgCall))
        var correlationId = this.id

        if ("<=>" == arrow) {
          val suspend =
            DomAst(EEngSuspend("spawn <=>", "Waiting for spawned engine", Some((e, a, l) => {
            })))

          // make sure the other engine knows it has to notify me
          correlationId = correlationId + "." + suspend.id

          msgs = rep(a, recurse, level, List(suspend))
        }

        // create new engine
        val eng = spawn(spawnedNodes.toList, Some(correlationId))

        eng.process // start it up in the background

        // todo use mkLink for asset, not hardcode url
        evAppChildren(a, DomAst(EInfo(
          s"""Spawn $arrow engine ${eng.href}""")))//.withPos((m.get.pos)))

        if ("<=>" == arrow) {
        }

        // else no need to return anything - children have been decomposed
      }

      case n1@ENext(m, ar, cond, _, _) if "-" == ar || "=>" == ar => {
        // message executed later

        // todo bubu: static parent parms overwrite side effects updated in this.ctx

        implicit val ctx = a.withCtx(mkPassthroughMsgContext(
          n1.parent,
          n1.parent.map(_.attrs).getOrElse(Nil),
          a.getCtx.get, //RAZ2 this.ctx,
          a))

        if (n1.test(a)) {
          val newmsg = n1.evaluateMsgCall
          val newnode = DomAst(newmsg, AstKinds.kindOf(newmsg.arch))

          // old node may have had children already
          newnode.moveAllNoEvents(a)

          msgs = rep(a, recurse, level, List(newnode))
        } else {
          a.parent.foreach { p =>
            evAppChildren(p,
              DomAst(EInfo("$if was false: " + n1.cond.mkString).withPos(n1.msg.pos), AstKinds.TRACE))
          }
        }
        // message will be evaluate() later
      }

      // simple assignment
      case n1@ENextPas(m, ar, cond, _, _) if "-" == ar || "=>" == ar => {

        // ctx needed for the test below
        implicit val ctx = a.getCtx.get

        if (n1.test(a)) {
          // run in parent's context - no need for a local
          appendValsPas(a, m.pos, Some(m), m.attrs, a.getCtx.get)
        } else {
          a.parent.foreach { p =>
            evAppChildren(p,
              DomAst(EInfo("$if was false: " + n1.cond.mkString).withPos(n1.msg.pos), AstKinds.TRACE))
          }
{'fraction_non_alphanumeric': 0.12687088018611864, 'fraction_numerical': 0.0023264831329972858, 'mean_word_length': 2.173228346456693, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 45, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5934085', 'n_tokens_mistral': 4176, 'n_tokens_neox': 4024, 'n_words': 1041}