#include "stdafx.h"
#include "InfoNode.h"
#include "Core/Array.h"
#include "Core/Str.h"
#include "Core/StrBuf.h"
#include "Rule.h"
#include "Production.h"
#include "Node.h"
#include "Lib/Array.h"

namespace storm {
	namespace syntax {

		const Nat InfoNode::errorMask  = 0x80000000;
		const Nat InfoNode::delimMask  = 0x40000000;
		const Nat InfoNode::lengthMask = 0x3FFFFFFF;

		InfoNode::InfoNode() {
			parentNode = null;
			color = tNone;
			data = 0;
			invalidate();
		}

		Nat InfoNode::length() {
			if ((data & lengthMask) == lengthMask) {
				data &= ~lengthMask;
				data |= computeLength() & lengthMask;
			}

			return data & lengthMask;
		}

		Bool InfoNode::error() const {
			return (data & errorMask) != 0;
		}

		void InfoNode::error(Bool v) {
			if (v)
				data |= errorMask;
			else
				data &= ~errorMask;
		}

		Bool InfoNode::delimiter() const {
			return (data & delimMask) != 0;
		}

		void InfoNode::delimiter(Bool v) {
			if (v)
				data |= delimMask;
			else
				data &= ~delimMask;
		}

		InfoLeaf *InfoNode::leafAt(Nat pos) {
			return null;
		}

		TextIndent InfoNode::indentAt(Nat pos) {
			return TextIndent();
		}

		Nat InfoNode::computeLength() {
			return 0;
		}

		void InfoNode::invalidate() {
			data |= lengthMask;
			if (parentNode)
				parentNode->invalidate();
		}

		Str *InfoNode::format() const {
			StrBuf *to = new (this) StrBuf();
			format(to);
			return to->toS();
		}

		void InfoNode::format(StrBuf *to) const {
			if (delimiter())
				*to << L" (delimiter)";
			if (error())
				*to << L" (contains errors)";
			if (color != tNone)
				*to << L" #" << name(engine(), color);
		}

		Nat InfoNode::dbg_size() {
			return sizeof(InfoNode);
		}

		/**
		 * Internal node.
		 */

		InfoInternal::InfoInternal(Production *p, Nat children) {
			this->prod = p;
			this->children = runtime::allocArray<InfoNode *>(engine(), &pointerArrayType, children);
		}

		InfoInternal::InfoInternal(InfoInternal *src, Nat children) {
			this->prod = src->prod;
			this->children = runtime::allocArray<InfoNode *>(engine(), &pointerArrayType, children);
			Nat copy = min(children, src->count());
			for (Nat i = 0; i < copy; i++)
				this->children->v[i] = src->children->v[i];
		}

		InfoLeaf *InfoInternal::leafAt(Nat pos) {
			for (nat i = 0; i < count(); i++) {
				Nat len = at(i)->length();
				if (pos < len)
					return at(i)->leafAt(pos);
				pos -= len;
			}
			return null;
		}

		TextIndent InfoInternal::indentAt(Nat pos) {
			Nat offset = 0;
			Nat indentStartOffsetBeg = 0;
			Nat indentStartOffsetEnd = 0;

			for (Nat i = 0; i < count(); i++) {
				InfoNode *child = at(i);
				Nat len = child->length();

				if (indent && i + 1 == indent->start)
					indentStartOffsetBeg = offset;
				if (indent && i == indent->start)
					indentStartOffsetEnd = offset;

				if (pos >= offset && pos < offset + len && len > 0) {
					TextIndent r = child->indentAt(pos - offset);
					r.offset(offset);

					if (indent)
						r.applyParent(indent, i, indentStartOffsetBeg, indentStartOffsetEnd);
					return r;
				}

				offset += len;
			}

			return TextIndent();
		}

		Nat InfoInternal::computeLength() {
			Nat len = 0;
			for (Nat i = 0; i < count(); i++)
				len += at(i)->length();
			return len;
		}

		void InfoInternal::outOfBounds(Nat v) {
			throw new (this) ArrayError(v, count());
		}

		void InfoInternal::set(Nat id, InfoNode *node) {
			if (id < count()) {
				children->v[id] = node;
				node->parent(this);
				invalidate();
			} else {
				outOfBounds(id);
			}
		}

		static Node *allocNode(Production *from, SrcPos pos) {
			ProductionType *type = from->type();

			// A bit ugly, but this is enough for the object to be considered a proper object
			// when it is populated.
			void *mem = runtime::allocObject(type->size().current(), type);
			Node *r = new (Place(mem)) Node(pos);
			type->vtable()->insert(r);

			// Create any arrays needed.
			for (nat i = 0; i < type->arrayMembers->count(); i++) {
				MemberVar *v = type->arrayMembers->at(i);
				int offset = v->rawOffset().current();

				// This will actually create the correct subtype as long as we're creating
				// something inherited from Object or TObject (which we are).
				Array<Object *> *arr = new (v->type.type) Array<Object *>();
				OFFSET_IN(r, offset, Object *) = arr;
			}

			return r;
		}

		template <class T>
		static void setValue(Node *node, MemberVar *target, T *elem) {
			int offset = target->rawOffset().current();
			if (isArray(target->type)) {
				// Arrays are initialized earlier.
				OFFSET_IN(node, offset, Array<T *> *)->push(elem);
			} else {
				OFFSET_IN(node, offset, T *) = elem;
			}
		}

		Node *InfoInternal::tree(Url *url) {
			return tree(url, 0);
		}

		Node *InfoInternal::tree(Url *url, Nat start) {
			if (error() || !prod)
				throw new (this) UsageError(S("Trying to create a syntax tree from a node that contains an error."));


			// Check number of children:
			Nat tokCount = prod->tokens->count();
			Nat repLength = 0;
			Bool countOk = tokCount == count();
			Nat repetitions = 0;
			if (prod->repType != repNone) {
				// TODO: Check so that this is correct!
				repLength = prod->repEnd - prod->repStart;
				if (skippable(prod->repType)) {
					if (tokCount == count() + repLength) {
						repetitions = 0;
						countOk = true;
					}
				}
				if (repeatable(prod->repType) && count() > tokCount) {
					Nat diff = count() - tokCount;
					if ((diff % repLength) == 0) {
						repetitions = 1 + (diff / repLength);
						countOk = true;
					}
				}
			}
			if (!countOk)
				throw new (this) UsageError(S("Invalid number of children for node."));

			// Output node.
			Node *out = allocNode(prod, SrcPos(url, start, start + length()));

			// Keep track of current position inside the node.
			Nat pos = start;

			// Keep track of the captured range if we need to.
			Nat captureStart = 0;
			Nat captureEnd = start + length();
			StrBuf *captureText = null;
			if (prod->repCapture)
				captureText = new (this) StrBuf();

			// Go through all children.
			for (Nat i = 0; i < count(); i++) {
				InfoNode *child = at(i);

				// Figure out the original token ID (= undo repetitions)
				Nat tokenId = i;
				if (prod->repType != repNone) {
					PVAR(prod->repStart + repLength * repetitions);
					if (i >= prod->repStart + repLength * repetitions)
						tokenId -= repLength * repetitions;
					else if (i >= prod->repStart)
						tokenId = prod->repStart + (i - prod->repStart) % repLength;
				}

				// Update capture
				if (i == prod->repStart)
					captureStart = pos;
				else if (tokenId == prod->repEnd)
					captureEnd = pos;
				if (captureText && i >= prod->repStart && tokenId < prod->repEnd)
					child->toS(captureText);

				// Validate and store the token:
				Token *token = prod->tokens->at(tokenId);

				if (token->asRegex()) {
					InfoLeaf *leaf = as<InfoLeaf>(child);
					if (!leaf)
						throw new (this) UsageError(S("Expected a leaf node but got an internal node."));

					if (token->target) {
						SrcPos p(url, pos, pos + leaf->length());
						setValue(out, token->target, new (this) SStr(leaf->toS(), p));
					}
				} else if (RuleToken *rt = token->asRule()) {
					InfoInternal *internal = as<InfoInternal>(child);
					if (!internal)
						throw new (this) UsageError(S("Expected an internal node but got a leaf node."));

					if (Production *p = internal->production()) {
						if (p->rule() != rt->rule)
							throw new (this) UsageError(S("Found an internal node that does not represent the proper rule."));
					} else {
						throw new (this) UsageError(S("Found an internal node that does not match a production."));
					}

					if (token->target)
						setValue(out, token->target, internal->tree(url, pos));
				} else {
					STORM_FAIL_S("Unknown token type used!");
				}

				pos += child->length();
			}

			if (prod->repCapture && prod->repCapture->target) {
				SrcPos p(url, captureStart, captureEnd);
				setValue(out, prod->repCapture->target, new (this) SStr(captureText->toS(), p));
			}

			return out;
		}

		void InfoInternal::toS(StrBuf *to) const {
			for (Nat i = 0; i < children->count(); i++)
				children->v[i]->toS(to);
		}

		void InfoInternal::format(StrBuf *to) const {
			*to << L"{";
			{
				Indent z(to);
				if (ProductionType *type = prod->type()) {
					*to << L"\nproduction: " << type->name;
					if (Rule *owner = prod->rule())
						*to << L" of " << owner->name;
				}

				if (indent)
					*to << L"\nindent: " << indent;

				for (Nat i = 0; i < children->count(); i++) {
					InfoNode *node = children->v[i];
					*to << L"\n" << node->length() << L" -> ";
					children->v[i]->format(to);
				}
			}
			*to << L"\n}";
			InfoNode::format(to);
		}

		Nat InfoInternal::dbg_size() {
			Nat total = sizeof(InfoInternal);
			total += sizeof(GcArray<InfoNode *>) + (count()*sizeof(InfoNode *)) - sizeof(InfoNode *);

			if (indent)
				total += sizeof(InfoIndent);

			for (Nat i = 0; i < count(); i++) {
				if (children->v[i])
					total += children->v[i]->dbg_size();
			}

			return total;
		}

		/**
		 * Leaf node.
		 */

		InfoLeaf::InfoLeaf(RegexToken *regex, Str *match) : v(match), regex(regex) {}

		InfoLeaf *InfoLeaf::leafAt(Nat pos) {
			return this;
		}

		Nat InfoLeaf::computeLength() {
			// This is in codepoints!
			return v->count();
		}

		void InfoLeaf::set(Str *v) {
			this->v = v;
			invalidate();
		}

		Bool InfoLeaf::matchesRegex() const {
			return matchesRegex(v);
		}

		Bool InfoLeaf::matchesRegex(Str *s) const {
			if (!regex)
				return false;

			return regex->regex.matchAll(s);
		}

		Str *InfoLeaf::toS() const {
			return v;
		}

		void InfoLeaf::toS(StrBuf *to) const {
			*to << v;
		}

		void InfoLeaf::format(StrBuf *to) const {
			*to << L"'" << v->escape('\'') << L"'";
			if (regex)
				*to << L" (matches \"" << regex->regex << L"\")";
			InfoNode::format(to);
		}

		Nat InfoLeaf::dbg_size() {
			// Approximation...
			return sizeof(InfoLeaf) + sizeof(Str) + sizeof(wchar)*length();
		}

	}
}
