
$(document).ready(
    function() {
        var g = {
            'numColumns': 16,
            'numRows': 9,
            'cellWidth': 20,
            'cellHeight': 20,
            'flips': 7,
            'patterns': getPatterns()
        };
        
        createSpinners();
        applyStyles();
        setPatternClasses();
        
        $('div.spinner').mouseleave(flipMe);
        $('div#invertButton').click(invertCells).click(toggleButtonState);
        $('div#pxlfunkButton').click(flipPxlfunk).click(toggleButtonState);
        $('div#clearButton').click(clearCells);
        $('div#clickModeButton').click(toggleClickMode);
        
        $('div#clearButton').click(resetButtonState);
        $('div#clearButton').mousedown(toggleButtonState);
        
        
        
        function createSpinners() {
            var numSpinners = g.numColumns * g.numRows;
            for (var n = 0; n < numSpinners; n++) {
                $('#spinnerContainer').append(createSpinnerDiv());
            }
            
            
            function createSpinnerDiv() {
                var div = document.createElement('div');
                $(div).append(document.createElement('div'));
                $(div).addClass('spinner');
                return div;
            }
        }
        
        
        function applyStyles() {
            $('div#spinnerContainer').css({
                'width': (g.cellWidth + 1) * g.numColumns,
                'height': (g.cellHeight + 1) * g.numRows
            });
            $('div.spinner').css({
                'width': g.cellWidth,
                'height': g.cellHeight
            });
            $('div.spinner').css('top', function(index) {
                return Math.floor(index / g.numColumns) * (g.cellHeight + 1) + 4;
            });
            $('div.spinner').css('left', function(index) {
                return (index % g.numColumns) * (g.cellWidth + 1) + 4;
            });
            
            $('div#buttons').css({
                'width': (g.cellWidth + 1) * g.numColumns + 9
            });
        }
        
        
        function setPatternClasses() {
            for (var key in g.patterns) {
            	$('div.spinner').each( function(index) {
                    if (patternFlag(key, index))
                        $(this).children().addClass(key);
                });
            }
            
            
            function patternFlag(key, index) {
                try {
                    return g.patterns[key][getColumn(index)][getRow(index)];
                } catch(error) {
                    return false;
                }
            }
            
            function getColumn(index) {
                return index % g.numColumns;
            }
            
            function getRow(index) {
                return Math.floor(index / g.numColumns);
            }
        }
        
        
        function flipMe(e) {
            flip(this, g.flips);
            if(this.timeoutID) {
                window.clearTimeout(this.timeoutID);
            }
            if (g.flips % 2 != 0) this.timeoutID = window.setTimeout(getResetFunc(this), 7000);
            
            
            function getResetFunc(elem) {
                resetFunc = function() {
                    flip(elem, 1);
                };
                return resetFunc;   //Closure
            }
        }
        
        
        function flip(elem, n) {
            var sDiv = $(elem).children();
            for(n; n > 0; n--) {
                sDiv.animate(
                    { width: 0 },
                    1 / (0.002 * n) + 50,
                    'swing',
                    toggleColor
                ).animate(
                    { width: g.cellWidth },
                    1 / (0.002 * n) + 50
                );
            }
            
            
            function toggleColor() {
                $(this).toggleClass('flipped');
                
            }
        }
        
        
        function invertCells(e) {
            for( var i = 0; i < g.numColumns; i++) {
                window.setTimeout(getFlipColumnFunc(i), i * 150);
            }
        }
        
        
        function flipPxlfunk(e) {
            for ( var i = 0; i < g.numColumns; i++) {
                window.setTimeout(getFlipColumnFunc(i, 'pxlfunk'), i * 150);
            }
        }
        
        
        function clearCells(e) {
            for( var i = 0; i < g.numColumns; i++) {
                window.setTimeout(getFlipColumnFunc(i, 'flipped'), i * 150);
            }
        }
        
        
        function getFlipColumnFunc(col, classToFlip) {
            flipColumnFunc = function() {
                for(var i = 0; i < g.numRows; i++) {
                    $('div.spinner').eq(col + i * g.numColumns).each(function(index) {
                        if(classToFlip) {
                            if ($(this).children().hasClass(classToFlip)) flip(this, 1);
                        } else flip(this, 1);
                    });
                }
            };
            return flipColumnFunc;  //closure
        }
        
        
        function toggleButtonState() {
            if ($(this).toggleClass('activeButton'));
        }
        
        
        function resetButtonState() {
            $('div.activeButton:not(#clickModeButton)').removeClass('activeButton');
        }
        
        
        function toggleClickMode(e) {
            if ($(this).hasClass('activeButton')) {
                $(this).removeClass('activeButton');
                stopClickMode();
            } else {
                $(this).addClass('activeButton');
                startClickMode();
            }
            
            
            function startClickMode() {
                $('div.spinner').unbind('mouseleave', flipMe);
                $('div.spinner').click(function(e) {
                    $(this).children().toggleClass('flipped');
                });
            }
            
            function stopClickMode() {
                $('div.spinner').unbind('click');
                $('div.spinner').mouseleave(flipMe);
            }
        }
        
        
        function getPatterns() {
            return {
                pxlfunk: [
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 1, 1, 1, 0],
                    [0, 0, 0, 0, 1, 0, 1, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 1, 1, 0, 0],
                    [0, 1, 1, 1, 0, 0, 0, 1, 0], 
                    [0, 1, 1, 0, 0, 1, 1, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 1, 0, 1, 0, 1, 1, 1, 0],
                    [0, 0, 1, 0, 0, 1, 0, 0, 0],
                    [0, 1, 0, 1, 0, 1, 1, 1, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 1, 1, 1, 0, 1, 1, 1, 0],
                    [0, 0, 0, 1, 0, 0, 1, 0, 0],
                    [0, 0, 0, 0, 0, 1, 0, 1, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0]
                ]
                
                /*
                template: [
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [0, 0, 0, 0, 0, 0, 0, 0, 0]
                ]
                */
            }
        }
    }
);




