ReaJ = {}
window.onload = function(){

/////////////////////////////////////////////////////////////////////////////
// 適当な補助関数
/////////////////////////////////////////////////////////////////////////////

ReaJ.elem = _LIFT(function(tag, a){
                var dom = document.createElement(tag)
                if( a instanceof Array )
                    for(var i=0; i<a.length; ++i)
                        dom.appendChild( as_node(a[i]) )
                else
                    for(var i=1; i<arguments.length; ++i)
                        dom.appendChild( as_node(arguments[i]) )
                return dom
              })

/////////////////////////////////////////////////////////////////////////////
// シグナルの共通実装
/////////////////////////////////////////////////////////////////////////////

function Signal()
{
	return {
		isSignal:
			true,

		// Current value
		value:
			undefined,
		get:
			function() {if(this.dirty) {this.value = this.calc(); this.dirty = false;} return this.value},
		forceSet:
			function(v) {if(this.value != v){
			               this.value=v; this.setDirtyFlag(); this.dirty=false; this.update()}},

		// Dirty flag
		dirty:
			true,
		setDirtyFlag:
			function() {
				this.dirty = true
				for(var i=0; i<this.watchers.length; ++i)
					this.watchers[i].setDirtyFlag()
			},

		// Dependency
		watchers:
			[],
		addWatcher:
			function(nd) {
				this.watchers.push(nd)
			},

		// view update
		update:
			function() {
				for(var i=0; i<this.watchers.length; ++i)
					this.watchers[i].update()
			},

		// Should be overridden
		calc:
			function() {return this.value}
	}
}

/////////////////////////////////////////////////////////////////////////////
// 組み込みで提供するシグナル
/////////////////////////////////////////////////////////////////////////////

ReaJ.mouseX = Signal() ; ReaJ.mouseX.value = 0
ReaJ.mouseY = Signal() ; ReaJ.mouseY.value = 0
ReaJ.msec   = Signal() ; ReaJ.msec.value = 0
ReaJ.sec    = Signal() ; ReaJ.sec.value = 0

window.onmousemove = function(e)
{
	function getMouseX(e) {
		if(window.opera)      return e.clientX
		else if(document.all) return document.body.scrollLeft+event.clientX
		else if(document.layers||document.getElementById) return e.pageX
	}
	function getMouseY(e) {
		if(window.opera)      return e.clientY
		else if(document.all) return document.body.scrollTop+event.clientY
		else if(document.layers||document.getElementById) return e.pageY
	}
	ReaJ.mouseX.forceSet( getMouseX(e) )
	ReaJ.mouseY.forceSet( getMouseY(e) )
}

var origin = (new Date()).getTime()
window.setInterval(function()
{
	var tm = (new Date()).getTime() - origin
	ReaJ.msec.forceSet( tm )
	ReaJ.sec.forceSet( Math.floor(tm/1000) )
}, 97)

/////////////////////////////////////////////////////////////////////////////
// ユーティリティ
/////////////////////////////////////////////////////////////////////////////

Array.prototype.map = function(f) {
	var aa = []
	for(var i=0; i<this.length; ++i)
		aa.push( f(this[i]) )
	return aa
}

/////////////////////////////////////////////////////////////////////////////
// コンパイラ・ランタイム
/////////////////////////////////////////////////////////////////////////////

function _CONST(v)
{
	var obj = Signal()
	obj.value = v
	return obj
}

function _ID(s1)
{
	var obj = Signal()
	obj.reassign = function(snew) {
		//TODO: s1.watchers.remove(this)
		s1 = snew
		s1.addWatcher(obj)
		this.forceSet(s1.get())
	}
	obj.calc = function() { return s1.get() }
	s1.addWatcher(obj)
	return obj
}

function _REASSIGN(obj, snew)
{
	obj.reassign(snew)
	return obj
}

function _OPCOND(s1, vs2, vs3)
{
	var s2, s3
	function rs2() {
		if(s2) return s2
		s2 = vs2()
		s2.addWatcher(obj)
		return s2
	}
	function rs3() {
		if(s3) return s3
		s3 = vs3()
		s3.addWatcher(obj)
		return s3
	}

	var obj = Signal()
	obj.calc = function() {
		return s1.get() ? rs2().get() : rs3().get()
	}
	s1.addWatcher(obj)
	return obj
}

function _ELEM(tag)
{
	var ss = []
	for(var i=1; i<arguments.length; ++i)
		ss.push( arguments[i] )

	var obj = Signal()
	obj.calc = function() {
		var v = document.createElement(tag.get())
		for(var i=0; i<ss.length; ++i)
			v.appendChild(as_node(ss[i].get()))
		return v
	}

	tag.addWatcher(obj)
	for(var i=0; i<ss.length; ++i)
		ss[i].addWatcher(obj)
	return obj
}

function _APN(f, ss)
{
	var obj  = Signal()
	obj.calc = function() {
		var a = []
		for(var i=0; i<ss.length; ++i)
			a.push( ss[i].get() )
		return f.apply(null, a)
	}
	for(var i=0; i<ss.length; ++i)
		ss[i].addWatcher(obj)
	return obj
}

function _AP(sf, ss)
{
	var obj = Signal()
	obj.calc = function() {
		return sf.get().apply(null, ss).get()
	}
	sf.addWatcher(obj)
	for(var i=0; i<ss.length; ++i)
		ss[i].addWatcher(obj)
	return obj
}

function _APM(so,mhd,ss)
{
	var obj = Signal()
	obj.calc = function() {
		var my = so.get()
		var arg = []
		for(var i=0; i<ss.length; ++i)
			arg.push( ss[i].get() )
		return my[mhd].apply(my, arg)
	}

	so.addWatcher(obj)
	for(var i=0; i<ss.length; ++i)
		ss[i].addWatcher(obj)
	return obj
}

function _TEXT(s1)
{
	var obj = Signal()
	obj.update = function() { return document.createTextNode(s1.get()) }
	s1.addWatcher(obj)
	return obj
}

function _LIFT(e1)
{
	if( e1.isSignal )
		return e1

	if( e1.constructor === Function )
		return _CONST(function(){
			var arg = []
			for(var i=0; i<arguments.length; ++i)
				arg.push( arguments[i].get() )
			return _CONST(e1.apply(this, arg))
		})

	return _CONST(e1)
}

function compile(ast)
{
	if( ast instanceof Array )
		return ast.map(compile).join(";")

	if( ast.arity == "literal" )
		return "_CONST(" + ast.value.toSource() + ")"

	if( ast.arity == "name" )
		return (ast.value in ReaJ ? "ReaJ."+ast.value : "_LIFT("+ast.value+")")

	if( ast.value == "(" )
		if( ast.arity == "ternary" )
			return "_APM(" + compile(ast.first) + "," + ast.second.value.toSource() +
			                 ",[" + ast.third.map(compile).join(",") + "])"
		else
			return "_AP(" + compile(ast.first) + ",[" + ast.second.map(compile).join(",") + "])"
	else if( ast.value == "=" )
		if(ast.assignment)
			return "_REASSIGN(" + ast.first.value + ", " + compile(ast.second) + ")"
		else
			return "var " + ast.first.value + " = _ID(" + compile(ast.second) + ")"
	else if( ast.value == "function" )
		return "_CONST(function(" + ast.first.map(function(a){return a.value}).join(",") + "){" +
		               compile(ast.second) + "})"
	else if( ast.value == "return" )
		return "return " + compile(ast.first)
	else if( ast.value == "?" )
		return "_OPCOND( "+compile(ast.first)+", "+
		                 "function(){return "+compile(ast.second) +"}, "+
		                 "function(){return "+compile(ast.third) + "}  )"
	else if( ast.arity == "unary" )
		if( ast.value == "[" )
			return "_APN(Array, [" + ast.first.map(compile).join(",") + "])"
		else
			return "_APN(function(b){return "+ast.value+" b}, [" + compile(ast.first)+ "])"
	else if( ast.arity == "binary" )
		if( ast.value == "[" )
			return "_APN( function(a,b){return a[b]}, [" +
			              compile(ast.first) + ", " + compile(ast.second)+ "])"
		else
			return "_APN( function(a,b){return a "+ast.value+" b}, [" +
			              compile(ast.first) + ", " + compile(ast.second)+ "])"
}

function as_node(jsobj)
{
	if( jsobj && jsobj.nodeType )
		return jsobj
	return document.createTextNode(jsobj)
}

/////////////////////////////////////////////////////////////////////////////
// めいん
/////////////////////////////////////////////////////////////////////////////

var parse = make_parse()

function scriptNodes()
{
	function textOf(dom) {
		return dom.text ? dom.text : dom.textContent
	}

	var ssa = []

	var ss = document.getElementsByTagName("script")
	for(var i=0; i<ss.length; ++i)
		if( ss[i].type == "text/reaj" )
			ssa.push( {src:textOf(ss[i]), dom:null} )

	var ss = document.getElementsByTagName("R")
	for(var i=0; i<ss.length; ++i)
		ssa.push( {src:textOf(ss[i]), dom:ss[i]} )
	return ssa
}

var ss = scriptNodes()
for(var i=0; i<ss.length; ++i)
{
	var ast = parse( ss[i].src )
	var sig = eval( compile(ast) )
	if( ss[i].dom )
		(function(sig, dom){
			var obj = Signal()
			obj.update = function() {
				var old = dom
				dom = as_node(sig.get())
				old.parentNode.replaceChild(dom, old)
			}
			obj.update()
			sig.addWatcher(obj)
		})( sig, ss[i].dom )
}

/////////////////////////////////////////////////////////////////////////////
}
