/*
 * This file is part of ComputerCraft - http://www.computercraft.info
 * Copyright Daniel Ratcliffe, 2011-2016. Do not distribute without permission.
 * Send enquiries to dratcliffe@gmail.com
 */

package dan200.computercraft.shared.proxy;

import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.core.computer.Computer;
import dan200.computercraft.core.computer.MainThread;
import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider;
import dan200.computercraft.shared.common.TileGeneric;
import dan200.computercraft.shared.computer.blocks.BlockCommandComputer;
import dan200.computercraft.shared.computer.blocks.BlockComputer;
import dan200.computercraft.shared.computer.blocks.TileCommandComputer;
import dan200.computercraft.shared.computer.blocks.TileComputer;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
import dan200.computercraft.shared.computer.items.ComputerItemFactory;
import dan200.computercraft.shared.computer.items.ItemCommandComputer;
import dan200.computercraft.shared.computer.items.ItemComputer;
import dan200.computercraft.shared.media.common.DefaultMediaProvider;
import dan200.computercraft.shared.media.inventory.ContainerHeldItem;
import dan200.computercraft.shared.media.items.ItemDiskExpanded;
import dan200.computercraft.shared.media.items.ItemDiskLegacy;
import dan200.computercraft.shared.media.items.ItemPrintout;
import dan200.computercraft.shared.media.items.ItemTreasureDisk;
import dan200.computercraft.shared.media.recipes.DiskRecipe;
import dan200.computercraft.shared.media.recipes.PrintoutRecipe;
import dan200.computercraft.shared.network.ComputerCraftPacket;
import dan200.computercraft.shared.peripheral.PeripheralType;
import dan200.computercraft.shared.peripheral.commandblock.CommandBlockPeripheralProvider;
import dan200.computercraft.shared.peripheral.common.*;
import dan200.computercraft.shared.peripheral.diskdrive.ContainerDiskDrive;
import dan200.computercraft.shared.peripheral.diskdrive.TileDiskDrive;
import dan200.computercraft.shared.peripheral.modem.BlockAdvancedModem;
import dan200.computercraft.shared.peripheral.modem.TileAdvancedModem;
import dan200.computercraft.shared.peripheral.modem.TileCable;
import dan200.computercraft.shared.peripheral.modem.TileWirelessModem;
import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
import dan200.computercraft.shared.peripheral.printer.ContainerPrinter;
import dan200.computercraft.shared.peripheral.printer.TilePrinter;
import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import dan200.computercraft.shared.pocket.items.PocketComputerItemFactory;
import dan200.computercraft.shared.pocket.peripherals.PocketModem;
import dan200.computercraft.shared.pocket.recipes.PocketComputerUpgradeRecipe;
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
import dan200.computercraft.shared.turtle.inventory.ContainerTurtle;
import dan200.computercraft.shared.util.*;
import net.minecraft.block.Block;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.item.Item;
import net.minecraft.item.ItemRecord;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.play.server.SPacketUpdateTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumHand;
import net.minecraft.util.IThreadListener;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.fml.client.event.ConfigChangedEvent;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import net.minecraftforge.fml.common.network.FMLNetworkEvent;
import net.minecraftforge.fml.common.network.IGuiHandler;
import net.minecraftforge.fml.common.network.NetworkRegistry;
import net.minecraftforge.fml.common.registry.GameRegistry;
import net.minecraftforge.oredict.RecipeSorter;

import java.io.File;

public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy
{
    public ComputerCraftProxyCommon()
    {
    }

    // IComputerCraftProxy implementation

    @Override
    public void preInit()
    {
        registerItems();
    }

    @Override
    public void init()
    {
        registerTileEntities();
        registerForgeHandlers();
    }

    @Override
    public abstract boolean isClient();

    @Override
    public abstract boolean getGlobalCursorBlink();

    @Override
    public abstract long getRenderFrame();

    @Override
    public void deleteDisplayLists( int list, int range )
    {
    }

    @Override
    public abstract Object getFixedWidthFontRenderer();

    @Override
    public String getRecordInfo( ItemStack recordStack )
    {
        Item item = recordStack.getItem();
        if (item instanceof ItemRecord)
        {
            ItemRecord record = (ItemRecord) item;
            String key = ObfuscationReflectionHelper.getPrivateValue( ItemRecord.class, record, "field_185077_c" );
            return StringUtil.translateToLocal( key );
        }
        return null;
    }

    @Override
    public abstract void playRecord( SoundEvent record, String recordInfo, World world, BlockPos pos );

    @Override
    public abstract Object getDiskDriveGUI( InventoryPlayer inventory, TileDiskDrive drive );

    @Override
    public abstract Object getComputerGUI( TileComputer computer );

    @Override
    public abstract Object getPrinterGUI( InventoryPlayer inventory, TilePrinter printer );

    @Override
    public abstract Object getTurtleGUI( InventoryPlayer inventory, TileTurtle turtle );

    @Override
    public abstract Object getPrintoutGUI( EntityPlayer player, EnumHand hand );

    @Override
    public abstract Object getPocketComputerGUI( EntityPlayer player, EnumHand hand );

    public abstract File getWorldDir( World world );

    @Override
    public void handlePacket( final ComputerCraftPacket packet, final EntityPlayer player )
    {
        IThreadListener listener = player.getServer();
        if (listener != null)
        {
            if (listener.isCallingFromMinecraftThread())
            {
                processPacket( packet, player );
            } else
            {
                listener.addScheduledTask( new Runnable()
                {
                    @Override
                    public void run()
                    {
                        processPacket( packet, player );
                    }
                } );
            }
        }
    }

    private void processPacket( ComputerCraftPacket packet, EntityPlayer player )
    {
        switch (packet.m_packetType)
        {
            ///////////////////////////////////
            // Packets from Client to Server //
            ///////////////////////////////////
            case ComputerCraftPacket.TurnOn:
            case ComputerCraftPacket.Shutdown:
            case ComputerCraftPacket.Reboot:
            case ComputerCraftPacket.QueueEvent:
            case ComputerCraftPacket.RequestComputerUpdate:
            case ComputerCraftPacket.SetLabel:
            {
                int instance = packet.m_dataInt[0];
                ServerComputer computer = ComputerCraft.serverComputerRegistry.get( instance );
                if (computer != null)
                {
                    computer.handlePacket( packet, player );
                }
                break;
            }
            case ComputerCraftPacket.RequestTileEntityUpdate:
            {
                int x = packet.m_dataInt[0];
                int y = packet.m_dataInt[1];
                int z = packet.m_dataInt[2];
                BlockPos pos = new BlockPos( x, y, z );
                World world = player.getEntityWorld();
                TileEntity tileEntity = world.getTileEntity( pos );
                if (tileEntity != null && tileEntity instanceof TileGeneric)
                {
                    TileGeneric generic = (TileGeneric) tileEntity;
                    SPacketUpdateTileEntity description = generic.getUpdatePacket();
                    if (description != null)
                    {
                        ((EntityPlayerMP) player).connection.sendPacket( description );
                    }
                }
                break;
            }
        }
    }

    private void registerItems()
    {
        // Creative tab
        ComputerCraft.mainCreativeTab = new CreativeTabMain( CreativeTabs.getNextID() );

        // Blocks
        // Computer
        ComputerCraft.Blocks.computer = new BlockComputer();
        registerBlock( ComputerCraft.Blocks.computer, new ItemComputer( ComputerCraft.Blocks.computer ), "CC-Computer" );

        // Peripheral
        ComputerCraft.Blocks.peripheral = new BlockPeripheral();
        registerBlock( ComputerCraft.Blocks.peripheral, new ItemPeripheral( ComputerCraft.Blocks.peripheral ), "CC-Peripheral" );

        // Cable
        ComputerCraft.Blocks.cable = new BlockCable();
        registerBlock( ComputerCraft.Blocks.cable, new ItemCable( ComputerCraft.Blocks.cable ), "CC-Cable" );

        // Command Computer
        ComputerCraft.Blocks.commandComputer = new BlockCommandComputer();
        registerBlock( ComputerCraft.Blocks.commandComputer, new ItemCommandComputer( ComputerCraft.Blocks.commandComputer ), "command_computer" );

        // Command Computer
        ComputerCraft.Blocks.advancedModem = new BlockAdvancedModem();
        registerBlock( ComputerCraft.Blocks.advancedModem, new ItemAdvancedModem( ComputerCraft.Blocks.advancedModem ), "advanced_modem" );

        // Items
        // Floppy Disk
        ComputerCraft.Items.disk = new ItemDiskLegacy();
        GameRegistry.register( ComputerCraft.Items.disk.setRegistryName( new ResourceLocation( ComputerCraft.MOD_ID, "disk" ) ) );

        ComputerCraft.Items.diskExpanded = new ItemDiskExpanded();
        GameRegistry.register( ComputerCraft.Items.diskExpanded.setRegistryName( new ResourceLocation( ComputerCraft.MOD_ID, "diskExpanded" ) ) );

        // Treasure Disk
        ComputerCraft.Items.treasureDisk = new ItemTreasureDisk();
        GameRegistry.register( ComputerCraft.Items.treasureDisk.setRegistryName( new ResourceLocation( ComputerCraft.MOD_ID, "treasureDisk" ) ) );

        // Printout
        ComputerCraft.Items.printout = new ItemPrintout();
        GameRegistry.register( ComputerCraft.Items.printout.setRegistryName( new ResourceLocation( ComputerCraft.MOD_ID, "printout" ) ) );

        // Pocket computer
        ComputerCraft.Items.pocketComputer = new ItemPocketComputer();
        GameRegistry.register( ComputerCraft.Items.pocketComputer.setRegistryName( new ResourceLocation( ComputerCraft.MOD_ID, "pocketComputer" ) ) );

        // Recipe types
        RecipeSorter.register( "computercraft:impostor", ImpostorRecipe.class, RecipeSorter.Category.SHAPED, "after:minecraft:shapeless" );
        RecipeSorter.register( "computercraft:impostor_shapeless", ImpostorShapelessRecipe.class, RecipeSorter.Category.SHAPELESS, "after:minecraft:shapeless" );
        RecipeSorter.register( "computercraft:disk", DiskRecipe.class, RecipeSorter.Category.SHAPELESS, "after:minecraft:shapeless" );
        RecipeSorter.register( "computercraft:printout", PrintoutRecipe.class, RecipeSorter.Category.SHAPELESS, "after:minecraft:shapeless" );
        RecipeSorter.register( "computercraft:pocket_computer_upgrade", PocketComputerUpgradeRecipe.class, RecipeSorter.Category.SHAPELESS, "after:minecraft:shapeless" );

        // Recipes
        // Computer
        ItemStack computer = ComputerItemFactory.create( -1, null, ComputerFamily.Normal );
        GameRegistry.addRecipe( computer,
                "XXX", "XYX", "XZX",
                'X', Blocks.STONE,
                'Y', Items.REDSTONE,
                'Z', Blocks.GLASS_PANE
        );

        // Advanced Computer
        ItemStack advancedComputer = ComputerItemFactory.create( -1, null, ComputerFamily.Advanced );
        GameRegistry.addRecipe( advancedComputer,
                "XXX", "XYX", "XZX",
                'X', Items.GOLD_INGOT,
                'Y', Items.REDSTONE,
                'Z', Blocks.GLASS_PANE
        );

        // Disk Drive
        ItemStack diskDrive = PeripheralItemFactory.create( PeripheralType.DiskDrive, null, 1 );
        GameRegistry.addRecipe( diskDrive,
                "XXX", "XYX", "XYX",
                'X', Blocks.STONE,
                'Y', Items.REDSTONE
        );

        // Wireless Modem
        ItemStack wirelessModem = PeripheralItemFactory.create( PeripheralType.WirelessModem, null, 1 );
        GameRegistry.addRecipe( wirelessModem,
                "XXX", "XYX", "XXX",
                'X', Blocks.STONE,
                'Y', Items.ENDER_PEARL
        );

        // Monitor
        ItemStack monitor = PeripheralItemFactory.create( PeripheralType.Monitor, null, 1 );
        GameRegistry.addRecipe( monitor,
                "XXX", "XYX", "XXX",
                'X', Blocks.STONE,
                'Y', Blocks.GLASS_PANE
        );

        // PrinterEmpty
        ItemStack printer = PeripheralItemFactory.create( PeripheralType.Printer, null, 1 );
        GameRegistry.addRecipe( printer,
                "XXX", "XYX", "XZX",
                'X', Blocks.STONE,
                'Y', Items.REDSTONE,
                'Z', new ItemStack( Items.DYE, 1, 0 ) // 0 = Black
        );

        // Advanced Monitor
        ItemStack advancedMonitors = PeripheralItemFactory.create( PeripheralType.AdvancedMonitor, null, 4 );
        GameRegistry.addRecipe( advancedMonitors,
                "XXX", "XYX", "XXX",
                'X', Items.GOLD_INGOT,
                'Y', Blocks.GLASS_PANE
        );

        // Networking Cable
        ItemStack cable = PeripheralItemFactory.create( PeripheralType.Cable, null, 6 );
        GameRegistry.addRecipe( cable,
                " X ", "XYX", " X ",
                'X', Blocks.STONE,
                'Y', Items.REDSTONE
        );

        // Wired Modem
        ItemStack wiredModem = PeripheralItemFactory.create( PeripheralType.WiredModem, null, 1 );
        GameRegistry.addRecipe( wiredModem,
                "XXX", "XYX", "XXX",
                'X', Blocks.STONE,
                'Y', Items.REDSTONE
        );

        // Computer
        ItemStack commandComputer = ComputerItemFactory.create( -1, null, ComputerFamily.Command );
        GameRegistry.addRecipe( commandComputer,
                "XXX", "XYX", "XZX",
                'X', Blocks.STONE,
                'Y', Blocks.COMMAND_BLOCK,
                'Z', Blocks.GLASS_PANE
        );

        // Advanced Modem
        ItemStack advancedModem = PeripheralItemFactory.create( PeripheralType.AdvancedModem, null, 1 );
        GameRegistry.addRecipe( advancedModem,
                "XXX", "XYX", "XXX",
                'X', Items.GOLD_INGOT,
                'Y', Items.ENDER_EYE
        );

        // Disk
        GameRegistry.addRecipe( new DiskRecipe() );

        // Impostor Disk recipes (to fool NEI)
        ItemStack paper = new ItemStack( Items.PAPER, 1 );
        ItemStack redstone = new ItemStack( Items.REDSTONE, 1 );
        ItemStack basicDisk = ItemDiskLegacy.createFromIDAndColour( -1, null, Colour.Blue.getHex() );
        GameRegistry.addRecipe( new ImpostorShapelessRecipe( basicDisk, new ItemStack[]{redstone, paper} ) );

        for (int colour = 0; colour < 16; ++colour)
        {
            ItemStack disk = ItemDiskLegacy.createFromIDAndColour( -1, null, Colour.values()[colour].getHex() );
            ItemStack dye = new ItemStack( Items.DYE, 1, colour );
            for (int otherColour = 0; otherColour < 16; ++otherColour)
            {
                if (colour != otherColour)
                {
                    ItemStack otherDisk = ItemDiskLegacy.createFromIDAndColour( -1, null, Colour.values()[colour].getHex() );
                    GameRegistry.addRecipe( new ImpostorShapelessRecipe( disk, new ItemStack[]{
                            otherDisk, dye
                    } ) );
                }
            }
            GameRegistry.addRecipe( new ImpostorShapelessRecipe( disk, new ItemStack[]{
                    redstone, paper, dye
            } ) );
        }

        // Printout
        GameRegistry.addRecipe( new PrintoutRecipe() );

        ItemStack singlePrintout = ItemPrintout.createSingleFromTitleAndText( null, null, null );
        ItemStack multiplePrintout = ItemPrintout.createMultipleFromTitleAndText( null, null, null );
        ItemStack bookPrintout = ItemPrintout.createBookFromTitleAndText( null, null, null );

        // Impostor Printout recipes (to fool NEI)
        ItemStack string = new ItemStack( Items.STRING, 1, 0 );
        GameRegistry.addRecipe( new ImpostorShapelessRecipe( multiplePrintout, new ItemStack[]{singlePrintout, singlePrintout, string} ) );

        ItemStack leather = new ItemStack( Items.LEATHER, 1, 0 );
        GameRegistry.addRecipe( new ImpostorShapelessRecipe( bookPrintout, new ItemStack[]{leather, singlePrintout, string} ) );

        // Pocket Computer
        ItemStack pocketComputer = PocketComputerItemFactory.create( -1, null, ComputerFamily.Normal, null );
        GameRegistry.addRecipe( pocketComputer,
                "XXX", "XYX", "XZX",
                'X', Blocks.STONE,
                'Y', Items.GOLDEN_APPLE,
                'Z', Blocks.GLASS_PANE
        );

        // Advanced Pocket Computer
        ItemStack advancedPocketComputer = PocketComputerItemFactory.create( -1, null, ComputerFamily.Advanced, null );
        GameRegistry.addRecipe( advancedPocketComputer,
                "XXX", "XYX", "XZX",
                'X', Items.GOLD_INGOT,
                'Y', Items.GOLDEN_APPLE,
                'Z', Blocks.GLASS_PANE
        );

        // Register pocket upgrades
        ComputerCraft.PocketUpgrades.wirelessModem = new PocketModem( false );
        ComputerCraftAPI.registerPocketUpgrade( ComputerCraft.PocketUpgrades.wirelessModem );
        ComputerCraft.PocketUpgrades.advancedModem = new PocketModem( true );
        ComputerCraftAPI.registerPocketUpgrade( ComputerCraft.PocketUpgrades.advancedModem );

        // Wireless Pocket Computer
        GameRegistry.addRecipe( new PocketComputerUpgradeRecipe() );

        // Impostor Pocket Computer recipes (to fool NEI)
        for (IPocketUpgrade upgrade : ComputerCraft.getVanillaPocketUpgrades())
        {
            GameRegistry.addRecipe( new ImpostorRecipe(
                1, 2,
                new ItemStack[]{ upgrade.getCraftingItem(), pocketComputer },
                PocketComputerItemFactory.create( -1, null, ComputerFamily.Normal, upgrade )
            ) );

            GameRegistry.addRecipe( new ImpostorRecipe(
                1, 2,
                new ItemStack[]{ upgrade.getCraftingItem(), advancedPocketComputer },
                PocketComputerItemFactory.create( -1, null, ComputerFamily.Advanced, upgrade )
            ) );
        }

        // Skulls (Easter Egg)
        // Dan
        NBTTagCompound tag = new NBTTagCompound();
        tag.setString( "SkullOwner", "dan200" );
        ItemStack danHead = new ItemStack( Items.SKULL, 1, 3 );
        danHead.setTagCompound( tag );
        GameRegistry.addShapelessRecipe( danHead, computer, new ItemStack( Items.SKULL, 1, 1 ) );

        // Cloudy
        tag = new NBTTagCompound();
        tag.setString( "SkullOwner", "Cloudhunter" );
        ItemStack cloudyHead = new ItemStack( Items.SKULL, 1, 3 );
        cloudyHead.setTagCompound( tag );
        GameRegistry.addShapelessRecipe( cloudyHead, monitor, new ItemStack( Items.SKULL, 1, 1 ) );
    }

    private void registerBlock( Block block, Item item, String name) {
        GameRegistry.register( block.setRegistryName( new ResourceLocation( ComputerCraft.MOD_ID, name ) ) );
        GameRegistry.register( item.setRegistryName( new ResourceLocation( ComputerCraft.MOD_ID, name ) ) );
    }

    private void registerTileEntities()
    {
        // Tile Entities
        GameRegistry.registerTileEntity( TileComputer.class, "computer" );
        GameRegistry.registerTileEntity( TileDiskDrive.class, "diskdrive" );
        GameRegistry.registerTileEntity( TileWirelessModem.class, "wirelessmodem" );
        GameRegistry.registerTileEntity( TileMonitor.class, "monitor" );
        GameRegistry.registerTileEntity( TilePrinter.class, "ccprinter" );
        GameRegistry.registerTileEntity( TileCable.class, "wiredmodem" );
        GameRegistry.registerTileEntity( TileCommandComputer.class, "command_computer" );
        GameRegistry.registerTileEntity( TileAdvancedModem.class, "advanced_modem" );

        // Register peripheral providers
        ComputerCraftAPI.registerPeripheralProvider( new DefaultPeripheralProvider() );
        if (ComputerCraft.enableCommandBlock)
        {
            ComputerCraftAPI.registerPeripheralProvider( new CommandBlockPeripheralProvider() );
        }

        // Register bundled power providers
        ComputerCraftAPI.registerBundledRedstoneProvider( new DefaultBundledRedstoneProvider() );

        // Register media providers
        ComputerCraftAPI.registerMediaProvider( new DefaultMediaProvider() );
    }

    private void registerForgeHandlers()
    {
        ForgeHandlers handlers = new ForgeHandlers();
        MinecraftForge.EVENT_BUS.register( handlers );
        NetworkRegistry.INSTANCE.registerGuiHandler( ComputerCraft.instance, handlers );
    }

    public class ForgeHandlers implements
            IGuiHandler
    {
        private ForgeHandlers()
        {
        }

        // IGuiHandler implementation

        @Override
        public Object getServerGuiElement( int id, EntityPlayer player, World world, int x, int y, int z )
        {
            BlockPos pos = new BlockPos( x, y, z );
            switch (id)
            {
                case ComputerCraft.diskDriveGUIID:
                {
                    TileEntity tile = world.getTileEntity( pos );
                    if (tile != null && tile instanceof TileDiskDrive)
                    {
                        TileDiskDrive drive = (TileDiskDrive) tile;
                        return new ContainerDiskDrive( player.inventory, drive );
                    }
                    break;
                }
                case ComputerCraft.computerGUIID:
                {
                    TileEntity tile = world.getTileEntity( pos );
                    if (tile != null && tile instanceof TileComputer)
                    {
                        TileComputer computer = (TileComputer) tile;
                        return new ContainerComputer( computer );
                    }
                    break;
                }
                case ComputerCraft.printerGUIID:
                {
                    TileEntity tile = world.getTileEntity( pos );
                    if (tile != null && tile instanceof TilePrinter)
                    {
                        TilePrinter printer = (TilePrinter) tile;
                        return new ContainerPrinter( player.inventory, printer );
                    }
                    break;
                }
                case ComputerCraft.turtleGUIID:
                {
                    TileEntity tile = world.getTileEntity( pos );
                    if (tile != null && tile instanceof TileTurtle)
                    {
                        TileTurtle turtle = (TileTurtle) tile;
                        return new ContainerTurtle( player.inventory, turtle.getAccess(), turtle.getServerComputer() );
                    }
                    break;
                }
                case ComputerCraft.printoutGUIID:
                {
                    return new ContainerHeldItem( player, x == 0 ? EnumHand.MAIN_HAND : EnumHand.MAIN_HAND );
                }
                case ComputerCraft.pocketComputerGUIID:
                {
                    return new ContainerPocketComputer( player, x == 0 ? EnumHand.MAIN_HAND : EnumHand.OFF_HAND );
                }
            }
            return null;
        }

        @Override
        public Object getClientGuiElement( int id, EntityPlayer player, World world, int x, int y, int z )
        {
            BlockPos pos = new BlockPos( x, y, z );
            switch (id)
            {
                case ComputerCraft.diskDriveGUIID:
                {
                    TileEntity tile = world.getTileEntity( pos );
                    if (tile != null && tile instanceof TileDiskDrive)
                    {
                        TileDiskDrive drive = (TileDiskDrive) tile;
                        return getDiskDriveGUI( player.inventory, drive );
                    }
                    break;
                }
                case ComputerCraft.computerGUIID:
                {
                    TileEntity tile = world.getTileEntity( pos );
                    if (tile != null && tile instanceof TileComputer)
                    {
                        TileComputer computer = (TileComputer) tile;
                        return getComputerGUI( computer );
                    }
                    break;
                }
                case ComputerCraft.printerGUIID:
                {
                    TileEntity tile = world.getTileEntity( pos );
                    if (tile != null && tile instanceof TilePrinter)
                    {
                        TilePrinter printer = (TilePrinter) tile;
                        return getPrinterGUI( player.inventory, printer );
                    }
                    break;
                }
                case ComputerCraft.turtleGUIID:
                {
                    TileEntity tile = world.getTileEntity( pos );
                    if (tile != null && tile instanceof TileTurtle)
                    {
                        TileTurtle turtle = (TileTurtle) tile;
                        return getTurtleGUI( player.inventory, turtle );
                    }
                    break;
                }
                case ComputerCraft.printoutGUIID:
                {
                    return getPrintoutGUI( player, x == 0 ? EnumHand.MAIN_HAND : EnumHand.OFF_HAND );
                }
                case ComputerCraft.pocketComputerGUIID:
                {
                    return getPocketComputerGUI( player, x == 0 ? EnumHand.MAIN_HAND : EnumHand.OFF_HAND );
                }
            }
            return null;
        }

        // Event handlers

        @SubscribeEvent
        public void onConnectionOpened( FMLNetworkEvent.ClientConnectedToServerEvent event )
        {
            ComputerCraft.clientComputerRegistry.reset();
        }

        @SubscribeEvent
        public void onConnectionClosed( FMLNetworkEvent.ClientDisconnectionFromServerEvent event )
        {
            ComputerCraft.clientComputerRegistry.reset();
        }

        @SubscribeEvent
        public void onClientTick( TickEvent.ClientTickEvent event )
        {
            if (event.phase == TickEvent.Phase.START)
            {
                ComputerCraft.clientComputerRegistry.update();
            }
        }

        @SubscribeEvent
        public void onServerTick( TickEvent.ServerTickEvent event )
        {
            if (event.phase == TickEvent.Phase.START)
            {
                MainThread.executePendingTasks();
                ComputerCraft.serverComputerRegistry.update();
            }
        }

        @SubscribeEvent
        public void onWorldLoad( WorldEvent.Load event )
        {
        }

        @SubscribeEvent
        public void onWorldUnload( WorldEvent.Unload event )
        {
        }

        @SubscribeEvent
        public void onConfigChanged( ConfigChangedEvent.OnConfigChangedEvent event) {
            if( event.getModID().equals( ComputerCraft.MOD_ID ) )
            {
                ComputerCraft.syncConfig();
            }
        }
    }
}
