function alllife(){
    /** @private */
    var _cache = [];
    var _allDownloaded = false;
    var _amount = 0;
    var _showsAll = false;
    var _listChangedCallback;

    this.init = function(callback){
        this.setCallback(callback);

        $.ajaxSetup({
            'dataType': 'json',
            'error'     : function (XMLHttpRequest, textStatus, errorThrown) { alert(arguments); }
        });
    };

    this.setCallback = function(callback){
        _listChangedCallback = callback || new Function();
    };

    this.getInterviews = function () {
        return _cache;
    };

    this.load = function(amount){
        if( (!_cache.length || amount > _cache.length || 0 == amount) && ! _allDownloaded ){
            // get only images not in cache
            $.get(
                '/ajax/list/?limit=' + (_showsAll?0:(amount - _amount)) + '&offset=' + _cache.length + '&callback=?',
                function(data){
                    // ignore errors
                    if(data['errors'].length){
                        return false;
                    }
                    // add downloaded data to cache
                    for( var i = 0; i < data.result.length; i++){
                        var interview = data.result[i];
                        _cache.push(interview);
                    }
                    // notify UI
                    _listChangedCallback(_amount, _cache.length);
                    _amount = _cache.length;
                }
            );
            _allDownloaded = (0 == amount);
            return true;
        }
        if(!amount)
            amount = _cache.length;
        _listChangedCallback(_amount, amount);
        _amount = amount;
        return true;
    };

    this.toggleAll = function(){
        return (_showsAll = !_showsAll);
    };

    /**
     * 
     * @param {Number} id Numeric identifier of object
     * @return {Object|null} interview
     */
    this.get = function (id) {
        var result = null;
        for(var interview in _cache){
            if(_cache[interview].id == id){
                result = _cache[interview];
                break;
            }
        }
        return result;
    };

};

var metrics = {};
var app = new alllife();
var allShown = false;
var pinned = false;
var popupTimer = null;

/**
 * Calculate maximum size of gallery based on current window size
 * 
 */
function calcGridDimensions(){
    var h = $(window).height() - metrics.header - metrics.banner - metrics.footer - metrics.container.padding.vert * 2;
    var w = $(document).width() - metrics.container.padding.horz * 2;

    var rows = Math.max(1, Math.floor(h / metrics.container.photo.height));
    var cols = Math.floor(w / metrics.container.photo.width);

    var containerPadding = {
        'horz': metrics.container.padding.horz + Math.floor((w - cols * metrics.container.photo.width) / 2),
        'vert': metrics.container.padding.vert + Math.floor((h - rows * metrics.container.photo.height) / 2)
    };

    return {
    	/** Количество рядов */
        'rows'			: rows,
    	/** Количество столбцов */
        'cols'			: cols,
        /** Отступ от краев по горизонтали */
    	'marginHorz'	: containerPadding.horz,
        /** Отступ от краев по вертикали */
    	'marginVert'	: containerPadding.vert
    };
}

/**
 * Autosize container to fit gallery 
 */
function autosize(){
    var grid = calcGridDimensions();
    $('#vcard').hide();
    $('#container')
    	.css('padding', grid.marginVert + 'px ' + (grid.marginHorz - 4) + 'px');
    app.load( grid.rows * grid.cols );
}

/**
 * 
 * @param {Object} data Data to be displayed
 * @param {Boolean} pin  If true, the info will be pinned until user hovers it
 * @return {Boolean}
 */
function showVcard(data,  pin){
    if(!data) return false;

    // don't show vcard if there's one pinned
    if(!pin && pinned) return false;

    var container = $('#container');
    var photo = $('#container img#interview' + data.id);
    var vcard = $('#vcard');
    var photoPos = {};

    // if photo is not present in the list below
    if(!photo.size() || pin){
        photoPos = {
            'top'   : container.offset().top - 10,
            'left'  : container.outerWidth() / 2
        };
    } else {    
    	photoPos = photo.position();
    	photoPos = {
			'top'	: photoPos.top - vcard.outerHeight(),
    		'left'	: photoPos.left - 18
    	};
    }

    if (photoPos.left > (container.width() - vcard.width())) {
    	photoPos['left'] = container.width() - vcard.width() + parseInt(container.css('padding-left'));
	};

    vcard
        .css({ opacity: 'hide' })
        .show()
        .find('img.photo')
            .attr('src', data.image)
        .end()
        .find('.subject a')
            .text(data.subject)
            .attr('href', data.url)
        .end()
        .find('.name')
            .text(data.display_name)
        .end()
        .css({
            'top'   : photoPos.top + 'px',
            'left'  : photoPos.left + 'px'
        })
        .animate(
            {
                'opacity': 'show',
                'top': '+=5px'
            },
            'medium'
        );
        
    pinned = (arguments.length > 1) && pin;
    return false;
}

/**
 * Show info about the project
 */
function showAbout(){
    $('#about').fadeIn('medium');
}

/**
 * Create callback to track loading progress
 * @param {Number} total Total number of items to load
 * @return {Function} Callback
 */
function progress(total){
	var 
		_total = total,
		_done = 0,
		$loader = $('#loader'),
		$percentage = $loader.find('#percentage'),
		$progressBar = $loader.find('#progress'),
		h = 0;
	
	h = $loader.height();
	$loader
		.fadeIn('fast');
	
	function update(){
		if(!_total) return;
		
		var percentage = parseInt(100 * _done / _total);
		$percentage.text(percentage);
		$progressBar.width(percentage + '%');
		
		if(_done >= _total){
			$loader.fadeOut();
			return;
		}

	}
	
	return function(){
		_done++;
		update();
	};
};

/**
 * Callback called when new data received from server
 * @param {Number} from Old number of elements in the gallery
 * @param {Number} to   New number of elements in the gallery
 */
function listChanged(from, to){
    var interviews = app.getInterviews();
    var $container = $('#container');
    if(to > from){
    	var loaderNeeded = (0 === from);
    	var html = '';
    	if(loaderNeeded){
    		var loadCallback = progress(to - from);
    	}
        for(var i = from, $image; i < to; i++){
        	if(loaderNeeded)
        	{
        		$image = $('<img id="interview' + interviews[i].id + '" src="' + interviews[i].thumbnail + '" />');
        		$image
        			.load(loadCallback)
        			.error(loadCallback);
        		$container.append($('<a href="' + interviews[i].url + '" target="_blank"></a>').append($image));
        	} else {
        		html += '<a href="' + interviews[i].url + '" target="_blank"><img id="interview' + interviews[i].id + '" src="' + interviews[i].thumbnail + '" /></a>';
        	}
        }
    	if(!loaderNeeded)
    	{
    		$container.html($container.html()+html);
    	}
            
    } else if(!allShown) {
       $('a:gt(' + (to - 1) + ')', $container).remove();
    }

}

/**
 * Setup application handlers
 */
$(function(){

	// add intro movie
	function addIntroMovie(){
		swfobject.embedSWF(
				'http://video.zingan.com/mediaplayer.swf',
				'intro',
				'530',
				'398',
				'9.0.0',
				null,
				{
					'file'				: 'http://video.zingan.com/allfun/AllLife_231009.mp4',
					'image'				: 'http://video.zingan.com/allfun/AllLife_231009.jpg',
					'fallback'			: 'http://video.zingan.com/allfun/AllLife_231009.flv',
					'width'				: 530,
					'height'			: 398,
					'shownavigation'	: true
				}
		);
	}
	
	// add autocomplete for search
	function initSearch(){
		$('#search').autocomplete(
				'/ajax/search?callback=?',
				{
					scroll: true,
					scrollHeight: 300,
					cacheLength: 1,
					minChars: 3,
					parse: function(data){
					var results = [];
					var fullName = '';
					for(var i in data.result) {
						fullName = data.result[i].display_name;
						results.push({
							'data'  : data.result[i],
							'value' : fullName,
							'result': fullName
						});
					}
					return results;
				},
				'formatItem'    : function(data, index, max, value, term){
					return '<img src="' + data.thumbnail + '" width="20" height="20" />' + value + '';
				}
				}
		)
		.result(function(event, data, formatted){
			showVcard(data, true);
		});
	}

    /**
     * Basic metrics of main page components
     */
	metrics = {
        /** Height of header section */
        'header'    : $('#header').height(),
        'banner'    : 0,
        'container' : {
            /** Minimum padding allowed for gallery container */
            'padding'   : { 'horz': 20, 'vert': 15 },
            /** Size of photo,  including margins */
            'photo'     : { 'width': 44, 'height': 44 }
        },
        /** Height of footer section */
        'footer'    : $('#footer').height()
    };
    var searchTimer;
    var baloonTimer;
    var aboutTimer;
    
    // default animation easing method
    $.easing.def = "swing";
   
    var $loader = $('#loader');
    h = $loader.height();
	$loader
		.css('padding-top', parseInt(h / 3) + 'px');
 
    addIntroMovie();
    initSearch();

    $('#container img').live('mouseover', function(){
        clearTimeout(popupTimer);
        popupTimer = setTimeout("showVcard(app.get(/\\d+$/.exec('" + $(this).attr('id') + "')))", 500);
	}); 
    $('#header .about-button').hover(function(){
        aboutTimer = setTimeout('showAbout()', 500);
    }, function(){
        clearTimeout(aboutTimer);
    });
    $('#about .close-button').click(function(){
        $('#about').fadeOut('medium');
        // TODO: stop playing movie
        return false;
    });
    
    // hide vcard when pointer is outside of container
    $('#header').mouseover(function(){
        popupTimer = setTimeout("if(!pinned) $('#vcard').hide()", 400);
    });
    
    
    app.init(listChanged);
    $(window).resize(autosize);
    autosize();

    $('#footer .show-all a')
        .click(function(){
            var caption = '',
            	$footer = $('#footer'),
            	allShown = app.toggleAll();

            if(allShown){
                $(window).unbind('resize', autosize);
            	app.load(0);
            	$(this).removeClass('expand').addClass('collapse');
            	$footer.css('position', 'relative');
                caption = 'Свернуть';
            } else {
            	$('#vcard').hide();
                $(window).bind('resize', autosize);
            	$footer.css('position', 'absolute');
                autosize();
                $(this).removeClass('collapse').addClass('expand');
                caption = 'Показать всех';
            }
            $(this).find('span').text(caption);
            return false;
        }
    );

    $('#vcard').hover(
        function(){
        	pinned = false;
            clearTimeout(popupTimer);
        },
        function(){
            popupTimer = setTimeout("$('#vcard').fadeOut('medium')", 400);
        }
    );

});
