/*
 *  NekoML Standard Library
 *  Copyright (c)2005 Motion-Twin
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License or the LICENSE file for more details.
 */

function string( a : 'a array ) : string {
	neko("
		var s = '[';
		var items = a[0];
		var l = a[2];
		var i = 0;
		while i < l {
			s = s + @Core.string(items[i]);
			i = i + 1;
			if i < l
				s = s + ', ';
		}
		s = s + ']';
		s
	");
};

neko("
	@make = function(a) {
		$array(a,string,$asize(a));
	};
	$exports.@make = @make;
");

function length( a : 'a array ) : int {
	neko("a[2]");
}

function create() : 'a array {
	neko("$array($array(),string,0)");
}

function add( a : 'a array, x : 'a ) : void {
	neko("
		var k = a[0];
		var n = a[2];
		if( $asize(k) == n ) {
			k = $amake(n*2+1);
			$ablit(k,0,a[0],0,n);
			a[0] = k;
		}
		k[n] = x;
		a[2] = n + 1;
	");
}

function get( a : 'a array, p : int ) : 'a {
	a.[p]
}

function set( a : 'a array, p : int, x : 'a ) : void {
	a.[p] := x;
}

function make( size : int, x : 'a ) : 'a array {
	neko("
		var a = $amake(size);
		var i = 0;
		while( i < size ) {
			a[i] = x;
			i = i + 1;
		}
		$array(a,string,size)
	");
}

function init( size : int, f : int -> 'a ) : 'a array {
	neko("
		var a = $amake(size);
		var i = 0;
		while( i < size ) {
			a[i] = $apply(f,i);
			i = i + 1;
		}
		$array(a,string,size)
	");
}

function iter( f : 'a -> void, a : 'a array ) : void {
	neko("
		var l = a[2];
		var i = 0;
		a = a[0];
		while (i  <  l) {
			f(a[i]);
			i = i + 1;
		}
	");
}

function iteri( f : int -> 'a -> void, a : 'a array ) : void {
	neko("
		var l = a[2];
		var i = 0;
		a = a[0];
		while (i  <  l) {
			f(i,a[i]);
			i = i + 1;
		}
	");
}

function map( f : 'a -> 'b, a : 'a array ) : 'b array {
	neko("
		var l = a[2];
		var i = 0;
		var a2 = $amake(l);
		a = a[0];
		while i < l {
			a2[i] = $apply(f,a[i]);
			i = i + 1;
		}
		$array(a2,string,l);
	");
}

function sort( f : 'a -> 'a -> int, a : 'a array ) : void {
	neko("
		var l = a[2];
		var i = 0;
		a = a[0];
		while( i < l ) {
			var swap = false;
			var j = 0;
			var max = l - i - 1;
			while( j < max ) {
				if( f(a[j],a[j+1]) > 0 ) {
					var tmp = a[j+1];
					a[j+1] = a[j];
					a[j] = tmp;
					swap = true;
				}
				j = j + 1;
			}
			if( $not(swap) )
				break;
			i = i + 1;
		}
	");
}

function list( a : 'a array ) : 'a list {
	neko("
		var i = a[2];
		var l = @Core.@empty;
		a = a[0];
		while i > 0 {
			i = i - 1;
			l = @Core.@cons(a[i],l);
		}
		l
	");
}

function append( src : 'a array, dst : 'a array ) : void {
	neko("
		var l = src[2];
		var i = 0;
		src = src[0];
		while i < l {
			add(dst,src[i]);
			i = i + 1;
		}
	");
}

function sub( a : 'a array, p : int, l : int ) : 'a array {
	neko("
		var al = a[2];
		if( p + l > al ) $throw('$asub');
		$array($asub(a[0],p,l),string,l)
	");
}

function index( a : 'a array, it : 'a ) : int {
	neko("
		var al = a[2];
		a = a[0];
		var i = 0;
		while( i < al ) {
			if( @Core.@compare(a[i],it) == 0 )
				return i;
			i = i + 1;
		}
	");
	throw Not_found
}

