
(function (jQuery) {
    var name = "ticker";

    function indexify(i, length) {
        return (i >= length) ? indexify(i - length, length) : ((i < 0) ? indexify(i + length, length) : i);
    }

    function advanceItem(elem) {
        var data = elem.data(name);
        var length;
        for (var i = 0; i < 200; i++) {
            if (!data.content[i]) {
                length = i;
                break;
            }
        }
        data.nextItem = indexify((data.nextItem || 0), length);
        data.currentItem = data.nextItem;
        data.elemIndex = [data.currentItem];
        data.charIndex = 0;
        data.nextItem++;
    }

    function makeFamily(elem) {
        var obj = {
            elem: elem.clone().empty()
        };
        var children = elem.children();
        if (children.length) {
            children.each(function (i) {
                obj[i] = makeFamily(jQuery(this));
            });
            return obj;
        } else {
            obj.text = elem.text()
            return obj;
        }
    }

    function checkFamily(content, index) {
        var newIndex;
        if (content[index[0]]) {
            if (content[index[0]].text) {
                return content[index[0]];
            } else if (index.length == 1) {
                return true;
            } else {
                newIndex = jQuery.makeArray(index);
                return checkFamily(content[newIndex[0]], newIndex.splice(1, newIndex.length));
            }
        } else {
            return false;
        }
    }

    function incrementIndex(index) {
        if (index.length > 1) {
            index[index.length - 1]++;
            return index;
        } else {
            return false;
            // jQuery('#player').get(0).sendEvent('PLAY');
        }
    }

    function buildIndex(content, index) {
        if (index === false) {
            return false;
        }
        var obj = checkFamily(content, index);
        if (obj === false) {
            return buildIndex(content, incrementIndex(index.slice(0, index.length - 1)));
        } else if (obj === true) {
            index[index.length] = 0;
            return buildIndex(content, index);
        } else {
            return index;
        }
    }

    function buildFamily(elem, content, index, data) {
        var newIndex, newElem;
        var child = elem.children().eq(index[0]);
        if (!index.length) {
            return {
                readout: elem,
                text: content.text
            };
        } else if (child.length) {
            newElem = child;
        } else {
            newElem = content[index[0]].elem.appendTo(elem);
        }
        newIndex = jQuery.makeArray(index).slice(1, index.length);
        return buildFamily(newElem, content[index[0]], newIndex, data);
    }

    function initElem(elem) {
        var data = elem.data(name);
        jQuery("*", elem).empty();
        elem.empty();
        data.start = 0;
        data.sum = 0;
        if (data.cursorIndex) {
            cursorIndex = 0;
        }
    }

    function initChild(elem) {
        var data = elem.data(name);
        data.start = data.sum;
    }

    function ticker(elem, threadIndex, data) {
        var index, letter;
        if (data.cursorIndex !== false) {
            data.cursorIndex = indexify(data.cursorIndex + 1, data.cursorList.length);
            data.cursor.html(data.cursorList[data.cursorIndex]);
        } else {
            data.cursor.html(data.cursorList);
        }
        index = data.charIndex - data.start;
        letter = data.text.charAt(index - 1);
        data.cursor.before(letter);
        if (data.charIndex >= data.sum) {
            data.cursor.remove();
            data.elemIndex = incrementIndex(data.elemIndex);
            return tape(elem, threadIndex);
        } else {
            return setTimeout(function () {
                if (data.eventIndex == threadIndex) {
                    data.charIndex++;
                    ticker(elem, threadIndex, data);
                }
                threadIndex = null;
            }, data.rate);
        }
    }

    function tape(elem, threadIndex) {
        var data = elem.data(name);
        if (data.eventIndex == threadIndex) {
            data.elemIndex = buildIndex(data.content, data.elemIndex);
            if (data.elemIndex === false) {
                if (data.callback) {
                    data.callback();
                }

                return setTimeout(function () {
                    if (data.running && (data.eventIndex == threadIndex)) {
                        advanceItem(elem);
                        return tape(elem, threadIndex);
                    }
                    threadIndex = null;
                }, data.delay);
            } else if (!data.charIndex) {
                initElem(elem);
            } else {
                initChild(elem);
            }
            jQuery.extend(data, buildFamily(elem, data.content, data.elemIndex));
            data.sum = data.sum + data.text.length;
            data.readout.append(data.cursor);
            return ticker(elem, threadIndex, data);
        }
    }
    jQuery.fn[name] = function (options) {
        var o = jQuery.extend({}, jQuery.fn.ticker.defaults, options);
        return this.each(function () {
            var elem = jQuery(this);
            elem.data(name, {
                rate: o.rate,
                delay: o.delay,
                content: makeFamily(elem),
                cursor: o.cursor,
                cursorList: o.cursorList,
                cursorIndex: (typeof (o.cursorList) == "object") ? 0 : false,
                nextItem: 0,
                eventIndex: 0,
                callback: o.callback
            }).bind("stop", function (e) {
                var data = elem.data(name);
                data.running = false;
            }).bind("play", function (e) {
                var data = elem.data(name);
                data.eventIndex++;
                data.running = true;
                data.nextItem = (e.item || data.nextItem);
                advanceItem(elem);
                tape(elem, data.eventIndex);
            }).bind("control", function (e) {
                var data = elem.data(name);
                jQuery().extend(data, {
                    nextItem: e.item,
                    rate: e.rate,
                    delay: e.delay
                });
            }).children().remove();
        });
    };
    jQuery.fn[name].defaults = {
        rate: 40,
        delay: 2000,
        cursorList: "_",
        cursor: jQuery('<span class="cursor" />'),
        callback: null
    }
})(jQuery);

