function placeLevelObject( x, y, z, levelId, contentObjectId )
{
   // store the object in the DB
    jQuery.ez( 'ezxfatesmith::store_level_object::' + x + '::' + y + '::' + z + '::' + levelId + '::' + contentObjectId,
        {postData: ''},
        function(data){
            if ( data.error_text != '' )
            {
                alert( data.error_text );
                return false;
            }

            var item = JSON.parse( data.content );
            if ( item.id == 0 )
                return false;

            // create the tile image on the level
            // showLevelObjectInstanceProperties does that too, but this is faster
            createLevelObjectImageOnLevel( x, y, z, contentObjectId, false, item.properties );

            // show the properties box
            showLevelObjectInstanceProperties( item.id );

            $('#levelObjectInstanceInfoField').empty();
        } );
}

function nl2br(str) {return str.replace(/\n/g, '<br />');}

function resizeFloorContainer()
{
    var floorContainer = $('#floorContainer');
    setPositivePxValue( floorContainer, 'width', $(window).width() - 370 );
    setPositivePxValue( floorContainer, 'height', $(window).height() - 45 );
}

function setPositivePxValue ( object, key, value )
{
    object.css( key, (value > 0 ? value : 0) + 'px' );
}


function menuTileClick( contentObjectId )
{
    if ( oldSelectedTileId > 0 )
        $( '#menuTile_' + oldSelectedTileId ).removeClass("selected");

    oldSelectedTileId = contentObjectId;
    selectedTile = tileList[contentObjectId];
    $( '#menuTile_' + contentObjectId ).addClass("selected");

    if ( selectedTile.data_map.identifier.content == "tile" )
        $('#zSpinBox').val( selectedTile.data_map.default_z.content );

    /*
    jQuery.ez( 'ezxfatesmith::fetch_default_properties::' + contentObjectId,
        {},
        function(data){
            if ( data.error_text != '' )
                alert( data.error_text );
            else
                $('#tilePropertiesField').html( data.content );
        } );
    */
}

function menuTileMouseOver( obj, contentObjectId )
{
    var tile = tileList[contentObjectId];
    $( '#tileInfoField' ).html( 'Click here to select tile "' + tile.name + '" (id: ' + tile.data_map.database_id.content + ')' );
}

function menuTileMouseOut( obj, contentObjectId )
{
    $( '#tileInfoField' ).empty();
}

function floorTileClick( x, y )
{
    if ( selectedTile == 0 ) return;
    var z = $('#zSpinBox').val();
    var levelId = $('#levelSpinBox').val();

    switch( selectedTile.data_map.identifier.content )
    {
        case "delete_x_y_z":
            if ( foreignWriteLockCheck() )
                removeLevelObjects( x, y, z, levelId );
        break;
        case "delete_x_y":
            if ( foreignWriteLockCheck() )
                removeLevelObjects( x, y, null, levelId );
        break;
        case "delete_level":
            if ( foreignWriteLockCheck() )
                if ( confirm( 'Do you realy want to clear level ' + levelId + '?' ) )
                    removeLevelObjects( null, null, null, levelId );
        break;
        case "create_border":
            if ( foreignWriteLockCheck() )
                if ( confirm( 'Do you realy want to place the wall border tiles on z=' + wallFloorTile.data_map.default_z.content + ' on level ' + levelId + '?' ) )
                    createLevelBorder( levelId );
        break;
        case "info":
            loadLevelObjectInstanceInfo( x, y );
        break;
        case "tile":
            if ( foreignWriteLockCheck() )
                placeLevelObject( x, y, z, levelId, selectedTile.id );
        break;
    }

}

function removeLevelObjectImages( x, y, z )
{
    // delete a level
    if ( ( x == null ) && ( y == null ) && ( z == null ) )
    {
        $('#floorContainerInner div.tile').each( function() {$(this).empty()} );
    }
    // delete all tiles on X/Y
    else if ( ( x != null ) && ( y != null ) && ( z == null ) )
    {
        $( '#floorTile_x' + x + '_y' + y ).each( function() {$(this).empty()} );
    }
    // delete a tile on X/Y/Z
    else if ( ( x != null ) && ( y != null ) && ( z != null ) )
    {
        $( '#floorTileImg_x' + x + '_y' + y + '_z' + z ).remove();
    }
}

function removeLevelObjects( x, y, z, levelId, reloadLevelObjectInstanceInfo )
{
    if ( !foreignWriteLockCheck() ) return false;

    removeLevelObjectImages( x, y, z );

    // remove the objects in the DB
    jQuery.ez( 'ezxfatesmith::remove_level_objects::' + x + '::' + y + '::' + z + '::' + levelId,
        {},
        function(data){
            if ( data.error_text != '' )
                alert( data.error_text );
            else
                if ( reloadLevelObjectInstanceInfo ) loadLevelObjectInstanceInfo( x, y );
        } );
}

/**
 * removes a level object instance
 *
 * @param id integer the id of the level object instance
 * @param x integer
 * @param y integer
 * @param z integer
 * @param removeObjectImages boolean
 * @return void
 */
function removeLevelObjectInstance(id, x, y, z, removeObjectImages) {
    if ( !foreignWriteLockCheck() ) return false;

    if ( removeObjectImages ) removeLevelObjectImages( x, y, z );

    // remove the object in the DB
    jQuery.ez( 'ezxfatesmith::remove_level_object_instance::' + id,
        {},
        function(data){
            if ( data.error_text != '' )
                alert( data.error_text );
            else
                $('#levelObjectInstanceInfoField').html( data.content );
        } );
}

function createLevelBorder( levelId )
{
    if ( levelId == null ) return false;

    var x = 0;
    var y = 0;
    var z = wallFloorTile.data_map.default_z.content;
    var contentObjectId = wallFloorTile.id;

    for( x = 0; x <= 31; x++ )
    {
        removeLevelObjectImages( x, 0, null );
        removeLevelObjectImages( x, 31, null );
        createLevelObjectImageOnLevel( x, 0, z, contentObjectId );
        createLevelObjectImageOnLevel( x, 31, z, contentObjectId );
    }

    for( y = 1; y < 31; y++ )
    {
        removeLevelObjectImages( 0, y, null );
        removeLevelObjectImages( 31, y, null );
        createLevelObjectImageOnLevel( 0, y, z, contentObjectId );
        createLevelObjectImageOnLevel( 31, y, z, contentObjectId );
    }

    // store the border in the DB
    jQuery.ez( 'ezxfatesmith::store_level_border::' + levelId + '::' + contentObjectId,
        {},
        function(data){
            if ( data.error_text != '' )
                alert( data.error_text );
        } );
}

function loadLevelObjectInstance( id )
{
    jQuery.ez( 'ezxfatesmith::fetch_level_object_instance::' + id,
        {},
        function(data){
            if ( data.error_text != '' )
            {
                alert( data.error_text );
                return false;
            }

            var item = JSON.parse( data.content );
            if ( item.id == 0 )
                return false;

            // create the tile image on the level
            createLevelObjectImageOnLevel( item.x, item.y, item.z, item.contentobject_id, false, item.properties );
        } );

}

function createLevelObjectImageOnLevel( x, y, z, contentObjectId, noOldImgCheck, properties )
{
    if ( noOldImgCheck == null ) noOldImgCheck = false;

    if ( !noOldImgCheck )
    {
        oldImg = $( '#floorTileImg_x' + x + '_y' + y + '_z' + z );

        // remove the current image from the floor
        if ( ! jQuery.isEmptyObject( oldImg ) )
            oldImg.remove();
    }

    var image = $(document.createElement("img"))
        .attr({
            'id': 'floorTileImg_x' + x + '_y' + y + '_z' + z,
            'src': '/' + tileList[contentObjectId].data_map.image.content.tile_rotate_0.url,
            'width': tileList[contentObjectId].data_map.image.content.tile_rotate_0.width,
            'height': tileList[contentObjectId].data_map.image.content.tile_rotate_0.height
         })
        .css('z-index', z )
        .appendTo( $( '#floorTile_x' + x + '_y' + y ) );

    if ( properties != undefined )
    {
        var delayedProperties = [];

        // draw some properties
        $.each( properties, function( i, property )
        {
            switch( property.name )
            {
                case "rotation":
                    delayedProperties.push( property );
                break;
                case "x_offset":
                    $('#floorTileImg_x' + x + '_y' + y + '_z' + z).css( 'left', parseInt( property.value ) + 'px' );
                break;
                case "y_offset":
                    $('#floorTileImg_x' + x + '_y' + y + '_z' + z).css( 'top', parseInt( property.value ) + 'px' );
                break;
            }
        } );

        // handle the delayed properties
        $.each( delayedProperties, function( i, property )
        {
            switch( property.name )
            {
                case "rotation":
                    // the rotation must be done last, because the canvas drawing is done in the background
                    $('#floorTileImg_x' + x + '_y' + y + '_z' + z).rotate( parseInt( property.value ) );
                break;
            }
        } );
    }
}

function floorSmithStartup()
{
    // set the empty floor tile
    $( '#floorContainerInner' ).css( 'background-image', 'url("/' + emptyFloorTile.data_map.image.content.tile_rotate_0.url + '")' );

    resizeFloorContainer();
    $(window).resize( function(){resizeFloorContainer()});

    $("#levelLoadProgressbar").progressbar({value: 0});

	$.ui.dialog.defaults.bgiframe = true;
    $("#itemDialog").dialog({
        autoOpen: false,
        width: 300,
        height: 300,
        buttons: {
            "Add items": function() {
                var selectedItems = [];
                $("#itemList li.ui-selected").each(function(){
                    selectedItems.push( parseInt( $(this).attr("id").split("_")[2] ) );
                });
                addItemsToContainer( currentContainerPropertyId, selectedItems );
            },
            Cancel: function() {
                $(this).dialog('close');
            }
        }
    });
    $("#levelLoadDialog").dialog({
        autoOpen: false,
        modal: true
    });
    $("#itemList").selectable();


    loadLevel( $('#levelSpinBox').val() );

    // create a cursor image on a high z-index
    $( '#floorContainerInner > div.tile' ).hover(
        function() {
            $('<img src="/extension/ezxfatesmith/design/standard/images/tileCursor.png" id="tileCursor" height="32" width="32" style="z-index: 999" />').appendTo($(this));
            var xyData = parseXYFromTileID($(this).attr('ID'));
            $( '#tileInfoField' ).html( 'x: ' + xyData.x + ' y: ' + xyData.y );

        },
        function() {
            $('#tileCursor').remove();
        }
    ).click(
        function() {
            var xyData = parseXYFromTileID($(this).attr('ID'));
            floorTileClick( xyData.x, xyData.y );
        }
    );

    // check if the level data is dirty every 10 seconds
    setInterval( "levelDataDirtyCheck()", 10000 );

    // handle the key events
    handleKeyEvents();
}

function parseXYFromTileID( tileID )
{
    var data = tileID.split('_');
    if ( data[0] != 'floorTile' ) return false;
    return {'x': parseInt(data[1].substr(1)), 'y': parseInt(data[2].substr(1))};
}

function loadLevel()
{
    var levelId = $('#levelSpinBox').val();

    var $levelLoadDialog = $('#levelLoadDialog');
    var $levelLoadProgressbar = $("#levelLoadProgressbar");
    $levelLoadProgressbar.progressbar( 'value', 0 );
    var $levelLoadStatusText = $('#levelLoadStatusText');
    $levelLoadStatusText.html( "Fetching data from server..." )

    $levelLoadDialog.dialog('open');
    $( '#ui-dialog-title-levelLoadDialog' ).html( "Level " + levelId + " is loading..." )
    jQuery.ez( 'ezxfatesmith::fetch_level::' + levelId,
        {},
        function( data )
        {
            if ( data.error_text )
            {
                alert( data.error_text );
                return false;
            }
            else
            {
                try
                {
                    $('#tileInfoField').empty();
                    $('#levelObjectInstanceInfoField').empty();
                    $('#tilePropertiesField').empty();

                    // remove the old tiles
                    $('#floorContainerInner div.tile').each( function() {$(this).empty()} );

                    // set the name edit box
                    $( '.level_edit_text' ).editable( 'destroy' );
                    $( '.level_edit_text' ).editable( '/ezjscore/call/ezxfatesmith::store_level::' + levelId, {
                        indicator : 'Saving...',
                        tooltip : 'Click to edit...'
                    });

                    // set the description edit box
                    $( '.level_edit_textarea' ).editable( 'destroy' );
                    $( '.level_edit_textarea' ).editable( '/ezjscore/call/ezxfatesmith::store_level::' + levelId, {
                        type : 'textarea',
                        submit : 'OK',
                        cancel : 'cancel',
                        rows : 3,
                        indicator : 'Saving...',
                        tooltip : 'Click to edit...'
                    });

                    currentLevel = JSON.parse( data.content );
                    if ( currentLevel.id == undefined )
                    {
                        $( '#levelName' ).html( 'click to set a name' );
                        $( '#levelDescription' ).html( 'click to set a description' );
                        $( '#levelLockField' ).empty();
                    }
                    else
                    {
                        $( '#levelName' ).html( currentLevel.name );
                        $( '#levelDescription' ).html( nl2br( currentLevel.description ) );
                        $( '#levelLockField' ).html( currentLevel.write_lock_container_html );

                        var factor = 100 / currentLevel.level_object_instances.length;
                        $levelLoadStatusText.html( "Placing tiles..." )

                        // place the new tiles
                        jQuery.each( currentLevel.level_object_instances, function( i, item )
                        {
                            $levelLoadProgressbar.progressbar( 'value', Math.ceil( i * factor ) );
                            createLevelObjectImageOnLevel( item.x, item.y, item.z, item.contentobject_id, true, item.properties );
                        } );
                    }
                }
                catch( err )
                {
                    // TODO: do something useful
                }

                $levelLoadDialog.dialog('close');
                return true;
            }
        }
    );
}

function loadLevelObjectInstanceInfo( x, y )
{
    var infoField = $( '#levelObjectInstanceInfoField' );
    var levelId = $('#levelSpinBox').val();

    jQuery.ez( 'ezxfatesmith::fetch_x_y_level_html::' + x + '::' + y + '::' + levelId,
        {},
        function(data){
            if ( data.error_text )
            {
                alert( data.error_text );
                return false;
            }
            else
            {
                infoField.html( data.content );
                $('#tilePropertiesField').empty();
                return true;
            }
        }
    );
}

function showLevelObjectInstanceProperties( levelObjectInstanceId )
{
    if ( oldSelectedLevelObjectInstanceId > 0 )
        $( '#menuLevelObjectInstance_' + oldSelectedLevelObjectInstanceId + ' img' ).removeClass("selected");

    oldSelectedLevelObjectInstanceId = levelObjectInstanceId;
    $( '#menuLevelObjectInstance_' + levelObjectInstanceId + ' img' ).addClass("selected");

    jQuery.ez( 'ezxfatesmith::fetch_level_object_instance_properties_html::' + levelObjectInstanceId,
        {},
        function(data){
            if ( data.error_text != '' )
                alert( data.error_text );
            else
                $('#tilePropertiesField').html( data.content );
        }
    );
}

function addLevelObjectInstanceProperty( levelObjectInstanceId, propertyContentObjectId )
{
    if ( !foreignWriteLockCheck() ) return false;

    jQuery.ez( 'ezxfatesmith::add_level_object_instance_property::' + levelObjectInstanceId + '::'+ propertyContentObjectId,
        {},
        function(data){
            if ( data.error_text != '' )
                alert( data.error_text );
            else
                if ( data.content != '' )
                    $('#tilePropertiesField').html( data.content );
        }
    );
}

function storeLevelObjectInstanceProperty( levelObjectInstancePropertyId, dataTypeString )
{
    if ( !foreignWriteLockCheck() ) return false;
    if ( levelObjectInstancePropertyId <= 0 ) return false;
    if ( dataTypeString == "" ) return false;

    switch( dataTypeString )
    {
        case "container":
        break;
        default:
            var value = $( '#property_value_' + levelObjectInstancePropertyId ).val();
            jQuery.ez( 'ezxfatesmith::store_level_object_instance_property::' + levelObjectInstancePropertyId,
                {
                    data: ({value: value})
                },
                function(data){
                    if ( data.error_text != '' )
                        alert( data.error_text );
                    else
                        if ( data.content != '' )
                            $('#tilePropertiesField').html( data.content );
                }
            );
        break;
    }
}

function deleteLevelObjectInstanceProperty( levelObjectInstancePropertyId )
{
    if ( !foreignWriteLockCheck() ) return false;
    if ( levelObjectInstancePropertyId <= 0 ) return false;

    jQuery.ez( 'ezxfatesmith::delete_level_object_instance_property::' + levelObjectInstancePropertyId,
        {},
        function(data){
            if ( data.error_text != '' )
                alert( data.error_text );
            else
                if ( data.content != '' )
                    $('#tilePropertiesField').html( data.content );
        }
    );
}

/**
 * lock/unlock the current level
 */
function levelLocking( action ) {
    if ( !foreignWriteLockCheck() ) return false;
    var levelId = parseInt( $('#levelSpinBox').val() );

    jQuery.ez( 'ezxfatesmith::level_locking::' + action + '::' + levelId,
        {},
        function(data){
            if ( data.error_text != '' )
                alert( data.error_text );
            else
                if ( data.content != '' )
                    $('#levelLockField').html( data.content );
        }
    );
}

/**
 * check if the level data is dirty
 */
function levelDataDirtyCheck() {
    var levelId = parseInt( $('#levelSpinBox').val() );

    jQuery.ez( 'ezxfatesmith::level_data_dirty_check::' + levelId + '::' + currentLevel.level_dirty_timestamp,
        {},
        function(data){
            if ( data.error_text != '' )
                alert( data.error_text );
            else
            {
                if ( data.content === true )
                    loadLevel();
            }
        }
    );
}

/**
 * check if the level is locked by an other one
 *
 * @return boolean true if no foreign write lock is present or false otherwise
 */
function foreignWriteLockCheck() {
    if ( currentLevel.has_foreign_write_lock )
        alert( "This level is locked by " + currentLevel.write_lock_user_name + "!" );
    return !currentLevel.has_foreign_write_lock;
}

/**
 * handles the key events
 */
function handleKeyEvents() {
    // test keypresses e.g. on http://www.texotela.co.uk/keypress.php
    $(window).keydown(function(event) {
        switch ( event.keyCode ) {
            case 188:   // KEY < ... rotate clockwise
            case 190:   // KEY > ... rotate counter-clockwise
                if ( currentLevelObjectInstanceId > 0 )
                {
                    var $propertyInput = $('.property-edit .datatype-rotation .value input');
                    // set the new angle
                    $propertyInput.val( calcRotationAngle( $propertyInput.val(), event.keyCode == 188 ) );
                    // store the property
                    $('.property-edit .datatype-rotation .controls .storeButton').click();
                }
            break;
        }
    });

}

/**
 * calculates rotation steps
 *
 * @param angle int
 * @param clockwise bool true for cw, flase for ccw
 * @return int new angle
 */
function calcRotationAngle(angle, clockwise) {
    var newAngle = 0;

    if (clockwise)  // cw
    {
        if ( angle < 90 )
            newAngle = 90;
        else if ( angle < 180 )
            newAngle = 180;
        else if ( angle < 270 )
            newAngle = 270;
    }
    else    // ccw
    {
        if ( ( angle > 270 ) || ( angle == 0 ) )
            newAngle = 270;
        else if ( angle > 180 )
            newAngle = 180;
        else if ( angle > 90 )
            newAngle = 90;
    }

    return newAngle;
}

/**
 * adds items to a level object instance container
 *
 * @param levelobjectinstanceid integer
 * @param items array item content object ids
 * @return bool true|false
 */
function addItemsToContainer(containerPropertyId, items) {
    if ( containerPropertyId == 0 ) return false;
    if ( items.length == 0 ) return false;

    jQuery.ez( 'ezxfatesmith::add_items_to_container::' + containerPropertyId + '::' + items.join('-'),
        {},
        function(data){
            if ( data.error_text != '' )
            {
                alert( data.error_text );
                return false;
            }

            $('#levelObjectInstanceInfoField').html( data.content );
        } );
    return true;
}


jQuery.fn._offset = jQuery.fn.offset;
jQuery.fn.extend({
    offset: function() {
        var a = arguments;
        return (a.length) ? this.animate({top: a[0].top || a[0],
left: a[0].left || a[1]}, (a[0].top ? a[1] : a[2]) || 1) :
this._offset();
    }

});


// the tile the user clicked on
var selectedTile = 0;
var oldSelectedTileId = 0;
var oldSelectedLevelObjectInstanceId = 0;
var currentLevel;
var currentLevelObjectInstanceId = 0;
var currentContainerPropertyId = 0;