﻿// 
// Abstract: Defines client type system

// Copyright(C), 2006.
//

$namespace("sys");

var forEach = function(v, callback, context)
{
    var target = Object; // NOTE that Object: public Function
    if(Type.isFunction(v)) target = Function;
    else if(Type.isFunction(v.forEach)) { v.forEach(callback, context); return; }
    else if(Type.isString(v)) { target = String; }
    else if(Type.isNumber(v.length)) { target = Array; }
    target.forEach(v, callback, context);
}

Type = sys.Type = new function()
{
    this.defined = function(sType){ return typeof(sType) != 'undefined'; }
    this.undefined = function(sType) { return typeof(sType) == 'undefined'; }
    this.isNumber = function(v) { return typeof(v) == 'number'; }
    this.isBoolean = function(v) { return typeof(v) == 'boolean'; }
    this.isString = function(v) { return typeof(v) == 'string'; }
    this.isFunction = function(v) { return typeof(v) == 'function'; }
    this.isObject = function(v) { return typeof(v) == 'object'; }
    this.isArray = function(v) { return typeof(v) == 'object' && v.constructor == Array; }
    this.isRegex = function(v) { return typeof(v) == 'object' && v.constructor == RegEx; }
    
    this.resolve = function(type)
    {
        try
        {
            if(this.isString(type)) type = Function.resolve(type);
            else if(this.isObject(type) && !Object.isNull(type)) type = type.constructor;
            else if(!this.isFunction(type)) type = null;
        }
        catch(e)
        {
            type = null;
        }
        return type;
    }
    
    this.compare = function(lhs, rhs) 
    { 
        lhs = this.resolve(lhs);
        return lhs && (lhs == this.resolve(rhs));
    }
    
    this.activate = function(type) 
    { 
        var func = this.resolve(type);
        if(!func) throw new Exception("unknown type");
        return func.apply({}, [].addRange(arguments, 1)); 
    }
}

Convert = sys.Convert = new function()
{
    function _throwIfNaN(v) { if(isNaN(v)) throw new Exception("invalid conversion"); return v; }
    this.toString = function(vStr) { return vStr + ""; }
    this.toNumber = function(vNum) { return vNum - 0; }
    this.toInt = function(vNum) { return _throwIfNaN(parseInt(vNum)); }
    this.toFloat = function(vNum) { return _throwIfNaN(parseFloat(vNum)); }
    this.toBoolean = function(vBln) { return !!vBln; }
    this.toObject = function(vObj) { return vObj ? vObj : null; }
}

/*****************************************************************************
 * Add extension to Number - sys.Number
 *****************************************************************************/
sys.Number = Number;
Number._typeName = "Number";
Number._fullTypeName = "sys.Number";

Number.parse = function(value) 
{
    if (!value || (value.length == 0)) {
        return 0;
    }
    return parseFloat(value);
}

/*****************************************************************************
 * Add extension to Object - sys.Object
 *****************************************************************************/
sys.Object = object = Object;
sys.Object._typeName = "Object";
sys.Object._fullTypeName = "sys.Object";

Object.eval = function(vObj)
{
    if(typeof(vObj) == 'object' || typeof(vObj) == 'function') return vObj;
    if(typeof(vObj == 'string')) {
        var obj; try{eval("obj=" + vObj);}catch(e){obj=null;}
        return Convert.toObject(obj);
    }
    throw new ArgumentException("vObj");
}

Object.createProperties = function(obj, arrProperties)
{
    if(!obj) return;
    var len = arrProperties.length;
    for(var i = 0; i <= len - 1; i+=2)
    {
        var prop = arrProperties[i];
        if(!obj[prop]) obj[prop] = arrProperties[i+1];
    }
}

Object.isNull = function(o){ return null == o || "undefined" == typeof o; }
Object.clone = function(o) { if(!o) return null; var copy={};for(var p in o) copy[p]=o[p]; return copy; }

/*****************************************************************************
 * Add extension to String - sys.String
 *****************************************************************************/
 
sys.String = sys.string = string = String;
sys.String._typeName = "String";
sys.String._fullTypeName = "sys.String";

String.forEach = function(s, callback, context)
{
    if(s) s.forEach(callback, context);
}

String.isNullOrEmpty = function(s, bTrim)
{
    if(null == s || "undefined" == typeof(s) || "" == s) return true;
    if(bTrim) s = s.trim();
    return "" == s;
}

String.join = function(strArr, delimiter)
{
    strArr = strArr || [];
    delimiter = delimiter || ",";
    var buf = new sys.StringBuilder();
    var len = strArr.length;
    for(var i = 0; i < len; i++)
    {
        if(i > 0) buf.append(delimiter);
        buf.append(strArr[i]);
    }
    return buf.toString();
}

String.compare = function(str1,str2,bIgnoreCase) 
{
	if(null==str1&&null!=str2) return -1;
	if(null!=str1&&null==str2) return 1;
	if(null==str1&&null==str2) return 0;
	if(bIgnoreCase) {str1 = str1.toLowerCase();str2 = str2.toLowerCase();}
	var p1 = 0,p2 = 0;
	while(str1.charCodeAt(p1)==str2.charCodeAt(p2) && p1<str1.length && p2<str2.length) {p1++;p2++;}
	if(p1==str1.length&&p2==str2.length) return 0;
	if(p1==str1.length&&p2<str2.length) return -1;
	if(p1<str1.length&&p2==str2.length) return 1;
	return str1.charCodeAt(p1)==str2.charCodeAt(p2)?0:(str1.charCodeAt(p1)>str2.charCodeAt(p2)?1:-1);
}
	
String.format = function(format)
{
    var buffer = new sys.StringBuilder();
    var i = 0;
    while(true)
    {
        if(i < format.length - 1 && format.charAt(i) == "}" && 
            format.charAt(i+1) == "}") {
            buffer.append("}"); i+=2; continue;
        }
        var next = format.indexOf("{", i);
        if (next < 0) { buffer.append(format.slice(i)); break; }
        buffer.append(format.slice(i, next));
        i = next + 1;
        if (format.charAt(i) == "{") { buffer.append("{");i++;continue; }
        next = format.indexOf("}", i);
        var arg = format.slice(i, next).split(":");
        var argIndex = Number.parse(arg[0]);
        var argVal = arguments[argIndex + 1];
        if (null == argVal) {arg = "";}
        if(arg.length > 1) buffer.append(argVal.toString(arg[1]));
        else buffer.append(argVal.toString());
        i = next+1;
    }
    
    return buffer.toString().replace(/}}/g, "}");
}

String.localeFormat = function(format) {
    for (var i = 1; i < arguments.length; i++) {
        var arg = arguments[i];
        if (arg == null) {
            arg = '';
        }
        format = format.replace("{" + (i - 1) + "}", arg.toLocaleString());
    }
    return format;
}

var __sys_sp = sys.String.prototype;

__sys_sp.endsWith = function(suffix) {return (this.substr(this.length - suffix.length) == suffix);}
__sys_sp.startsWith = function(prefix) {return (this.substr(0, prefix.length) == prefix);}
__sys_sp.lTrim = __sys_sp.trimStart = function() {return this.replace(/^\s*/, "");}
__sys_sp.rTrim = __sys_sp.trimEnd = function() {return this.replace(/\s*$/, "");}
__sys_sp.trim = function() {return this.trimStart().trimEnd();}
__sys_sp.compareTo = function(str,bIgnoreCase){return String.compare(this,str,bIgnoreCase);}
__sys_sp.removeSpaces = function() {return this.replace(/ /gi,'');}
__sys_sp.removeExtraSpaces = function() {return(this.replace(__sys_sp.removeExtraSpaces.re, " "));}
__sys_sp.removeExtraSpaces.re = new RegExp("\\s+", "gi");
__sys_sp.removeSpaceDelimitedString = function(r) {var s = " " + this.trim() + " ";return s.replace(" " + r + " "," ").trim();}
__sys_sp.addSpaceDelimitedString = function(r){return this.removeSpaceDelimitedString(r) + " " + r;}
__sys_sp.encodeURI = function() {return encodeURI(this);}
__sys_sp.encodeHtml = function(){return this.replace(/\&/g, "&amp;").replace(/\>/g, "&gt;").replace(/\</g, "&lt;").replace(/\'/g, "&#039;").replace(/\"/g, "&quot;");}
__sys_sp.decodeURI = function() {return unescape(this);}

__sys_sp.forEach = function(callback, context)
{
    Array.forEach(
        this.split(""), 
        function(ch, i){callback.call(context, ch, i, this);}
        );
}

/*****************************************************************************
 * Add extension to Array - sys.Array
 *****************************************************************************/
 
sys.Array = Array;
sys.Array._typeName = "Array";
sys.Array._fullTypeName = "sys.Array";

if(!Array.forEach)
{
    Array.forEach = function(array, callback, context)
    {
        if(!array || Type.undefined(array.length) || !callback) return;
        var len = array.length;
        for(var i = 0; i < len; i++) {
            callback.call(context, array[i], i, array);
        }
    }
}

var __sys_ap = sys.Array.prototype;

__sys_ap.add = __sys_ap.queue = function(item) {this.push(item);}
__sys_ap.clear = function() {if (this.length > 0) this.splice(0, this.length);}
__sys_ap.clone = function() {return [].addRange(this);}
__sys_ap.contains =  __sys_ap.exists =  function(item) {var index = this.indexOf(item);return (index >= 0);}
__sys_ap.insert = function(index, item) {this.splice(index, 0, item);}
__sys_ap.removeAt = function(index) {return this.splice(index, 1)[0];}
__sys_ap.dequeue = __sys_ap.shift;

__sys_ap.addRange = function(items, start, end) 
{
    if (!items) return;
    if(!Type.defined(start)) start = 0;
    if(!Type.defined(end)) end = items.length - 1;
    for(; start <= end; start++) this.push(items[start]);
    return this;
}

if (!__sys_ap.indexOf) 
{
    __sys_ap.indexOf = function(item, startIndex) 
    {
        var length = this.length;
        if (length != 0) 
        {
            startIndex = startIndex || 0;
            if (startIndex < 0) startIndex = Math.max(0, length + startIndex);
            for (var i = startIndex; i < length; i++) if (this[i] == item) return i;
        }
        return -1;
    }
}

if (!__sys_ap.forEach)
{
	__sys_ap.forEach = function (fnCb, objContext) 
	{
		var length = this.length;	
		for (var i = 0; i < length; i++) 
		{
			fnCb.call(objContext, this[i], i, this);
		}
	}
}

__sys_ap.remove = function(item)
{
    var index = this.indexOf(item);
    if (index >= 0) this.splice(index, 1);
    return (index>=0);
}

/*****************************************************************************
 * Add extension to Math - sys.Math
 *****************************************************************************/
 
sys.Math = Math;

// difference that makes the boundary of equation of two float numbers.
sys.Math.FloatEqualCompareDifferenceLimit = 1e-6;

// division of two integer numbers
sys.Math.idiv = function(dividend,divisor)
{
	if(0 == divisor) throw new Exception("divisor can not be 0");
	var quotient = Math.floor(Math.abs(dividend)/Math.abs(divisor));
	var negDividend = dividend < 0 ? 1 : 0;
	var negDivisor = divisor < 0 ? 1 : 0;
	if(negDividend + negDivisor == 1) return -quotient;
	return quotient;
}

// modular of two integer numbers
sys.Math.imod = function(dividend,divisor)
{
	if(0 == divisor) throw new Exception("divisor can not be 0");
	return dividend - divisor * sys.Math.idiv(dividend,divisor);
}

// comparation of two integer numbers
sys.Math.icmp = function(num1,num2)
{
	num1 = sys.Convert.toNumber(num1);
	num2 = sys.Convert.toNumber(num2);
	return (Math.abs(num1 - num2) < sys.Math.FloatEqualCompareDifferenceLimit) ? 
		0 : (num1 < num2 ? -1 : 1);
}

// comparation of two float numbers
sys.Math.fcmp = function(fNum1,fNum2)
{
	fNum1 = sys.Convert.toDouble(fNum1);
	fNum2 = sys.Convert.toDouble(fNum2);
	return (Math.abs(fNum1 - fNum2) < sys.Math.FloatEqualCompareDifferenceLimit) ? 
		0 : (fNum1 < fNum2 ? -1 : 1);
}

// calculate the lease common multiple of num1 and num2
sys.Math.lcm = function(num1,num2)
{
	// first deal with some special cases
	if(num1 <= 0 || num2 <= 0) return null;
	return (num1 * num2) / sys.Math.gcd(num1,num2);
}

// calculate the greatest common divisor of num1 and num2
sys.Math.gcd = function(num1,num2)
{
	// first deal with some special cases.
	if(num1 <= 0 || num2 <= 0) return null;
	if(1 == num1 || 1 == num2) return 1;
	if(num1 == num2) return num1;
	if(num1 > num2) return sys.Math.gcd(num2,num1);
	var remainder = num2 % num1;
	
	// now,we can get that num1 < num2
	while(0 != remainder)
	{
		num2 = num1;
		num1 = remainder;
		remainder = num2 % num1;
	}
	
	return num1;
}

sys.Math.hex2dec = function(hex)
{
    return parseInt(hex, 16);
}

sys.Math.dec2hex = function(dec, w)
{
    var s = Number(dec).toString(16);
    if(Type.defined(w))
    {
        var t = "";
        for(var i = 0; i < w; i++) t+="0";
        if(s.length < w) s = t.substr(0, t.length - s.length) + s;
    }
    return s;
}

/*****************************************************************************
 * Add extension to Function - sys.Function
 * We provide object oriented model w/ Function, esp. Inheritance.
 *****************************************************************************/
 
sys.Function = Function;
sys.Function._typeName = "Function";
sys.Function._fullTypeName = "sys.Function";

//
// Parses the specified arguments to a function object, where
// v could be function name (fully qualified), function object.
//
sys.Function.resolve = function(v)
{
    if(Object.isNull(v)) return null;
    if(Type.isFunction(v)) return v;
    if(!Type.isString(v)) v = v.toString();
    if(!Function._cachedResolves) Function._cachedResolves = {};
    var oFnc = Function._cachedResolves[v];
    if(!oFnc) {
        oFnc = Object.eval(v);
        if(null == oFnc || !Type.isFunction(oFnc)) oFnc = null;
        else  Function._cachedResolves[v] = oFnc;
    }
    return oFnc;
}

sys.Function.apply = function(instance, func, _arguments)
{
    if(!func) throw new Exception("can not apply invalid function");
    if(_arguments) func.apply(instance, _arguments);
    else func.apply(instance);
}

var __sys_fp = sys.Function.prototype;

__sys_fp.getTypeName = function() { return this._typeName; }
__sys_fp.getFullTypeName = function() { return this._fullTypeName; }
__sys_fp.getBaseType = function() { return this._baseType; }

__sys_fp.forEach = function(object, callback, context)
{
    if(!object)
    {
        for(var p in this.prototype)
        {
            callback.call(context, this.prototype[p], p, this.prototype);
        }
    }
    else
    {
        for(var p in object)
        {
            if(Type.undefined(this.prototype[p])) 
            {
                callback.call(context, object[p], p, object);
            }
        }
    }
}

__sys_fp.defineInterfaceMember = function(instance, name, args)
{
    if(!Type.defined(args)) args = "";
    eval(String.format("instance.{0} = function({1}) {{{2}}}",
        name, Type.undefined(args) ? "" : args, 
        "throw new Exception(ExceptionTypes.NotImplemented, \"interface member " + 
        this._fullTypeName + "." + name + " has not been implemented yet.\");"
        ));
}

__sys_fp.declareClass = function(typeName, vBaseType, interfaces)
{
    this._typeName = this._fullTypeName = typeName;
    
    var cannotInherite = false;
    
    if(Type.isString(vBaseType)) this._baseType = vBaseType;
    else if(Type.isObject(vBaseType) && null != vBaseType) {
        if(!vBase.constructor._sealed) this._baseType = vBaseType.constructor;
        else cannotInherite = true;
    } else if(Type.isFunction(vBaseType)) {
        if(!vBaseType._sealed) this._baseType = vBaseType;
        else cannotInherite = true;
    }
    
    if(cannotInherite) throw new Exception("Sealed class cannot be inherited");
    
    //
    // flag indicating that the inheritance hierarchy for this class has not
    // been initialized, and it will not be initialized until the first instance
    // of this class is created.
    //
    this._inheritanceHierarchyInitialized = false;
    
    if(interfaces) this._interfaces = [].addRange(arguments, 2);
}

__sys_fp.declareSealedClass = function(typeName, vBaseType)
{
    this.declareClass(typeName, vBaseType);
    this._sealed = true;
}

__sys_fp.declareAbstractClass = function(typeName, vBaseType)
{
    this.declareClass(typeName, vBaseType);
    this._abstract = true;
}

__sys_fp.declareInterface = function(interfaceName)
{
    this.declareClass(interfaceName);
    this._interface = this._abstract = true;
}

__sys_fp._walkInheritanceHierarchy = function(root)
{
    if(root && !root._inheritanceHierarchyInitialized)
    {
        var baseType = this.base || Function.resolve(this._baseType);
        if(baseType && baseType != this && !baseType.inheritesFrom(this) && !baseType._sealed)
        {
            this.base = baseType;
            
            baseType._walkInheritanceHierarchy(root);
            
            var basePrototype = baseType.prototype;
            var thisPrototype = this.prototype;
            
            // copy base prototype members to the current instance
            for(var member in basePrototype)
            {
                if(!thisPrototype[member]) 
                    thisPrototype[member] = basePrototype[member];
            }
        }
        this._inheritanceHierarchyInitialized = true;
    }
}

__sys_fp._callBaseCtors = function(instance, _arguments)
{
    if(!this.base) return;
    Function.apply(instance, this.base, _arguments);
}

__sys_fp._applyInterfaces = function(instance, interfaces)
{
    if(!interfaces) return;
    var len = interfaces.length;
    for(var i = 0; i < len; i++) 
    {
        Function.resolve(interfaces[i]).apply(instance);
    }
}

__sys_fp.inheritesFrom = function(type)
{
    if(this == type) return true;
    if(this.base) return this.base.inheritesFrom(type);
    else if(this._baseType) {
        this.base = Function.resolve(this._baseType);
        if(this.base) this.base.inheritesFrom(type);
    }
    return false;
}

__sys_fp.implement = function(_interface)
{
    var _interfaces = this._interfaces;
    if(_interfaces) {
        _interface = Type.resolve(_interface);
        var len = _interfaces.length;
        for(var i = 0; i < len; i++) {
            if(Type.compare(_interfaces[i], _interface)) return true;
        }
    }
    
    if(this.base) if(this.base.implement(_interface)) return true;
    
    if(this._baseType) {
        this.base = Function.resolve(this._baseType);
        if(this.base) return this.base.implement(_interface);
    }
    
    return false;
}

/**
 * function that is called to intialize this specified instance.
 * It's intended to used in the constructor of the derived class.
 */
__sys_fp.initialize = function(instance, _arguments)
{
    // intialize inheritance tree, by walking up the hierarchy, in needed.
    // this step will apply all inherited prototype members to this class.
    this._walkInheritanceHierarchy(this);
    
    // apply base constructors upon to the specified instance to apply
    // all non-prototype members to the current instance.
    this._callBaseCtors(instance, _arguments);
    
    // apply interfaces members to the current instance
    this._applyInterfaces(instance, this._interfaces);
}

/**
 * function that is used to declare some method of instance as virtual.
 * all virtual methods go w/ instance in the internal virtual function table.
 * virtual methods are called by keys.
 */
__sys_fp.declareVirtualMethod = function(instance, methodName)
{
    if(!instance || !methodName) throw new Exception(
        "can not declare virtual w/ invalid arguments");
    if(!instance._vtbl) instance._vtbl = {};
    var entryKey = this._fullTypeName + "." + methodName;
    if(instance._vtbl[entryKey]) throw new Exception("virtual method already declared");
    instance._vtbl[entryKey] = instance[methodName];
}

/**
 * function used to get the specified virtual method by 
 *  1. lookuping up the inheritance hierarchy and virtual function tables.
 *  2. if (1) fails, method defined in prototype is returned.
 */
__sys_fp.getVirtualMethod = function(instance, methodName, baseType)
{
    var funcVirtual = null;
    
    if(!baseType) baseType = this._baseType;
    baseType = Function.resolve(baseType);
    
    if(!baseType) return null;
    
    if(instance._vtbl) 
    {
        while(baseType) 
        {
            funcVirtual = instance._vtbl[baseType._fullTypeName + "." + methodName];
            if(funcVirtual) break;
            baseType = baseType.base || Function.resolve(baseType._baseType);
         }
    }
    else 
    {
        funcVirtual = baseType.prototype[methodName];
    }
    return funcVirtual;
}

/**
 * call the specified virtual method.
 */
__sys_fp.callVirtualMethod = function(instance, methodName, _arguments, baseType)
{
    var funcVirtual = this.getVirtualMethod(instance, methodName, baseType);
    if(!funcVirtual) throw new Exception("virtual method not found");
    Function.apply(instance, funcVirtual, _arguments);
}

/**
 * Class Constant Object,used to wrap some constants definitions.
 * Inside the constant object,the value of any constant can be any kind of object.
 */
sys.Consts = function(vConsts)
{
	var _mapping = {};
	var _hashArray = [];
	
	// get an empty object to store all the constants.
	sys.Utils.deleteObject(_mapping,false,true);
	if(vConsts) arguments = vConsts;
	
	for(var i = 0; i < arguments.length - 1; i += 2)
	{
		_mapping[arguments[i]] = arguments[i + 1];
		_hashArray[Math.idiv(i,2)] = _mapping[arguments[i]];
	}
	
	/**
	 * get the array representing the constant object.
	 */
	_mapping.toArray = function()
	{
		return _hashArray.clone();
	}
	
	/**
	 * Merge another constant object with the current one.
	 */
	_mapping.merge = function(consts)
	{
		if(typeof(consts)!='object' || !consts.toArray) throw new Exception(
		    "consts shoule be instance of Consts");
		for(var p in consts) {
			if(!_mapping[p]) {
				_hashArray.push(consts[p]);
				_mapping[p] = consts[p];
			}
		}
		return _mapping;
	}
	
	return _mapping;
}

sys.Consts.create = function(){ return new sys.Consts(arguments); }

/**
 * class represents an Enum.
 * To create an enum, use either of the following syntaxes:
 *  1. var myEnum = new sys.Enum("M1", "M2");
 *  2. var myEnum = sys.Enum.create("M1", "M2");
 *  3. var myEnum = sys.Enum.createEx("M1", "v1", "M2", "v2");
 */
Enum = sys.Enum = (function()
{
    function _Enum(/* arguments name value pairs */)
    {
        var _values =  null;
        var _names = null;
           
        function _extend(e)
        {
            for(var p in e) {
                if(e[p] && !sys.Type.isFunction(e[p]) && !this[p]) this[p] = e[p];
            }
        }
        
        var len = arguments.length;
        for(var i = 0; i <= len - 1; i+=2) 
        {
            this[arguments[i]] = arguments[i+1];
        }
        
        /**
         * get all enum values.
         */
        this.getNames = function()
        {
            if(null == _names) {
                _names = []; 
                for(var e in this) {
                    if(this[e] && !sys.Type.isFunction(this[e])) _names.push(e);
                }
            }
            return _names;
        }
        
        /**
         * get all enum values
         */
        this.getValues = function()
        {
            if(null == _values) {
                _values = [];
                for(var e in this) {
                    if(this[e] && !sys.Type.isFunction(this[e])) _values.push(this[e]);
                }
            }
            return _values;
        }
        
        /**
         * parse the specified enum name to its corresponding value.
         */
        this.parse = function(name)
        {
            for(var e in this) if(e == name) return this[e];
            throw new Exception("invalid enum name");
        }
        
        /**
         * parse the specified value to its corresponding name.
         */
        this.toString = function(value)
        {
            for(var e in this) {
                if(this[e] && !sys.Type.isFunction(this[e]) && this[e] == value) return e;
            }
            throw new Exception("invaid enum value");
        }
        
        this.extend = function()
        {
            _extend(Function.apply(this, _Wrapper.create, arguments));
        }
        
        this.extendEx = function()
        {
            _extend(Function.apply(this, _Wrapper.createEx, arguments));
        }
        
        return this;
    }
    
    function _Wrapper()
    {
        return Function.apply(this, _Wrapper.create, arguments);
    }
    
    _Wrapper.create = function()
    {
        var enums = [];
        var len = arguments.length;
        for(var i=0;i<len;i++) enums.push(arguments[i], arguments[i]);
        return _Enum.apply(_Wrapper == this ? {} : this, enums);
    }
    
    _Wrapper.createEx = function()
    {
        return _Enum.apply(_Wrapper == this ? {} : this,arguments);
    }
    
    return _Wrapper;
})();

/**
 * class represents Flags.
 * To create a flag, use the either of the syntaxes below:
 *  var f = new Flags("F1", 1, "F2", 2, "F3", 4);
 *  var f = Flags.create("F1", 1, "F2", 2, "F3", 4);
 */
Flags = sys.Flags = function()
{
    this.parse = function(n)
    {
        var v = 0;
        if(String.isNullOrEmpty(n)) n = "";
        var parts = n.split("|");
        var valid = false;
        for(var i = 0; i < parts.length; i++)
        {
            var temp = this[parts[i]];
            if(temp && !sys.Type.isFunction(temp)) { v |= temp; valid = true; }
        }
        if(!valid) throw new Excpetion("invalid flags name");
        return v;
    }
    
    this.toString = function(v)
    {
        var s = "";
        v = sys.Convert.toInt(v);
        for(var f in this) {
            if(!sys.Type.isFunction(this[f]) && ((this[f] & v) == this[f])) {
                if("" == s) s += f;
                else s += ("|" + f);
            }
        }
        return s;
    }
    
    var len = arguments.length;
    for(var i = 0; i <= len - 1; i+=2)
    {
        this[arguments[i]] = arguments[i+1];
    }
    
    return this;
}

Flags.create = function() { return Function.apply({}, Flags.apply, arguments); }

/**
 * Exception Types across the calendar system.
 */
ExceptionTypes = sys.ExceptionTypes = sys.Enum.create(
	"General","ArgumentOutOfRange","NullReference","ArgumentException",
	"IllegalFormat","NetworkProblem","IndexOutOfRange","Runtime",
	"NotImplemented","OverFlow","AbstractMethodNotImpl","UnsupportedEraValue",
	"DependencyNotFound","UnsupportedMethod","UnknownEnumType","DividedByZero",
	"TypeCast","UnknownType","UnknownCultureInfo");
ExceptionTypes.all = ExceptionTypes.getNames();

/**
 * Class Exception.
 */ 
Exception = sys.Exception = function()
{
	if(Type.isString(arguments[0]) && ExceptionTypes.all.contains(arguments[0]))
	{
		this.exceptionType = arguments[0];
		this.description = arguments[1];
		this.message = this.description;
		this.details = arguments[2];
	}
	else
	{
		this.exceptionType = sys.ExceptionTypes.General;
		this.description = arguments[0];
		this.message = this.description;
		this.details = arguments[1];
	}
}

sys.Exception.prototype.toString = function()
{
	return String.format("{0}Exception\nMessage: {1}\nDetails: {2}",
		this.exceptionType,this.message,this.details||"no detailed information");
}

sys.Exception = Exception;
sys.Exception._typeName = "Exception";
sys.Exception._fullTypeName = "sys.Exception";

/**
 * Delegate class,like .NET Delegate,supports MultiCastDelegate.
 *
 * @ params: when create an instance of Delegate,you should pass in the instance and method pairs,e.g.,
 *	 var del = new Delegate(obj1,"callback1",obj2,"callback2",...) or
 *	 var del = new Delegate(obj1,obj1.callback1,obj2,obj2.callback2,...),where,
 *	 obj1 can be null,if it is,it will be defaulted to script root,i.e.,window in most of the cases.
 */
Delegate = sys.Delegate = function(/*delegate list*/)
{
    var argLen = arguments.length;
	if(argLen < 1) throw new Exception(ExceptionTypes.Argument,"no callback specified");
	var args = [];
	if(1 == argLen) { args.push(null); argLen ++; }
	args.addRange(arguments);
	
	var callbacks = [];
	for(var i = 0; i < argLen; i += 2)
	{
		var invoker = args[i];
		var callback = args[i+1];
		if(!Type.isObject(invoker) || Object.isNull(invoker)) invoker = sys.env.getScriptRoot();
		if(Type.isString(callback)) { callback = invoker[callback]; }
		else if(!Type.isFunction(callback)) { callback = null; }
		
		if(null == callback) throw Exception("bad callback");
		
		callbacks.push({invoker:invoker, callback:callback});
	}
	
	function _callback(_objCallback, _arguments)
	{
	    return function()
	    {
	        Function.apply(_objCallback.invoker,_objCallback.callback, _arguments);
	    }
	}
	
	/**
	 * invoke this delegate,causing all callbacks applied to the corresponding invoker.
	 */
	function invoke(/*actual arguments*/)
	{
	    for(var i = 0; i < callbacks.length; i++)
	    {
	        setTimeout(_callback(callbacks[i], arguments), 0);
	    }
	}
	
	return invoke;
}

/**
 * Class represents a callback.
 */
Callback = sys.Callback = function(funcCallback, context)
{
    funcCallback = Function.resolve(funcCallback);
    if(!funcCallback) throw new Exception("Invalid callback");
    return function()
    {
        funcCallback(context);
    }
}

/**
 * Class StringBuilder.
 */
sys.StringBuilder = function(sInit) {
    var buffer = new Array();
   
    this.append = function(s) 
    {
        if (!((s == null) || (typeof(s) == 'undefined') || 
            (typeof(s) == 'string') && (s.length == 0)))
        {
	        buffer.push(s);
        }    
    }
    
    this.appendLine = function(s,lineBreak) 
    {
        this.append(s);
        buffer.push(typeof(lineBreak) == 'undefined' ? '\r\n' : lineBreak);
    }

	this.appendFormat = function()
	{
		this.append(String.format.apply(null, arguments));
	}
	
    this.clear = function() 
    {
        buffer.clear();
    }

    this.isEmpty = function() 
    {
        return (buffer.length == 0);
    }

    this.toString = function(sDelim) 
    {
        return buffer.join(sDelim || "");
    }

    this.append(sInit);
    
    return this;
}
sys.StringBuilder.declareClass("sys.StringBuilder");

/**
 * Class represents the Linked List(a.k.a.Chain) date structure.
 */
sys.Chain = function()
{
	var length = 0;
	
	this.head = null;
	this.tail = null;
	this.getHead = function() { return this.head; }
	this.getTail = function() { return this.tail; }
	
	//TODO:private head and tail later.
	this.getLength = function() { return length; }
	
	this.appendNode = function(chainNode)
	{
		if(!chainNode) return;
		chainNode.prev = this.tail;
		if(this.tail) this.tail.next = chainNode;
		if(null == this.head) this.head = chainNode;
		this.tail = chainNode;
		chainNode.next = null;
		length ++;
	}
	
	this.removeNode = function(chainNode)
	{
		var pNode = this.head;
		while(pNode && !pNode.equals(chainNode)) pNode = pNode.next;
		if(pNode) 
		{
			if(pNode.prev) pNode.prev.next = pNode.next;
			if(pNode.next) pNode.next.prev = pNode.prev;
			length --;
		}
		
		return pNode;
	}
	
	this.removeAt = function(iIndex,bBackward)
	{
		var stopIndex = bBackwark ? length - iIndex - 1 : iIndex;
		if(stopIndex < 0 || stopIndex >= length) return null;
		
		var pNode = this.head;
		for(var i = 0; i < stopIndex; i ++) pNode = pNode.next;
		if(pNode.prev) pNode.prev.next = pNode.next;
		if(pNode.next) pNode.next.prev = pNode.prev;
		
		length --;
		
		return pNode;
	}
	
	this.clear = function()
	{
		var pNode = this.head;
		while(pNode) { pNode.dispose(); delete pNode; pNode = pNode.next; }
		this.head = this.tail = null;
		length = 0;
	}
}
sys.Chain.declareClass("sys.Chain");

/**
 * Stack.
 */
sys.Stack = function(initArr)
{
	var frames = initArr || new Array();
	
	// standard operations
	this.push = function(obj) { frames.push(obj); }
	this.pop = function() { return frames.pop(); }
	this.top = function() { return this.isEmpty() ? null : frames[frames.length - 1]; }
	this.empty = function() { return frames.length == 0; }
	this.length = function() { return frames.length; }
	this.clear = function() { var obj;while(obj = this.pop()) delete obj; }
	this.toArray = function() { return frames.clone();}
}
sys.Stack.fromArray = function(arr) { return new sys.Stack(arr); }
sys.Stack.declareClass("sys.Stack");

sys.Queue = function(initArr)
{
    var frames = initArr || [];
    this.push = function(obj) { frames.push(obj); }
    this.pop = function() { if(frames.length > 0) return frames.removeAt(0); throw "empty queue"; }
    this.length = function() { return frames.length; }
    this.empty = function() { return frames.length == 0; }
    this.clear = function() { frames.clear(); }
    this.toArray = function() { return frames.clone(); }
}
sys.Queue.fromArray = function(arr) { return new sys.Queue(arr); }
sys.Queue.declareClass("sys.Queue");

/**
 * Hashtable.
 */
sys.Hashtable = function()
{
	var _map = {};
	
	sys.Utils.deleteObject(_map,false,true);
	
	this.item = function(key){ return _map[key]; }
	this.remove = function(key) { var obj = _map[key]; delete _map[key]; return obj; }
	this.contains = function(key){ return !!_map[key]; }
	
	this.add = function(key,value)
	{
		if(!_map[key]) _map[key] = value;
		else throw new Exception(ExceptionTypes.Argument,"Item already exists");
	}
	
	this.getKeys = function()
	{
		var keys = new Array();
		for(var key in this)
		{
			keys.push(key);
		}
		return keys;
	}
	
	this.dispose = function() { sys.Utils.deleteObject(_map); }
}
sys.Hashtable.declareClass("sys.Hashtable");

/**
 * Matrix.
 */
sys.Matrix = function(row,col)
{
	row = Convert.toInt(row);
	col = Convert.toInt(col);
	if(row < 1 || col < 1) throw new Exception(ExceptionTypes.Argument,"bad row col");
	
	var arrMatrix = [];
	
	arrMatrix.getRow = function() { return row; }
	arrMatrix.getCol = function() { return col; }
	
	for(var i = 0; i < row; i ++) {
		var matrixRow = new Array();
		for(var j = 0; j < col ; j ++) matrixRow.push(null);
		arrMatrix.push(matrixRow);
	}
	
	arrMatrix.erase = function()
	{
		var row = this.getRow(),col = this.getCol();
		for(var i = 0; i < row; i ++) 
			for(var j = 0; j < col ; j ++) 
				this[i][j] = null;
	}
	
	return arrMatrix;
}
sys.Matrix.declareSealedClass("sys.Matrix");

sys.Pool = function(activator, max)
{
    if(!activator || !Type.isFunction(activator)) 
        throw new Exception("activator should not be null");
    max = Convert.toInt(max);
    
    var _objects = new sys.Queue();
    
    for(var i = 0; i < max; i++) 
        _objects.push(activator());
    
    this.queryNext = function()
    {
        return _objects.pop();
    }
    
    this.restore = function(item)
    {
        _objects.push(item);
    }
    
    this.dispose = function()
    {
        _objects.clear();
    }
}
sys.Pool.declareSealedClass("sys.Pool");

sys.IDisposable = function()
{
    sys.IDisposable.defineInterfaceMember(this, "dispose", "bDispose");
}
sys.IDisposable.declareInterface("sys.IDisposable");