/*******************************************************************************
 * Copyright (c) 2000, 2008 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package java.lang;

import java.io.PrintStream;

import intrinsic.Date;

public final class System {

	public static final PrintStream err;

	public static final PrintStream out;

	private static int nextHashCode;
	
	static final Arguments dummy1 = null;
	static final JavaArray dummy2 = null;
	static final Cloneable dummy3 = null;

	static {
		nextHashCode++;
		initSystem ();
		err = new TracePrintStream(new TraceOutputStream());
		out = new TracePrintStream(new TraceOutputStream());
	}
	
	private native static void initSystem() throws
		ArrayIndexOutOfBoundsException,
		ClassCastException
	/*{
		// Initialize Object prototype
		var obj:Object = Object.prototype;
		obj.clone__ = function():Object {
			import mx.utils.ObjectUtil;
			if (!(this is Cloneable)) throw new CloneNotSupportedException();
			var cls:Object = this.constructor;
			var result:Object = new cls();
			for each (var prop:String in ObjectUtil.getClassInfo(this)["properties"]) {
				result[prop] = this[prop];
			}
			return result;
		};
		obj.setPropertyIsEnumerable("clone__", false);
		obj.hashCode__ = function():int {
			return java.lang.System.identityHashCode__Ljava_lang_Object_2(this);
		};
		obj.setPropertyIsEnumerable("hashCode__", false);
		obj.equals__Ljava_lang_Object_2 = function (other:Object):Boolean {
			return this === other;
		}
		obj.setPropertyIsEnumerable("equals__Ljava_lang_Object_2", false);
		obj.getClass__ = function ():java.lang.Class__ {
			return java.lang.Class__.getClass__Ljava_lang_Object_2(this);
		}
		obj.setPropertyIsEnumerable("getClass__", false);
		obj.toString__ = function ():String {
			return this.getClass__().getName__() + "@" + this.hashCode__();
		}
		obj.setPropertyIsEnumerable("toString__", false);
		obj.finalize__ = function ():void {
		}
		obj.setPropertyIsEnumerable("finalize__", false);
		obj.notify__ = function ():void {
		}
		obj.setPropertyIsEnumerable("notify__", false);
		obj.notifyAll__ = function ():void {
		}
		obj.setPropertyIsEnumerable("notifyAll__", false);
		obj.wait__ = function ():void {
		}
		obj.setPropertyIsEnumerable("wait__", false);
		obj.wait__J = function (timeout:Number):void {
		}
		obj.setPropertyIsEnumerable("wait__J", false);
		obj.wait__JI = function (timeout:Number, nanos:int):void {
		}
		obj.setPropertyIsEnumerable("wait__JI", false);
		
		// Initialize Object class to implement super (i.e super.clone());
		obj = Object;
		obj.clone__ = function(o:Object):Object {
			import mx.utils.ObjectUtil;
			if (!(o is Cloneable)) throw new CloneNotSupportedException();
			var cls:Object = o.constructor;
			var result:Object = new cls();
			for each (var prop:String in ObjectUtil.getClassInfo(o)["properties"]) {
				result[prop] = o[prop];
			}
			return result;
		};
		obj.setPropertyIsEnumerable("clone__", false);
		obj.hashCode__ = function(o:Object):int {
			return java.lang.System.identityHashCode__Ljava_lang_Object_2(o);
		};
		obj.setPropertyIsEnumerable("hashCode__", false);
		obj.equals__Ljava_lang_Object_2 = function (o:Object, other:Object):Boolean {
			return o === other;
		}
		obj.setPropertyIsEnumerable("equals__Ljava_lang_Object_2", false);
		obj.getClass__ = function (o:Object):java.lang.Class__ {
			return java.lang.Class__.getClass__Ljava_lang_Object_2(o);
		}
		obj.setPropertyIsEnumerable("getClass__", false);
		obj.toString__ = function (o:Object):String {
			return o.getClass__().getName__() + "@" + o.hashCode__();
		}
		obj.setPropertyIsEnumerable("toString__", false);
		obj.finalize__ = function (o:Object):void {
		}
		obj.setPropertyIsEnumerable("finalize__", false);
		obj.notify__ = function (o:Object):void {
		}
		obj.setPropertyIsEnumerable("notify__", false);
		obj.notifyAll__ = function (o:Object):void {
		}
		obj.setPropertyIsEnumerable("notifyAll__", false);
		obj.wait__ = function (o:Object):void {
		}
		obj.setPropertyIsEnumerable("wait__", false);
		obj.wait__J = function (o:Object, timeout:Number):void {
		}
		obj.setPropertyIsEnumerable("wait__J", false);
		obj.wait__JI = function (o:Object, timeout:Number, nanos:int):void {
		}
		obj.setPropertyIsEnumerable("wait__JI", false);

		// Initialize String class
		obj = String;
		obj.__init__Ljava_lang_String_2 = function(str:String):String {
			return str;
		}
		obj.__init___3C = function(data:JavaArray):String {
			return Object(String).valueOf___3C(data);
		}
		obj.__init___3CII = function(data:JavaArray, offset:int, count:int):String {
			return Object(String).valueOf___3CII(data, offset, count);
		}
		obj.valueOf__Z = function(b:Boolean):String {
			return b ? "true" : "false";
		}
		obj.valueOf__C = function(c:Number):String {
			return String.fromCharCode(c);
		}
		obj.valueOf___3C = function(data:JavaArray):String {
			return Object(String).valueOf___3CII(data, 0, data.length);
		}
		obj.valueOf___3CII = function(data:JavaArray, offset:int, count:int):String {
			var str:String = "";
			var end:int = offset + count;
			for (var i:int=offset; i<end; i++) {
				str += String.fromCharCode(Number(data[i]));
			}
			return str;
		}
		obj.valueOf__D = function(d:Number):String {
			return "" + d;
		}
		obj.valueOf__F = function(f:Number):String {
			return "" + f;
		}
		obj.valueOf__I = function(i:Number):String {
			return "" + i;
		}
		obj.valueOf__J = function(j:Number):String {
			return "" + j;
		}
		obj.valueOf__Ljava_lang_Object_2 = function(o:Object):String {
			if (o == null) return "null";
			return o.toString__();
		}
		obj.copyValueOf___3C = function(data:JavaArray):String {
			return Object(String).valueOf___3C(data);
		}
		obj.copyValueOf___3CII = function(data:JavaArray, offset:int, count:int):String {
			return Object(String).valueOf___3CII(data, offset, count);
		}
		
		// Initialize String prototype
		obj = String.prototype;
		obj.hashCode__ = function():int {
			var hash:int = 0;
			for (var i:int = 0; i<this.length; i++) {
				hash = hash * 31 + this.charCodeAt(i);
			}
			return hash;
		};
		obj.toString__ = function():String {
			return this;
		};
		obj.charAt__I = function(index:int):int {
			return this.charCodeAt(index);
		};
		obj.compareTo__Ljava_lang_Object_2 = function(obj:Object):int {
			if (!(obj is String)) throw new java.lang.ClassCastException();
			return this.compareTo__Ljava_lang_String_2(String(obj));
		};
		obj.compareTo__Ljava_lang_String_2 = function(str:String):int {
			var str1:String = this;
			var str2:String = String(obj);
			var end:int = Math.min(str1.length, str2.length);
			for (var i:int = 0; i < end; i++) {
				var c1:Number = str1.charCodeAt(i);
				var c2:Number = str2.charCodeAt(i);
				if (c1 != c2) {
					return c1 - c2;
				}
			}
			return str1.length - str2.length;
		};
		obj.compareToIgnoreCase__Ljava_lang_String_2 = function(str:String):int {
			return this.toUpperCase()._compareTo__Ljava_lang_String_2(str.toUpperCase());
		};
		obj.concat__Ljava_lang_String_2 = function(str:String):String {
			if (str.length == 0) return this;
			return this.concat(str);
		};
		obj.contentEquals__Ljava_lang_StringBuffer_2 = function(str:java.lang.StringBuffer):Boolean {
			return this == str.buffer;
		};
		obj.endsWith__Ljava_lang_String_2 = function(str:String):Boolean {
			return this.indexOf(str) == this.length - str.length;
		};
		obj.equalsIgnoreCase__Ljava_lang_String_2 = function(str:String):Boolean {
			return this.toUpperCase() == str.toUpperCase();
		};
		obj.getChars__II_3CI = function(srcBegin:int, srcEnd:int, dst:JavaArray, dstBegin:int):void {
			//TODO - check range
			for(var i:int=srcBegin; i<srcEnd; i++) {
				dst[dstBegin+i] = this.charCodeAt(i);
			}
		}
		obj.indexOf__I = function(ch:int):int {
			return this.indexOf(String.fromCharCode(ch));
		}
		obj.indexOf__II = function(ch:int, fromIndex:int):int {
			return this.indexOf(String.fromCharCode(ch), fromIndex);
		}
		obj.indexOf__Ljava_lang_String_2 = function(str:String):int {
			return this.indexOf(str);
		}
		obj.indexOf__Ljava_lang_String_2I = function(str:String, fromIndex:int):int {
			return this.indexOf(str, fromIndex);
		}
		obj.lastIndexOf__I = function(ch:int):int {
			return this.lastIndexOf(String.fromCharCode(ch));
		}
		obj.lastIndexOf__II = function(ch:int, fromIndex:int):int {
			return this.lastIndexOf(String.fromCharCode(ch), fromIndex);
		}
		obj.lastIndexOf__Ljava_lang_String_2 = function(str:String):int {
			return this.lastIndexOf(str);
		}
		obj.lastIndexOf__Ljava_lang_String_2I = function(str:String, fromIndex:int):int {
			return this.lastIndexOf(str, fromIndex);
		}
		obj.length__ = function():int {
			return this.length;
		};
		obj.matches__Ljava_lang_String_2 = function(regex:String):Boolean {
		 	var matchObj:RegExp = new RegExp(regex).exec(this);
	    	return (matchObj == null) ? false : (this == matchObj[0]);
		};
		obj.replace__CC = function(oldChar:int, newChar:int):String {
			var code:String = java.lang.Integer.toHexString__I(oldChar);
			var regx:RegExp = new RegExp("\\x" + code, "g"); 
			return this.replace(regx, String.fromCharCode(newChar));
		};
		//TODO replaceFirst, replaceAll, split
		obj.startsWith__Ljava_lang_String_2 = function(str:String):Boolean {
			return this.indexOf(str) == 0;
		};
		obj.startsWith__Ljava_lang_String_2I = function(str:String, toffset:int):Boolean {
			return this.indexOf(str, toffset) == toffset;
		};
		obj.subSequence__II = function(startIndex:int, endIndex:int):String {
			return this.substring(startIndex, endIndex);
		};
		obj.substring__I = function(startIndex:int):String {
			return this.substring(startIndex);
		};
		obj.substring__II = function(startIndex:int, endIndex:int):String {
			return this.substring(startIndex, endIndex);
		};
		obj.toCharArray__ = function():JavaArray {
			var result:JavaArray = new JavaArray("[C");
			var chars:Array = new Array(this.length);
			result.array = chars;
			for (var i:int = 0; i<this.length; i++) {
				chars[i] = this.charCodeAt(i);
			}
			return result;
		};
		obj.toUpperCase__ = function():String {
			return this.toUpperCase();
		};
		obj.toLowerCase__ = function():String {
			return this.toLowerCase();
		};
		obj.trim__ = function():String {
			return this.replace(/^(\s*)/, '').replace(/\s*$/, '');
		};
	}
	public static function stringValueOf(o: Object): String {
	  return o == null ? "null" : o.toString__();
	}*/;
	
	public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)/*{
		//TODO check range
		var end:int, i:int, j:int;
		if (srcPos < destPos) {
			end = srcPos;
			j = destPos + length - 1;
			i = srcPos + length - 1;
			while (i>=end) {
				dest[j--] = src[i--];
			}
		} else {
			end = srcPos + length;
			j = destPos;
			i = srcPos;
			while (i<end) {
				dest[j++] = src[i++];
			}
		}
	}*/;

	public static long currentTimeMillis() {
		return new Date().getTime();
	}
	
	public static void exit(int status) {
	}
	
	public static void gc() {
	}
	
	public static String getProperty(String key) {
		return getProperty(key, null);
	}
	
	public static String getProperty(String key, String dfts) {
		if (key == null) throw new NullPointerException();
		if (key.length() == 0) throw new IllegalArgumentException();
		if (key.equals("line.separator")) return "\n";
		if (key.equals("file.encoding")) return "utf-8";
		if (key.equals("os.name")) return intrinsic.flash.system.Capabilities.os;
		String serverString = intrinsic.flash.system.Capabilities.serverString;
		if (key.equals("os.version")) return serverString.substring(serverString.indexOf("ML") + 3);;
		return dfts;
	}

	public static native int identityHashCode(Object obj)/*{
		if (obj == null) return 0;
		if (obj.__hashCode__ == undefined) {
			obj.__hashCode__  = java.lang.System.nextHashCode++;
		}
		return obj.__hashCode__;
	}*/;

	public static native void setErr(PrintStream err)/*{
		java.lang.System.err = err;
	}*/;

	public static native void setOut(PrintStream out)/*{
		java.lang.System.out = out;
	}*/;

}
