package tectech.thing.metaTileEntity.hatch;

import static net.minecraft.util.StatCollector.translateToLocal;
import static net.minecraft.util.StatCollector.translateToLocalFormatted;
import static org.lwjgl.opengl.GL11.GL_BLEND;
import static org.lwjgl.opengl.GL11.GL_ONE_MINUS_SRC_ALPHA;
import static org.lwjgl.opengl.GL11.GL_SRC_ALPHA;
import static org.lwjgl.opengl.GL11.glBlendFunc;
import static org.lwjgl.opengl.GL11.glColor4f;
import static org.lwjgl.opengl.GL11.glDisable;
import static org.lwjgl.opengl.GL11.glEnable;

import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumChatFormatting;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.fluids.FluidStack;

import com.gtnewhorizons.modularui.api.GlStateManager;
import com.gtnewhorizons.modularui.api.drawable.UITexture;
import com.gtnewhorizons.modularui.api.math.Pos2d;
import com.gtnewhorizons.modularui.api.screen.ModularWindow;
import com.gtnewhorizons.modularui.api.screen.UIBuildContext;
import com.gtnewhorizons.modularui.common.widget.ButtonWidget;
import com.gtnewhorizons.modularui.common.widget.DrawableWidget;
import com.gtnewhorizons.modularui.common.widget.FakeSyncWidget;
import com.gtnewhorizons.modularui.common.widget.TextWidget;

import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import gregtech.api.enums.Textures;
import gregtech.api.gui.modularui.GTUIInfos;
import gregtech.api.gui.modularui.GTUITextures;
import gregtech.api.interfaces.ITexture;
import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
import gregtech.api.interfaces.modularui.IAddGregtechLogo;
import gregtech.api.interfaces.modularui.IAddUIWidgets;
import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
import gregtech.api.metatileentity.implementations.MTEHatch;
import gregtech.api.objects.GTRenderedTexture;
import gregtech.mixin.interfaces.accessors.EntityPlayerMPAccessor;
import tectech.TecTech;
import tectech.thing.gui.TecTechUITextures;
import tectech.util.CommonValues;
import tectech.util.TTUtility;

/**
 * Created by danie_000 on 15.12.2016.
 */
public class MTEHatchUncertainty extends MTEHatch implements IAddGregtechLogo, IAddUIWidgets {

    private static Textures.BlockIcons.CustomIcon ScreenON;
    private static Textures.BlockIcons.CustomIcon ScreenOFF;
    public short[] matrix = new short[] { 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500,
        500 };
    public byte selection = -1, mode = 0, status = -128; // all 8 bits set

    private String clientLocale = "en_US";

    public MTEHatchUncertainty(int aID, String aName, String aNameRegional, int aTier) {
        super(aID, aName, aNameRegional, aTier, 0, "");
        TTUtility.setTier(aTier, this);
        regenerate();
    }

    public MTEHatchUncertainty(String aName, int aTier, String[] aDescription, ITexture[][][] aTextures) {
        super(aName, aTier, 0, aDescription, aTextures);
        regenerate();
    }

    @Override
    @SideOnly(Side.CLIENT)
    public void registerIcons(IIconRegister aBlockIconRegister) {
        super.registerIcons(aBlockIconRegister);
        ScreenOFF = new Textures.BlockIcons.CustomIcon("iconsets/UC");
        ScreenON = new Textures.BlockIcons.CustomIcon("iconsets/UC_ACTIVE");
    }

    @Override
    public ITexture[] getTexturesActive(ITexture aBaseTexture) {
        return new ITexture[] { aBaseTexture, new GTRenderedTexture(ScreenON) };
    }

    @Override
    public ITexture[] getTexturesInactive(ITexture aBaseTexture) {
        return new ITexture[] { aBaseTexture, new GTRenderedTexture(ScreenOFF) };
    }

    @Override
    public void onPostTick(IGregTechTileEntity aBaseMetaTileEntity, long aTick) {
        if (aBaseMetaTileEntity.isServerSide() && (aTick & 15) == 0) {
            if (mode == 0) {
                aBaseMetaTileEntity.setActive(false);
                status = -128;
            } else {
                aBaseMetaTileEntity.setActive(true);
                shift();
                compute();
            }
        }
    }

    @Override
    public IMetaTileEntity newMetaEntity(IGregTechTileEntity iGregTechTileEntity) {
        return new MTEHatchUncertainty(mName, mTier, mDescriptionArray, mTextures);
    }

    @Override
    public boolean isGivingInformation() {
        return true;
    }

    @Override
    public String[] getInfoData() {
        return new String[] {
            translateToLocalFormatted("tt.keyword.Status", clientLocale) + ": " + EnumChatFormatting.GOLD + status };
    }

    @Override
    public boolean isSimpleMachine() {
        return true;
    }

    @Override
    public boolean allowPullStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
        ItemStack aStack) {
        return false;
    }

    @Override
    public boolean allowPutStack(IGregTechTileEntity aBaseMetaTileEntity, int aIndex, ForgeDirection side,
        ItemStack aStack) {
        return false;
    }

    @Override
    public void saveNBTData(NBTTagCompound aNBT) {
        super.saveNBTData(aNBT);
        aNBT.setByte("mSel", selection);
        aNBT.setByte("mMode", mode);
        aNBT.setByte("mStatus", status);
        NBTTagCompound mat = new NBTTagCompound();
        for (int i = 0; i < 16; i++) {
            mat.setShort(Integer.toString(i), matrix[i]);
        }
        aNBT.setTag("mMat", mat);
    }

    @Override
    public void loadNBTData(NBTTagCompound aNBT) {
        super.loadNBTData(aNBT);
        selection = aNBT.getByte("mSel");
        mode = aNBT.getByte("mMode");
        status = aNBT.getByte("mStatus");
        NBTTagCompound mat = aNBT.getCompoundTag("mMat");
        for (int i = 0; i < 16; i++) {
            matrix[i] = mat.getShort(Integer.toString(i));
        }
    }

    @Override
    public boolean isFacingValid(ForgeDirection facing) {
        return true;
    }

    @Override
    public boolean isAccessAllowed(EntityPlayer aPlayer) {
        return true;
    }

    @Override
    public boolean isValidSlot(int aIndex) {
        return false;
    }

    @Override
    public boolean isLiquidInput(ForgeDirection side) {
        return false;
    }

    @Override
    public boolean isFluidInputAllowed(FluidStack aFluid) {
        return false;
    }

    @Override
    public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) {
        if (aBaseMetaTileEntity.isClientSide()) {
            return true;
        }
        if (aPlayer instanceof EntityPlayerMPAccessor) {
            clientLocale = ((EntityPlayerMPAccessor) aPlayer).gt5u$getTranslator();
        }
        GTUIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer);
        return true;
    }

    @Override
    public String[] getDescription() {
        return new String[] { CommonValues.TEC_MARK_EM, translateToLocal("gt.blockmachines.hatch.certain.desc.0"), // Feeling
                                                                                                                   // certain,
                                                                                                                   // or
                                                                                                                   // not?
            EnumChatFormatting.AQUA.toString() + EnumChatFormatting.BOLD
                + translateToLocal("gt.blockmachines.hatch.certain.desc.1") // Schrödinger equation in a box
        };
    }

    private boolean balanceCheck(int sideLenY, short... masses) {
        float inequality = 0;
        for (int i = 0; i < masses.length >> 1; i++) {
            inequality += Math.abs(masses[i] - masses[masses.length - i - 1]);
        }
        return inequality < masses.length << 7;
    }

    public void regenerate() {
        for (int i = 0; i < matrix.length; i++) {
            matrix[i] = (short) TecTech.RANDOM.nextInt(1000);
        }
    }

    public byte compute() {
        int result = 0;
        switch (mode) {
            case 1: // ooo oxo ooo
                result = balanceCheck(4, matrix) ? 0 : 1;
                break;
            case 2: // ooo xox ooo
                result += balanceCheck(
                    4,
                    matrix[0],
                    matrix[4],
                    matrix[1],
                    matrix[5],
                    matrix[2],
                    matrix[6],
                    matrix[3],
                    matrix[7]) ? 0 : 1;
                result += balanceCheck(
                    4,
                    matrix[8],
                    matrix[12],
                    matrix[9],
                    matrix[13],
                    matrix[10],
                    matrix[14],
                    matrix[11],
                    matrix[15]) ? 0 : 2;
                break;
            case 3: // oxo xox oxo
                result += balanceCheck(
                    2,
                    matrix[0],
                    matrix[4],
                    matrix[8],
                    matrix[12],
                    matrix[1],
                    matrix[5],
                    matrix[9],
                    matrix[13]) ? 0 : 1;
                result += balanceCheck(
                    4,
                    matrix[0],
                    matrix[4],
                    matrix[1],
                    matrix[5],
                    matrix[2],
                    matrix[6],
                    matrix[3],
                    matrix[7]) ? 0 : 2;
                result += balanceCheck(
                    4,
                    matrix[8],
                    matrix[12],
                    matrix[9],
                    matrix[13],
                    matrix[10],
                    matrix[14],
                    matrix[11],
                    matrix[15]) ? 0 : 4;
                result += balanceCheck(
                    2,
                    matrix[2],
                    matrix[6],
                    matrix[10],
                    matrix[14],
                    matrix[3],
                    matrix[7],
                    matrix[11],
                    matrix[15]) ? 0 : 8;
                break;
            case 4: // xox ooo xox
                result += balanceCheck(2, matrix[0], matrix[4], matrix[1], matrix[5]) ? 0 : 1;
                result += balanceCheck(2, matrix[8], matrix[12], matrix[9], matrix[13]) ? 0 : 2;
                result += balanceCheck(2, matrix[2], matrix[6], matrix[3], matrix[7]) ? 0 : 4;
                result += balanceCheck(2, matrix[10], matrix[14], matrix[11], matrix[15]) ? 0 : 8;
                break;
            case 5: // xox oxo xox
                result += balanceCheck(2, matrix[0], matrix[4], matrix[1], matrix[5]) ? 0 : 1;
                result += balanceCheck(2, matrix[8], matrix[12], matrix[9], matrix[13]) ? 0 : 2;
                result += balanceCheck(4, matrix) ? 0 : 4;
                result += balanceCheck(2, matrix[2], matrix[6], matrix[3], matrix[7]) ? 0 : 8;
                result += balanceCheck(2, matrix[10], matrix[14], matrix[11], matrix[15]) ? 0 : 16;
                break;
        }
        return status = (byte) result;
    }

    private void shift() {
        int i = TecTech.RANDOM.nextInt(16), j = TecTech.RANDOM.nextInt(128);
        matrix[i] += ((matrix[i] & 1) == 0 ? 2 : -2) * j >> 5;
        matrix[i] += j == 0 ? 1 : 0;
        if (matrix[i] < 0) {
            matrix[i] = 0;
        } else if (matrix[i] > 1000) {
            matrix[i] = 999;
        }
    }

    public byte update(int newMode) {
        if (newMode == mode) {
            return status;
        }
        if (newMode < 0 || newMode > 5) {
            newMode = 0;
        }
        mode = (byte) newMode;
        regenerate();
        compute();
        return status;
    }

    @Override
    public void addGregTechLogo(ModularWindow.Builder builder) {
        builder.widget(
            new DrawableWidget().setDrawable(TecTechUITextures.PICTURE_TECTECH_LOGO_DARK)
                .setSize(18, 18)
                .setPos(112, 55));
    }

    @Override
    public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) {
        final boolean isAdvanced = mTier > 7;

        builder.widget(
            new DrawableWidget().setDrawable(TecTechUITextures.BACKGROUND_SCREEN_BLUE)
                .setPos(43, 4)
                .setSize(90, 72))
            .widget(
                new DrawableWidget().setDrawable(TecTechUITextures.PICTURE_UNCERTAINTY_MONITOR)
                    .setPos(46, 27)
                    .setSize(46, 46));

        int[] xPositions = new int[] { 7, 25, 133, 151 };
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                final int index = i * 4 + j;
                builder.widget(new ButtonWidget().setOnClick((clickData, widget) -> {
                    TecTech.proxy.playSound(getBaseMetaTileEntity(), "fx_click");
                    if (selection == -1) {
                        selection = (byte) index;
                    } else {
                        short temp = matrix[selection];
                        matrix[selection] = matrix[index];
                        matrix[index] = temp;
                        selection = -1;
                    }
                    compute();
                })
                    .setPlayClickSound(false)
                    .setBackground(GTUITextures.BUTTON_STANDARD, TecTechUITextures.OVERLAY_BUTTON_UNCERTAINTY[index])
                    .setPos(xPositions[i], 4 + j * 18)
                    .setSize(18, 18))
                    .widget(new FakeSyncWidget.ShortSyncer(() -> matrix[index], val -> matrix[index] = val));
            }
        }
        builder.widget(new FakeSyncWidget.ByteSyncer(() -> selection, val -> selection = val))
            .widget(new FakeSyncWidget.ByteSyncer(() -> mode, val -> mode = val))
            .widget(new FakeSyncWidget.ByteSyncer(() -> status, val -> status = val));

        builder.widget(
            new TextWidget().setStringSupplier(() -> "Status: " + (status == 0 ? "OK" : "NG"))
                .setDefaultColor(COLOR_TEXT_WHITE.get())
                .setPos(46, 7));

        for (int i = 0; i < 9; i++) {
            final int index = i;
            builder.widget(new DrawableWidget().setDrawable(() -> {
                UITexture valid = TecTechUITextures.PICTURE_UNCERTAINTY_VALID[index];
                UITexture invalid = TecTechUITextures.PICTURE_UNCERTAINTY_INVALID[index];
                switch (mode) {
                    case 1: // ooo oxo ooo
                        if (index == 4) return status == 0 ? valid : invalid;
                        break;
                    case 2: // ooo xox ooo
                        if (index == 3) return (status & 1) == 0 ? valid : invalid;
                        if (index == 5) return (status & 2) == 0 ? valid : invalid;
                        break;
                    case 3: // oxo xox oxo
                        if (index == 1) return (status & 1) == 0 ? valid : invalid;
                        if (index == 3) return (status & 2) == 0 ? valid : invalid;
                        if (index == 5) return (status & 4) == 0 ? valid : invalid;
                        if (index == 7) return (status & 8) == 0 ? valid : invalid;
                        break;
                    case 4: // xox ooo xox
                        if (index == 0) return (status & 1) == 0 ? valid : invalid;
                        if (index == 2) return (status & 2) == 0 ? valid : invalid;
                        if (index == 6) return (status & 4) == 0 ? valid : invalid;
                        if (index == 8) return (status & 8) == 0 ? valid : invalid;
                        break;
                    case 5: // xox oxo xox
                        if (index == 0) return (status & 1) == 0 ? valid : invalid;
                        if (index == 2) return (status & 2) == 0 ? valid : invalid;
                        if (index == 4) return (status & 4) == 0 ? valid : invalid;
                        if (index == 6) return (status & 8) == 0 ? valid : invalid;
                        if (index == 8) return (status & 16) == 0 ? valid : invalid;
                        break;
                }
                return null;
            })
                .setPos(55 + (index % 3) * 12, 36 + (index / 3) * 12)
                .setSize(4, 4));
        }

        for (int i = 0; i < 16; i++) {
            final int index = i;
            builder.widget(new DrawableWidget() {

                @Override
                public void draw(float partialTicks) {
                    if (isAdvanced) {
                        glEnable(GL_BLEND);
                        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
                        glColor4f(1f, 1f, 1f, (float) matrix[index] / 1000f);

                        // super.draw but without disabling blend
                        GlStateManager.pushMatrix();
                        getDrawable().draw(Pos2d.ZERO, getSize(), partialTicks);
                        GlStateManager.popMatrix();

                        glDisable(GL_BLEND);
                        glColor4f(1f, 1f, 1f, 1f);
                    } else {
                        if (TecTech.RANDOM.nextInt(1000) < matrix[index]) {
                            super.draw(partialTicks);
                        }
                    }
                }
            }.setDrawable(TecTechUITextures.PICTURE_UNCERTAINTY_INDICATOR)
                .setPos(47 + (i / 4) * 12, 28 + (i % 4) * 12)
                .setSize(8, 8))
                .widget(
                    new DrawableWidget()
                        .setDrawable(() -> selection == index ? TecTechUITextures.PICTURE_UNCERTAINTY_SELECTED : null)
                        .setPos(46 + (i / 4) * 12, 27 + (i % 4) * 12)
                        .setSize(10, 10));
        }
    }
}