package org.mozartoz.truffle.nodes.builtins;

import static org.mozartoz.truffle.nodes.builtins.Builtin.ALL;

import org.mozartoz.truffle.nodes.DerefNode;
import org.mozartoz.truffle.nodes.OzGuards;
import org.mozartoz.truffle.nodes.OzNode;
import org.mozartoz.truffle.runtime.OzCons;
import org.mozartoz.truffle.runtime.OzRecord;

import com.oracle.truffle.api.dsl.GenerateNodeFactory;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.object.HiddenKey;
import com.oracle.truffle.api.object.Property;

public abstract class VirtualStringBuiltins {

	@Builtin(name = "is")
	@GenerateNodeFactory
	@NodeChild("value")
	public static abstract class IsVirtualStringNode extends OzNode {

		@Specialization
		Object isVirtualString(Object value) {
			return unimplemented();
		}

	}

	@GenerateNodeFactory
	@NodeChild("value")
	public static abstract class ToCompactStringNode extends OzNode {

		@Specialization
		Object toCompactString(Object value) {
			return unimplemented();
		}

	}

	@Builtin(deref = ALL)
	@GenerateNodeFactory
	@NodeChildren({ @NodeChild("value"), @NodeChild("tail") })
	public static abstract class ToCharListNode extends OzNode {

		public abstract Object executeToCharList(Object value, Object tail);

		@Specialization
		Object toCharList(long codePoint, Object tail) {
			return new OzCons(codePoint, tail);
		}

		@Specialization
		Object toCharList(String atom, Object tail) {
			Object list = tail;
			for (int i = atom.length() - 1; i >= 0; i--) {
				list = new OzCons((long) atom.charAt(i), list);
			}
			return list;
		}

		@Specialization
		Object toCharList(OzCons cons, Object tail) {
			if (cons.getTail() == "nil") {
				return executeToCharList(cons.getHead(), tail);
			} else {
				return executeToCharList(cons.getHead(),
						executeToCharList(cons.getTail(), tail));
			}
		}

		@Specialization
		Object toCharList(DynamicObject record, Object tail) {
			assert OzRecord.getLabel(record) == "#";
			Object list = tail;
			for (Property property : record.getShape().getPropertyListInternal(false)) {
				if (!(property.getKey() instanceof HiddenKey)) {
					Object value = property.get(record, record.getShape());
					list = executeToCharList(value, list);
				}
			}
			return list;
		}

	}

	@Builtin(deref = ALL)
	@GenerateNodeFactory
	@NodeChild("value")
	public static abstract class ToAtomNode extends OzNode {

		@Child DerefNode derefNode = DerefNode.create();

		public abstract String executeToAtom(Object value);

		@Specialization
		String toAtom(String atom) {
			assert OzGuards.isAtom(atom);
			return atom;
		}

		@Specialization
		String toAtom(OzCons cons) {
			Object head = cons.getHead();
			long longHead = (long) head;
			char c = (char) longHead;
			Object tail = deref(cons.getTail());
			if (tail == "nil") {
				return ("" + c).intern();
			} else {
				return (c + executeToAtom(tail)).intern();
			}
		}

		@Specialization
		String toAtom(DynamicObject record) {
			assert OzRecord.getLabel(record) == "#";
			StringBuilder builder = new StringBuilder();
			for (Property property : record.getShape().getProperties()) {
				if (!(property.getKey() instanceof HiddenKey)) {
					Object value = property.get(record, record.getShape());
					builder.append(executeToAtom(deref(value)));
				}
			}
			return builder.toString().intern();
		}

		private Object deref(Object value) {
			return derefNode.executeDeref(value);
		}

	}

	@Builtin(deref = ALL)
	@GenerateNodeFactory
	@NodeChild("value")
	public static abstract class LengthNode extends OzNode {

		@Specialization
		long length(String atom) {
			return atom.length();
		}

	}

	@GenerateNodeFactory
	@NodeChild("value")
	public static abstract class ToFloatNode extends OzNode {

		@Specialization
		Object toFloat(Object value) {
			return unimplemented();
		}

	}

}

@@ -1321,16 +1321,68 @@
 Node %7B%0A%0A
+%09%09@Child DerefNode derefNode = DerefNode.create();%0A%0A
 %09%09public
@@ -1449,121 +1449,8 @@
 );%0A%0A
-%09%09@Specialization%0A%09%09Object toCharList(long codePoint, Object tail) %7B%0A%09%09%09return new OzCons(codePoint, tail);%0A%09%09%7D%0A%0A
 %09%09@S
@@ -1735,82 +1735,168 @@
 %0A%09%09%09
-if (cons.getTail() == %22nil%22) %7B%0A%09%09%09%09return executeToCharList(cons.getH
+Object head = cons.getHead();%0A%09%09%09assert head instanceof Long;%0A%09%09%09Object consTail = deref(cons.getTail());%0A%09%09%09if (consTail == %22nil%22) %7B%0A%09%09%09%09return new OzCons(h
 ead
-()
 , ta
@@ -1927,48 +1927,25 @@
 urn 
-executeToCharList(cons.getHead(),%0A%09%09%09%09%09%09
+new OzCons(head, 
 exec
@@ -1962,26 +1962,20 @@
 ist(cons
-.get
 Tail
-()
 , tail))
@@ -2352,21 +2352,28 @@
 harList(
+deref(
 value
+)
 , list);
@@ -2401,24 +2401,109 @@
  list;%0A%09%09%7D%0A%0A
+%09%09private Object deref(Object value) %7B%0A%09%09%09return derefNode.executeDeref(value);%0A%09%09%7D%0A%0A
 %09%7D%0A%0A%09@Builti

fix VS.toCharList