/*
* $Id: util.js 163 2010-08-10 01:34:45Z victor $
*/

// use tooDeeMixin for prototype inheritance, it's
// quick simple and fun 
//
// N.B. (!!) baseClass MUST allow for no-arg ctor or NULL
//
tooDeeMixin = function(target,baseClass,funcs){

    if( baseClass )
    {
        target.prototype = new baseClass;
    }
    for( var F in funcs )
        target.prototype[F] = funcs[F];
};

tooDeeOptMix = function() {
    function isArray(a)
    {
        return toString.call(a) === "[object Array]";
    }
    var args = [];
    for( var i = 0; i < arguments.length; i++ )
        args.push(arguments[i]);
    var destination = args.shift();
    for( var sourceI in args )
    {
        var source = args[sourceI];
        
        if( isArray(source) )
        {
            return source;
        }
        else
        {
            for(var property in source)
            {
                if( typeof(source[property]) == 'object' && (source[property] !== null) )
                {
                    if( typeof(source[property].Copy) == 'function' )
                    {
                        destination[property] = source[property].Copy();
                        continue;
                    }
                }

                // reference only
                destination[property] = source[property];
            }
        }
    }
    return destination;
    
};

if( typeof(_log) != 'function' )
{
    _log = function() {};
}


function tooDeeObject()
{
    
}

tooDeeObject.prototype =
{
    dbg: 'tooDeeObject',
    name: 'name_me',
    
    toString: function() {
        return this.name;
    },
    
    Copy: function() {
        return this; // see OptMix
    },
    
    // handy for binding to events
    SetProperty: function( name, value ) {
        this[name] = value;
    }
};

tooDeeEventDelegator = function()
{
    this.events = [];
    this.eventId = 0;
}

tooDeeMixin(tooDeeEventDelegator, tooDeeObject,
{
    Bind: function( eventName, func, obj, steps, data )
    {
        if( !this.events[eventName] )
            this.events[eventName] = [];
        
        steps = steps || 1;
        var e = this.events[eventName];
        var id = this.eventId++;
        e[id] = { f: func, d: data, s: steps, c: steps, o: obj };
        return id;
    },
    
    Unbind: function( eventName, eventId )
    {
        if( this.events[eventName] && this.events[eventName][eventId] )
            delete this.events[eventName][eventId];
    },
    
    UnbindAll: function (eventName)
    {
        if( this.events[eventName] )
            delete this.events[eventName];
    },
    
    Trigger: function( eventName, data )
    {
        data = data || [];
        var e = this.events[eventName], hit = 0;
        if( e )
        {
            e = e.slice(0); // allow handlers to populate the list w/o affecting it
                            // what will this break? hmmmm, let's find out
            for( var i in e )
            {
                var E = e[i];
                if( !--E.s )
                {
                    var callingData = E.d ? data.concat(E.d) : data;
                    E.f.apply( E.o, callingData );
                    E.s = E.c;
                    ++hit;
                }
            }
        }
        
        return hit;
    },
    
    Destroy: function()
    {
        for( var i in this.events )
        {
            var e = this.events[i];
            for( var n in e )
            {
                var E = e[n];
                E.o = E.f = null;
            }
            delete this.events[i];
        }
        
        this.events = null;
    }
});

var tooDeeList = function()
{
    this.list = [];
    this.stopped = false;
}

tooDeeMixin(tooDeeList, tooDeeObject,
{    
    Append: function(obj)
    {
        this.list.push(obj);
    },

    AppendUnique: function(obj)
    {
        if( this.list.indexOf(obj) == -1 )
        {
            this.Append(obj);
        }
        
    },
    
    Remove: function(obj)
    {
        var i;
        if( (i = this.list.indexOf(obj)) != -1 )
        {
            this.list.splice(i,1);
        }
    },
    
    Each: function(func)
    {
        this.stopped = false;
        var len = this.list.length;
        for( var i = 0; i < len; i++ )
        {
            func(this.list[i],i,this);
            if( this.stopped )
                break;
        }
    },
    
    Stop: function()
    {
        this.stopped = true;
    },
    
    First: function()
    {
        return this.list[0] || null;
    },
    
    Propogate: function( method, args )
    {
        var len = this.list.length;
        for( var i = 0; i < len; i++ )
        {
            var obj = this.list[i];
            obj[method].apply(obj,args);
        }
    },
    
    Concat: function( arr )
    {
        this.list = this.list.concat( arr );
    }
});

var tooDeeVec2 = function(x, y)
{
    this.Set(x,y);
}

tooDeeVec2.prototype = 
{
    dbg: 'tooDeeVec2',

    toString: function() {
            
        return '[' + this.x.toFixed(3) + ',' + this.y.toFixed(3) + ']';
    },
    
	Set: function(x, y) {

        if( typeof(x) == 'object' )
        {
            this.x = x.x;
            this.y = x.y;
        }
        else
        {
            if( x == undefined )
            {
                this.x = this.y = 0;
            }
            else
            {
                this.x = x;
                this.y = y;
            }
        }

        return this;
    },

	Copy: function() {
		return new tooDeeVec2(this.x,this.y);
	},

	Add: function(v) {
		this.x += v.x;
        this.y += v.y;
        return this;
	}
};

function tooDeeDimensions()
{
    /*
        So I called my mom today, mainly out of guilt, which
        I've been doing since my dad died a few months ago.
        It's rarely a pleasant experience and I can't imagine
        it is for her, although it's my compulsion that insists
        on trying to figure out her mind when it's proven (over
        and over) that she functions in ways that are
        unfathamable to me.
        
        Anyway, my point is: I don't enjoy talking to her all
        that often because her default is typically so negative,
        unlike the default values below.
    */
    this.UL = { x: Number.MAX_VALUE, y: Number.MAX_VALUE };
    this.LR = { x: 0, y: 0 };
    this.width = 0;
    this.height = 0;
}

tooDeeDimensions.prototype =
{
    toString: function()
    {
        function t3(n) { return n.toFixed(3) };
        return '[' + t3(this.UL.x) + ',' + t3(this.UL.y) + '],' +
               '[' + t3(this.LR.x) + ',' + t3(this.LR.y) + ']';
    },
    
    CopyFrom: function( dim )
    {
        this.UL.x = dim.UL.x;
        this.UL.y = dim.UL.y;
        this.LR.x = dim.LR.x;
        this.LR.y = dim.LR.y;
        this.width = dim.width;
        this.height = dim.height;
        return this;
    },
    
    Set: function( minX, minY, maxX, maxY )
    {
        this.UL.x = minX;
        this.UL.y = minY;
        this.LR.x = maxX;
        this.LR.y = maxY;
        this.width = maxX - minX;
        this.height = maxY - minY;
        return this;
    },
    
    Offset: function( xy )
    {
        this.UL.x += xy.x;
        this.UL.y += xy.y;
        this.LR.x += xy.x;
        this.LR.y += xy.y;
        return this;
    },
    
    Copy: function()
    {
        var copy = new tooDeeDimensions();
        copy.CopyFrom(this);
        return copy;
    }
}

