<commit_before>﻿using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;

namespace Ink.Runtime
{
    internal class CallStack
    {
        internal class Element
        {
            public Pointer currentPointer;

            public bool inExpressionEvaluation;
            public Dictionary<string, Runtime.Object> temporaryVariables;
            public PushPopType type;

            // When this callstack element is actually a function evaluation called from the game,
            // we need to keep track of the size of the evaluation stack when it was called
            // so that we know whether there was any return value.
            public int evaluationStackHeightWhenPushed;

            // When functions are called, we trim whitespace from the start and end of what
            // they generate, so we make sure know where the function's start and end are.
            public int functionStartInOuputStream;

            public Element(PushPopType type, Pointer pointer, bool inExpressionEvaluation = false) {
                this.currentPointer = pointer;
                this.inExpressionEvaluation = inExpressionEvaluation;
                this.temporaryVariables = new Dictionary<string, Object>();
                this.type = type;
            }

            public Element Copy()
            {
                var copy = new Element (this.type, currentPointer, this.inExpressionEvaluation);
                copy.temporaryVariables = new Dictionary<string,Object>(this.temporaryVariables);
                copy.evaluationStackHeightWhenPushed = evaluationStackHeightWhenPushed;
                copy.functionStartInOuputStream = functionStartInOuputStream;
                return copy;
            }
        }

        internal class Thread
        {
            public List<Element> callstack;
            public int threadIndex;
            public Pointer previousPointer;

            public Thread() {
                callstack = new List<Element>();
            }

			public Thread(Dictionary<string, object> jThreadObj, Story storyContext) : this() {
                threadIndex = (int) jThreadObj ["threadIndex"];

				List<object> jThreadCallstack = (List<object>) jThreadObj ["callstack"];
				foreach (object jElTok in jThreadCallstack) {

					var jElementObj = (Dictionary<string, object>)jElTok;

                    PushPopType pushPopType = (PushPopType)(int)jElementObj ["type"];

                    Pointer pointer = Pointer.Null;

					string currentContainerPathStr = null;
					object currentContainerPathStrToken;
					if (jElementObj.TryGetValue ("cPath", out currentContainerPathStrToken)) {
						currentContainerPathStr = currentContainerPathStrToken.ToString ();
						pointer.container = storyContext.ContentAtPath (new Path(currentContainerPathStr)) as Container;
                        pointer.index = (int) jElementObj ["idx"];
					}

                    bool inExpressionEvaluation = (bool)jElementObj ["exp"];

					var el = new Element (pushPopType, pointer, inExpressionEvaluation);

					var jObjTemps = (Dictionary<string, object>) jElementObj ["temp"];
					el.temporaryVariables = Json.JObjectToDictionaryRuntimeObjs (jObjTemps);

					callstack.Add (el);
				}

				object prevContentObjPath;
				if( jThreadObj.TryGetValue("previousContentObject", out prevContentObjPath) ) {
					var prevPath = new Path((string)prevContentObjPath);
                    previousPointer = storyContext.PointerAtPath(prevPath);
                }
			}

            public Thread Copy() {
                var copy = new Thread ();
                copy.threadIndex = threadIndex;
                foreach(var e in callstack) {
                    copy.callstack.Add(e.Copy());
                }
                copy.previousPointer = previousPointer;
                return copy;
            }

			public Dictionary<string, object> jsonToken {
				get {
					var threadJObj = new Dictionary<string, object> ();

					var jThreadCallstack = new List<object> ();
					foreach (CallStack.Element el in callstack) {
						var jObj = new Dictionary<string, object> ();
						if (!el.currentPointer.isNull) {
							jObj ["cPath"] = el.currentPointer.container.path.componentsString;
							jObj ["idx"] = el.currentPointer.index;
						}
						jObj ["exp"] = el.inExpressionEvaluation;
						jObj ["type"] = (int) el.type;
						jObj ["temp"] = Json.DictionaryRuntimeObjsToJObject (el.temporaryVariables);
						jThreadCallstack.Add (jObj);
					}

					threadJObj ["callstack"] = jThreadCallstack;
					threadJObj ["threadIndex"] = threadIndex;

                    if (!previousPointer.isNull)
                        threadJObj ["previousContentObject"] = previousPointer.Resolve().path.ToString();

					return threadJObj;
				}
			}
        }

        public List<Element> elements {
            get {
                return callStack;
            }
        }

		public int depth {
			get {
				return elements.Count;
			}
		}

        public Element currentElement { 
            get { 
                return callStack [callStack.Count - 1];
            } 
        }

        public int currentElementIndex {
            get {
                return callStack.Count - 1;
            }
        }

        public Thread currentThread
        {
            get {
                return _threads [_threads.Count - 1];
            }
            set {
                Debug.Assert (_threads.Count == 1, "Shouldn't be directly setting the current thread when we have a stack of them");
                _threads.Clear ();
                _threads.Add (value);
            }
        }
<commit_msg>little micro optimisation<commit_after>﻿using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;

namespace Ink.Runtime
{
    internal class CallStack
    {
        internal class Element
        {
            public Pointer currentPointer;

            public bool inExpressionEvaluation;
            public Dictionary<string, Runtime.Object> temporaryVariables;
            public PushPopType type;

            // When this callstack element is actually a function evaluation called from the game,
            // we need to keep track of the size of the evaluation stack when it was called
            // so that we know whether there was any return value.
            public int evaluationStackHeightWhenPushed;

            // When functions are called, we trim whitespace from the start and end of what
            // they generate, so we make sure know where the function's start and end are.
            public int functionStartInOuputStream;

            public Element(PushPopType type, Pointer pointer, bool inExpressionEvaluation = false) {
                this.currentPointer = pointer;
                this.inExpressionEvaluation = inExpressionEvaluation;
                this.temporaryVariables = new Dictionary<string, Object>();
                this.type = type;
            }

            public Element Copy()
            {
                var copy = new Element (this.type, currentPointer, this.inExpressionEvaluation);
                copy.temporaryVariables = new Dictionary<string,Object>(this.temporaryVariables);
                copy.evaluationStackHeightWhenPushed = evaluationStackHeightWhenPushed;
                copy.functionStartInOuputStream = functionStartInOuputStream;
                return copy;
            }
        }

        internal class Thread
        {
            public List<Element> callstack;
            public int threadIndex;
            public Pointer previousPointer;

            public Thread() {
                callstack = new List<Element>();
            }

			public Thread(Dictionary<string, object> jThreadObj, Story storyContext) : this() {
                threadIndex = (int) jThreadObj ["threadIndex"];

				List<object> jThreadCallstack = (List<object>) jThreadObj ["callstack"];
				foreach (object jElTok in jThreadCallstack) {

					var jElementObj = (Dictionary<string, object>)jElTok;

                    PushPopType pushPopType = (PushPopType)(int)jElementObj ["type"];

                    Pointer pointer = Pointer.Null;

					string currentContainerPathStr = null;
					object currentContainerPathStrToken;
					if (jElementObj.TryGetValue ("cPath", out currentContainerPathStrToken)) {
						currentContainerPathStr = currentContainerPathStrToken.ToString ();
						pointer.container = storyContext.ContentAtPath (new Path(currentContainerPathStr)) as Container;
                        pointer.index = (int) jElementObj ["idx"];
					}

                    bool inExpressionEvaluation = (bool)jElementObj ["exp"];

					var el = new Element (pushPopType, pointer, inExpressionEvaluation);

					var jObjTemps = (Dictionary<string, object>) jElementObj ["temp"];
					el.temporaryVariables = Json.JObjectToDictionaryRuntimeObjs (jObjTemps);

					callstack.Add (el);
				}

				object prevContentObjPath;
				if( jThreadObj.TryGetValue("previousContentObject", out prevContentObjPath) ) {
					var prevPath = new Path((string)prevContentObjPath);
                    previousPointer = storyContext.PointerAtPath(prevPath);
                }
			}

            public Thread Copy() {
                var copy = new Thread ();
                copy.threadIndex = threadIndex;
                foreach(var e in callstack) {
                    copy.callstack.Add(e.Copy());
                }
                copy.previousPointer = previousPointer;
                return copy;
            }

			public Dictionary<string, object> jsonToken {
				get {
					var threadJObj = new Dictionary<string, object> ();

					var jThreadCallstack = new List<object> ();
					foreach (CallStack.Element el in callstack) {
						var jObj = new Dictionary<string, object> ();
						if (!el.currentPointer.isNull) {
							jObj ["cPath"] = el.currentPointer.container.path.componentsString;
							jObj ["idx"] = el.currentPointer.index;
						}
						jObj ["exp"] = el.inExpressionEvaluation;
						jObj ["type"] = (int) el.type;
						jObj ["temp"] = Json.DictionaryRuntimeObjsToJObject (el.temporaryVariables);
						jThreadCallstack.Add (jObj);
					}

					threadJObj ["callstack"] = jThreadCallstack;
					threadJObj ["threadIndex"] = threadIndex;

                    if (!previousPointer.isNull)
                        threadJObj ["previousContentObject"] = previousPointer.Resolve().path.ToString();

					return threadJObj;
				}
			}
        }

        public List<Element> elements {
            get {
                return callStack;
            }
        }

		public int depth {
			get {
				return elements.Count;
			}
		}

        public Element currentElement { 
            get {
                var thread = _threads [_threads.Count - 1];
                var cs = thread.callstack;
                return cs [cs.Count - 1];
            } 
        }

        public int currentElementIndex {
            get {
                return callStack.Count - 1;
            }
        }

        public Thread currentThread
        {
            get {
                return _threads [_threads.Count - 1];
            }
            set {
                Debug.Assert (_threads.Count == 1, "Shouldn't be directly setting the current thread when we have a stack of them");
                _threads.Clear ();
                _threads.Add (value);
            }
        }

{'fraction_non_alphanumeric': 0.0815807921486155, 'fraction_numerical': 0.0007886435331230284, 'mean_word_length': 1.9830109775222164, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 33, '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': '4016854', 'n_tokens_mistral': 3194, 'n_tokens_neox': 2701, 'n_words': 758}