package jesse.keeblarcraft.gui.ShopKeeperGUI;

import jesse.keeblarcraft.Keeblarcraft;
import jesse.keeblarcraft.Utils.CommonStructures.Pair;
import jesse.keeblarcraft.gui.Generics.ClickableLayer;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder;
import net.minecraft.item.ItemStack;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;

// All the custom content that a store item will have. Handled by @see MerchandiseScroller.java
public class DrawableStoreItem extends ClickableLayer {
    // Names are hard. ITEM_ICON is the actual SLOT icon itself; not the in-game item!!!!
    private static final Identifier ITEM_ICON = new Identifier(Keeblarcraft.MOD_ID, "textures/gui/shopkeeper_item_slot.png");
    private double itemCost = 0.0;
    private String itemName = "";
    private Integer amountSelected = 0;
    ItemStack itemTextureId;

    // Increment counter
    private static class UpSelectionArrow {
        // Please use this parent class (ClickableLayer) debug print to find these coordinates if they move (func: PrintMouseClick())
        // X Y Pair
        public static Pair<Double, Double> top = new Pair<>(64.0, 12.0);
        public static Pair<Double, Double> bottomLeft = new Pair<>(47.0, 21.0);
        public static Pair<Double, Double> bottomRight = new Pair<>(82.0, 21.0);
    }

    // Decrement counter
    private static class DownSelectionArrow {
        // Please use this parent class (ClickableLayer) debug print to find these coordinates if they move (func: PrintMouseClick())
        // X Y Pair
        public static Pair<Double, Double> topLeft = new Pair<>(47.0, 39.0);
        public static Pair<Double, Double> topRight = new Pair<>(82.0, 39.0);
        public static Pair<Double, Double> bottom = new Pair<>(64.0, 48.0);
    }

    private static class TextPosition {
        public static Pair<Double, Double> UNIT_PRICE = new Pair<>(44.0, 1.0); // Area on widget that displays price per unit
        public static Pair<Double, Double> FINAL_PRICE = new Pair<>(0.0, 0.0); // TODO: TBD
        public static Pair<Double, Double> BUY_AMOUNT = new Pair<>(48.0, 26.0); // TODO: TBD
    }

    private enum CLICKED_SELECTION_ARROW {
        NONE,
        TOP,
        DOWN
    }

    // Helper function to calculate area of a triangle
    private static double CalculateTriangleArea(int x1, int y1, int x2, int y2, int x3, int y3)
    {
        return Math.abs((x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) / 2.0);
    }

    // Algorithm explained:
    // Let's call the points of the triangle ABC, we first calculate the area of this triangle with the following:
    //      Eq: AREA = [x1(y2-y3) + x2(y3 - y1) + x3(y1 - y2)] / 2
    //
    //  Now let's make 3 more areas; but instead of using ABC we will introduce point P and replace it with each letter
    //  and calculate that area. That means we have the entire triangle area (ABC), then area 1 (ABP), then
    //  area 2 (APC), then finally area 3 (PBC). The summation of these 3 areas should be equal to the global area we
    //  originally calculated
    //
     // Note: Pair<Double, Double> is a pair with the first being an X coordinate and second being Y (Pair<X,Y>)
    private Boolean TriangleBoundsCheck(Pair<Double, Double> POINT_A,
                                        Pair<Double, Double> POINT_B,
                                        Pair<Double, Double> POINT_C,
                                        Pair<Double, Double> point) {
        Boolean inBounds = false;
        double totalArea = CalculateTriangleArea((int)Math.floor(POINT_A.GetKey()), (int)Math.floor(POINT_A.GetValue()),
                                                (int)Math.floor(POINT_B.GetKey()), (int)Math.floor(POINT_B.GetValue()),
                                                (int)Math.floor(POINT_C.GetKey()), (int)Math.floor(POINT_C.GetValue()));

        double areaOne = CalculateTriangleArea((int)Math.floor(POINT_A.GetKey()), (int)Math.floor(POINT_A.GetValue()),
                                                (int)Math.floor(POINT_B.GetKey()), (int)Math.floor(POINT_B.GetValue()),
                                                (int)Math.floor(point.GetKey()), (int)Math.floor(point.GetValue()));

        double areaTwo = CalculateTriangleArea((int)Math.floor(POINT_A.GetKey()), (int)Math.floor(POINT_A.GetValue()),
                                                (int)Math.floor(point.GetKey()), (int)Math.floor(point.GetValue()),
                                                (int)Math.floor(POINT_C.GetKey()), (int)Math.floor(POINT_C.GetValue()));

        double areaThree = CalculateTriangleArea((int)Math.floor(point.GetKey()), (int)Math.floor(point.GetValue()),
                                                (int)Math.floor(POINT_B.GetKey()), (int)Math.floor(POINT_B.GetValue()),
                                                (int)Math.floor(POINT_C.GetKey()), (int)Math.floor(POINT_C.GetValue()));

//        System.out.println("Area one: " + areaOne);
//        System.out.println("Area two: " + areaTwo);
//        System.out.println("Area three: " + areaThree);
//        System.out.println("MX MY: " + point.GetKey() + " " + point.GetValue());
//        double addedArea = areaOne + areaTwo + areaThree;
//        System.out.println("Total area: " + totalArea);
//        System.out.println("Added areas: " + addedArea);
//        System.out.println("Triangle selected? " + (totalArea == (areaOne + areaTwo + areaThree)));

        return totalArea == (areaOne + areaTwo + areaThree);
    }

    // This function solves two things at once; if a selection point is within the bounds of a selection arrow
    // and determines which arrow it is if either.
    private CLICKED_SELECTION_ARROW InArrowBounds(double mX, double mY) {
        CLICKED_SELECTION_ARROW inBounds = CLICKED_SELECTION_ARROW.NONE;
        boolean isTopArrow = false;
        boolean isBottomArrow = false;

        // Check if click is the top triangle then bottom triangle (if it's not bottom triangle obvs)
        isTopArrow = TriangleBoundsCheck(UpSelectionArrow.bottomLeft, UpSelectionArrow.top, UpSelectionArrow.bottomRight, new Pair<>(mX, mY));

        if (!isTopArrow) {
            isBottomArrow = TriangleBoundsCheck(DownSelectionArrow.topLeft, DownSelectionArrow.topRight, DownSelectionArrow.bottom, new Pair<>(mX, mY));
        }

        if (isTopArrow) {
            inBounds = CLICKED_SELECTION_ARROW.TOP;
        } else if (isBottomArrow) {
            inBounds = CLICKED_SELECTION_ARROW.DOWN;
        }

        return inBounds;
    }

    public DrawableStoreItem(String itemName, Double itemCost, ItemStack itemTextureId) {
        this(itemName, itemCost, Text.of(""), 85, 50);
        this.itemTextureId = itemTextureId;
    }

    public DrawableStoreItem(String itemName, Double itemCost, Text layerName, int width, int height) {
        super(ITEM_ICON, layerName, width, height, 0, 0);
        this.itemCost = itemCost;
        this.itemName = itemName;
    }


    public Double GetPrice() {
        return this.itemCost * this.amountSelected;
    }

    public String GetName() {
        return this.itemName;
    }

    public void Add() {
        if (this.amountSelected < 99) {
            ++this.amountSelected;
        }
    }

    public void Subtract() {
        if (this.amountSelected > 0) {
            --this.amountSelected;
        }
    }

    @Override
    public boolean mouseClicked(double mX, double mY, int button) {
//        PrintMouseClick(mX, mY);
        // Normalize the mouse selection
        Pair<Double, Double> normalizedClick = NormalizeCoordinates(mX, mY);
        CLICKED_SELECTION_ARROW arrowPressed = InArrowBounds(normalizedClick.GetKey(), normalizedClick.GetValue());

        if (arrowPressed == CLICKED_SELECTION_ARROW.TOP) {
            Add();
        }
        else if (arrowPressed == CLICKED_SELECTION_ARROW.DOWN) {
            Subtract();
        }

        return true;
    }

    @Override
    public void renderButton(DrawContext context, int mouseX, int mouseY, float delta) {
        super.renderButton(context, mouseX, mouseY, delta);

        // Now we want to draw the item icon in the icon slot
        if (this.itemTextureId != null) {
            // I have no idea if it really ever CAN be null in practicality
            if (MinecraftClient.getInstance() != null) {
                int unitPriceXPos = this.getX() + TextPosition.UNIT_PRICE.GetKey().intValue();
                int unitPriceYPos = this.getY() + TextPosition.UNIT_PRICE.GetValue().intValue();
                context.drawText(MinecraftClient.getInstance().textRenderer, Text.of(Double.toString(this.itemCost)), unitPriceXPos, unitPriceYPos, 4, false);

                int buyAmountXPos = this.getX() + TextPosition.BUY_AMOUNT.GetKey().intValue();
                int buyAmountYPos = this.getY() + TextPosition.BUY_AMOUNT.GetValue().intValue();
                context.drawText(MinecraftClient.getInstance().textRenderer, Text.of(this.amountSelected.toString()), buyAmountXPos, buyAmountYPos, 100, false);
            }
            context.drawItem(this.itemTextureId, this.getX() + 13, this.getY() + 22); // TODO: Need to correct offest to draw
        } else {
            // TODO: Add default icon to draw if no texture is set or could be found for item
        }
    }

    @Override
    protected void appendClickableNarrations(NarrationMessageBuilder builder) {}
}
