package codechicken.translocator;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import codechicken.lib.math.MathHelper;
import codechicken.lib.raytracer.RayTracer;
import codechicken.lib.raytracer.IndexedCuboid6;
import codechicken.lib.vec.BlockCoord;
import codechicken.lib.vec.Vector3;
import codechicken.translocator.TileTranslocator.Attachment;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;

import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.IIcon;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.MovingObjectPosition.MovingObjectType;
import net.minecraft.util.Vec3;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.client.event.DrawBlockHighlightEvent;
import net.minecraftforge.fluids.IFluidHandler;

import static codechicken.lib.vec.Rotation.*;

public class BlockTranslocator extends Block
{
    private RayTracer rayTracer = new RayTracer();

    public BlockTranslocator() {
        super(Material.iron);
        setHardness(1.5F);
        setResistance(10.0F);
    }

    @Override
    public boolean hasTileEntity(int metadata) {
        return metadata < 2;
    }

    @Override
    public TileEntity createTileEntity(World world, int metadata) {
        switch (metadata) {
            case 0:
                return new TileItemTranslocator();
            case 1:
                return new TileLiquidTranslocator();
        }

        return null;
    }

    @Override
    public int getRenderType() {
        return -1;
    }

    @Override
    public boolean isOpaqueCube() {
        return false;
    }

    @Override
    public boolean renderAsNormalBlock() {
        return false;
    }

    public static boolean canExistOnSide(World world, int x, int y, int z, int side, int meta) {
        BlockCoord pos = new BlockCoord(x, y, z).offset(side);
        switch (meta) {
            case 0:
                return world.getTileEntity(pos.x, pos.y, pos.z) instanceof IInventory;
            case 1:
                return world.getTileEntity(pos.x, pos.y, pos.z) instanceof IFluidHandler;
        }
        return false;
    }

    @Override
    public void onNeighborBlockChange(World world, int x, int y, int z, Block block) {
        TileTranslocator ttrans = (TileTranslocator) world.getTileEntity(x, y, z);
        int meta = world.getBlockMetadata(x, y, z);

        for (int i = 0; i < 6; i++)
            if (ttrans.attachments[i] != null && !canExistOnSide(world, x, y, z, i, meta))
                if (ttrans.harvestPart(i, true))
                    break;
    }

    @Override
    public boolean removedByPlayer(World world, EntityPlayer player, int x, int y, int z, boolean willharvest) {
        MovingObjectPosition hit = RayTracer.retraceBlock(world, player, x, y, z);
        if (hit == null)
            return false;

        TileTranslocator ttrans = (TileTranslocator) world.getTileEntity(x, y, z);
        return ttrans.harvestPart(hit.subHit % 6, !player.capabilities.isCreativeMode);
    }

    @Override
    public int quantityDropped(int meta, int fortune, Random random) {
        return 0;
    }

    @Override
    public int damageDropped(int meta) {
        return meta;
    }

    @Override
    public ArrayList<ItemStack> getDrops(World world, int x, int y, int z, int md, int fortune) {
        ArrayList<ItemStack> ai = new ArrayList<ItemStack>();
        if (world.isRemote)
            return ai;

        TileTranslocator ttrans = (TileTranslocator) world.getTileEntity(x, y, z);
        if (ttrans != null)
            for (Attachment a : ttrans.attachments)
                if (a != null)
                    ai.addAll(a.getDrops());

        return ai;
    }

    @Override
    public IIcon getIcon(int par1, int par2) {
        return Blocks.obsidian.getIcon(par1, par2);
    }

    @Override
    public MovingObjectPosition collisionRayTrace(World world, int x, int y, int z, Vec3 start, Vec3 end) {
        return rayTracer.rayTraceCuboids(new Vector3(start), new Vector3(end), getParts(world, x, y, z), new BlockCoord(x, y, z), this);
    }

    public List<IndexedCuboid6> getParts(World world, int x, int y, int z) {
        TileTranslocator tile = (TileTranslocator) world.getTileEntity(x, y, z);
        if (tile == null)
            return null;

        List<IndexedCuboid6> cuboids = new LinkedList<IndexedCuboid6>();
        tile.addTraceableCuboids(cuboids);
        return cuboids;
    }

    @Override
    public void addCollisionBoxesToList(World world, int x, int y, int z, AxisAlignedBB ebb, List list, Entity entity) {
        List<IndexedCuboid6> cuboids = getParts(world, x, y, z);
        for (IndexedCuboid6 cb : cuboids) {
            AxisAlignedBB aabb = cb.toAABB();
            if (aabb.intersectsWith(ebb))
                list.add(aabb);
        }
    }

    @Override
    public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int side, float hitX, float hitY, float hitZ) {
        if (world.isRemote)
            return true;

        MovingObjectPosition hit = RayTracer.retraceBlock(world, player, x, y, z);
        TileTranslocator ttrans = (TileTranslocator) world.getTileEntity(x, y, z);

        if (hit != null) {
            if (hit.subHit < 6) {
                Vector3 vhit = new Vector3(hit.hitVec);
                vhit.add(-x - 0.5, -y - 0.5, -z - 0.5);
                vhit.apply(sideRotations[hit.subHit % 6].inverse());
                if (MathHelper.between(-2 / 16D, vhit.x, 2 / 16D) && MathHelper.between(-2 / 16D, vhit.z, 2 / 16D))
                    hit.subHit += 6;
            }

            return ttrans.attachments[hit.subHit % 6].activate(player, hit.subHit / 6);
        }

        return false;
    }

    @SideOnly(Side.CLIENT)
    @SubscribeEvent
    public void onBlockHighlight(DrawBlockHighlightEvent event) {
        if (event.target.typeOfHit == MovingObjectType.BLOCK && event.player.worldObj.getBlock(event.target.blockX, event.target.blockY, event.target.blockZ) == this)
            RayTracer.retraceBlock(event.player.worldObj, event.player, event.target.blockX, event.target.blockY, event.target.blockZ);
    }

    @Override
    public void getSubBlocks(Item par1, CreativeTabs par2CreativeTabs, List list) {
        list.add(new ItemStack(this, 1, 0));
        list.add(new ItemStack(this, 1, 1));
    }

    @Override
    public boolean canConnectRedstone(IBlockAccess world, int x, int y, int z, int side) {
        TileTranslocator ttrans = (TileTranslocator) world.getTileEntity(x, y, z);
        return ttrans.connectRedstone();
    }

    @Override
    public int isProvidingStrongPower(IBlockAccess world, int x, int y, int z, int side) {
        TileTranslocator ttrans = (TileTranslocator) world.getTileEntity(x, y, z);
        return ttrans.strongPowerLevel(side);
    }

    @Override
    @SideOnly(Side.CLIENT)
    public void registerBlockIcons(IIconRegister par1IconRegister) {
    }
}