public interface GuiRenderer {

    ReadablePoint getOpenGlOffset();

    MatrixStack getMatrixStack();

    ReadableDimension getSize();

    void setDrawingArea(int x, int y, int width, int height);

    void bindTexture(Identifier location);

    void bindTexture(int glId);

    void drawTexturedRect(int x, int y, int u, int v, int width, int height);

    void drawTexturedRect(int x, int y, int u, int v, int width, int height, int uWidth, int vHeight, int textureWidth, int textureHeight);

    void drawRect(int x, int y, int width, int height, int color);

    void drawRect(int x, int y, int width, int height, ReadableColor color);

    void drawRect(int x, int y, int width, int height, int topLeftColor, int topRightColor, int bottomLeftColor, int bottomRightColor);

    void drawRect(int x, int y, int width, int height, ReadableColor topLeftColor, ReadableColor topRightColor, ReadableColor bottomLeftColor, ReadableColor bottomRightColor);

    int drawString(int x, int y, int color, String text);

    int drawString(int x, int y, ReadableColor color, String text);

    int drawCenteredString(int x, int y, int color, String text);

    int drawCenteredString(int x, int y, ReadableColor color, String text);

    int drawString(int x, int y, int color, String text, boolean shadow);

    int drawString(int x, int y, ReadableColor color, String text, boolean shadow);

    int drawCenteredString(int x, int y, int color, String text, boolean shadow);

    int drawCenteredString(int x, int y, ReadableColor color, String text, boolean shadow);

    /**
     * Inverts all colors on the screen.
     * @param right Right border of the inverted rectangle
     * @param bottom Bottom border of the inverted rectangle
     * @param left Left border of the inverted rectangle
     * @param top Top border of the inverted rectangle
     */
    void invertColors(int right, int bottom, int left, int top);

} public class OffsetGuiRenderer implements GuiRenderer {

    @NonNull
    private final GuiRenderer renderer;

    @NonNull
    private final ReadablePoint position;

    @NonNull
    private final ReadableDimension size;

    private final boolean strict;

    /**
     * Creates a new strict offset jgui renderer with the same settings as the supplied one.
     * @see #OffsetGuiRenderer(GuiRenderer, ReadablePoint, ReadableDimension, boolean)
     * @param renderer The renderer to copy from
     */
    public OffsetGuiRenderer(OffsetGuiRenderer renderer) {
        this(renderer.renderer, renderer.position, renderer.size, true);
    }

    /**
     * Create a new offset GUI renderer. All calls to this renderer are forwarded to the parent
     * renderer with coordinates offset by the specified position.
     * This also ensures that drawing happens within the specified bounds.
     * @param renderer The parent renderer
     * @param position The position of this renderer within the parent (used as is, not copied)
     * @param size The size of the drawable area (used as is, not copied)
     */
    public OffsetGuiRenderer(GuiRenderer renderer, ReadablePoint position, ReadableDimension size) {
        this(renderer, position, size, true);
    }


    /**
     * Create a new offset GUI renderer. All calls to this renderer are forwarded to the parent
     * renderer with coordinates offset by the specified position.
     * If strict is {@code true}, this also ensures that drawing happens within the specified bounds.
     * @param renderer The parent renderer
     * @param position The position of this renderer within the parent (used as is, not copied)
     * @param size The size of the drawable area (used as is, not copied)
     * @param strict Whether drawing should be forced to be within the drawable area
     */
    public OffsetGuiRenderer(GuiRenderer renderer, ReadablePoint position, ReadableDimension size, boolean strict) {
        this.renderer = renderer;
        this.position = position;
        this.size = size;
        this.strict = strict;
    }

    @Override
    public ReadablePoint getOpenGlOffset() {
        ReadablePoint parentOffset = renderer.getOpenGlOffset();
        return new Point(parentOffset.getX() + position.getX(), parentOffset.getY() + position.getY());
    }

    @Override
    public MatrixStack getMatrixStack() {
        return renderer.getMatrixStack();
    }

    @Override
    public ReadableDimension getSize() {
        return size;
    }

    @Override
    public void setDrawingArea(int x, int y, int width, int height) {
        if (!strict) {
            renderer.setDrawingArea(x + position.getX(), y + position.getY(), width, height);
            return;
        }
        int x2 = x + width;
        int y2 = y + height;
        // Convert and clamp top and left border
        x = Math.max(0, x + position.getX());
        y = Math.max(0, y + position.getY());
        // Clamp and convert bottom and right border
        x2 = Math.min(x2, size.getWidth()) + position.getX();
        y2 = Math.min(y2, size.getHeight()) + position.getY();
        // Make sure bottom and top / right and left aren't flipped
        x2 = Math.max(x2, x);
        y2 = Math.max(y2, y);
        // Pass to parent
        renderer.setDrawingArea(x, y, x2 - x, y2 - y);
    }

    public void startUsing() {
        pushScissorState();
        setDrawingArea(0, 0, size.getWidth(), size.getHeight());
    }

    public void stopUsing() {
        popScissorState();
    }

    @Override
    public void bindTexture(Identifier location) {
        renderer.bindTexture(location);
    }

    @Override
    public void bindTexture(int glId) {
        renderer.bindTexture(glId);
    }

    @Override
    public void drawTexturedRect(int x, int y, int u, int v, int width, int height) {
        renderer.drawTexturedRect(x + position.getX(), y + position.getY(), u, v, width, height);
    }

    @Override
    public void drawTexturedRect(int x, int y, int u, int v, int width, int height, int uWidth, int vHeight, int textureWidth, int textureHeight) {
        renderer.drawTexturedRect(x + position.getX(), y + position.getY(), u, v, width, height, uWidth, vHeight, textureWidth, textureHeight);
    }

    @Override
    public void drawRect(int x, int y, int width, int height, int color) {
        renderer.drawRect(x + position.getX(), y + position.getY(), width, height, color);
    }

    @Override
    public void drawRect(int x, int y, int width, int height, ReadableColor color) {
        renderer.drawRect(x + position.getX(), y + position.getY(), width, height, color);
    }

    @Override
    public void drawRect(int x, int y, int width, int height, int topLeftColor, int topRightColor, int bottomLeftColor, int bottomRightColor) {
        renderer.drawRect(x + position.getX(), y + position.getY(), width, height, topLeftColor, topRightColor, bottomLeftColor, bottomRightColor);
    }

    @Override
    public void drawRect(int x, int y, int width, int height, ReadableColor topLeftColor, ReadableColor topRightColor, ReadableColor bottomLeftColor, ReadableColor bottomRightColor) {
        renderer.drawRect(x + position.getX(), y + position.getY(), width, height, topLeftColor, topRightColor, bottomLeftColor, bottomRightColor);
    }

    @Override
    public int drawString(int x, int y, int color, String text) {
        return renderer.drawString(x + position.getX(), y + position.getY(), color, text) - position.getX();
    }

    @Override
    public int drawString(int x, int y, ReadableColor color, String text) {
        return renderer.drawString(x + position.getX(), y + position.getY(), color, text) - position.getX();
    }

    @Override
    public int drawCenteredString(int x, int y, int color, String text) {
        return renderer.drawCenteredString(x + position.getX(), y + position.getY(), color, text) - position.getX();
    }

    @Override
    public int drawCenteredString(int x, int y, ReadableColor color, String text) {
        return renderer.drawCenteredString(x + position.getX(), y + position.getY(), color, text) - position.getX();
    }

    @Override
    public int drawString(int x, int y, int color, String text, boolean shadow) {
        return renderer.drawString(x + position.getX(), y + position.getY(), color, text, shadow) - position.getX();
    }

    @Override
    public int drawString(int x, int y, ReadableColor color, String text, boolean shadow) {
        return renderer.drawString(x + position.getX(), y + position.getY(), color, text, shadow) - position.getX();
    }

    @Override
    public int drawCenteredString(int x, int y, int color, String text, boolean shadow) {
        return renderer.drawCenteredString(x + position.getX(), y + position.getY(), color, text, shadow) - position.getX();
    }

    @Override
    public int drawCenteredString(int x, int y, ReadableColor color, String text, boolean shadow) {
        return renderer.drawCenteredString(x + position.getX(), y + position.getY(), color, text, shadow) - position.getX();
    }

    @Override
    public void invertColors(int right, int bottom, int left, int top) {
        int x = position.getX();
        int y = position.getY();
        renderer.invertColors(right + x, bottom + y, left + x, top + y);
    }
} public class RenderInfo {
    public final float partialTick;
    public final int mouseX;
    public final int mouseY;
    public final int layer;

    public RenderInfo(float partialTick, int mouseX, int mouseY, int layer) {
        this.partialTick = partialTick;
        this.mouseX = mouseX;
        this.mouseY = mouseY;
        this.layer = layer;
    }

    public RenderInfo offsetMouse(int minusX, int minusY) {
        return new RenderInfo(partialTick, mouseX - minusX, mouseY - minusY, layer);
    }

    public RenderInfo layer(int layer) {
        return this.layer == layer ? this : new RenderInfo(partialTick, mouseX, mouseY, layer);
    }

    public void addTo(CrashReport crashReport) {
        CrashReportSection category = crashReport.addElement("Render info details");
        MCVer.addDetail(category, "Partial Tick", () -> "" + partialTick);
        MCVer.addDetail(category, "Mouse X", () -> "" + mouseX);
        MCVer.addDetail(category, "Mouse Y", () -> "" + mouseY);
        MCVer.addDetail(category, "Layer", () -> "" + layer);
    }

    public float getPartialTick() {
        return this.partialTick;
    }

    public int getMouseX() {
        return this.mouseX;
    }

    public int getMouseY() {
        return this.mouseY;
    }

    public int getLayer() {
        return this.layer;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        RenderInfo that = (RenderInfo) o;
        return Float.compare(that.partialTick, partialTick) == 0 &&
                mouseX == that.mouseX &&
                mouseY == that.mouseY &&
                layer == that.layer;
    }

    @Override
    public int hashCode() {
        return Objects.hash(partialTick, mouseX, mouseY, layer);
    }

    @Override
    public String toString() {
        return "RenderInfo{" +
                "partialTick=" + partialTick +
                ", mouseX=" + mouseX +
                ", mouseY=" + mouseY +
                ", layer=" + layer +
                '}';
    }
} public interface GuiContainer<T extends GuiContainer<T>> extends ComposedGuiElement<T> {

    T setLayout(Layout layout);
    Layout getLayout();

    void convertFor(GuiElement element, Point point);

    /**
     * Converts the global coordinates of the point to ones relative to the element.
     * @param element The element, must be part of this container
     * @param point The point
     * @param relativeLayer Layer at which the point is relative to this element,
     *                      positive values are above this element
     */
    void convertFor(GuiElement element, Point point, int relativeLayer);

    Map<GuiElement, LayoutData> getElements();
    T addElements(LayoutData layoutData, GuiElement... elements);
    T removeElement(GuiElement element);
    T sortElements();
    T sortElements(Comparator<GuiElement> comparator);

    ReadableColor getBackgroundColor();
    T setBackgroundColor(ReadableColor backgroundColor);
} public abstract class AbstractComposedGuiElement<T extends AbstractComposedGuiElement<T>>
        extends AbstractGuiElement<T> implements ComposedGuiElement<T> {
    public AbstractComposedGuiElement() {
    }

    public AbstractComposedGuiElement(GuiContainer container) {
        super(container);
    }

    @Override
    public int getMaxLayer() {
        return getLayer() + getChildren()
                .stream()
                .mapToInt(e -> e instanceof ComposedGuiElement ? ((ComposedGuiElement<?>) e).getMaxLayer() : e.getLayer())
                .max()
                .orElse(0);
    }

    @Override
    public <C, R> R forEach(
            int layer,
            Class<C> ofType,
            BiFunction<ComposedGuiElement<?>, Integer, R> recurse,
            Function<C, R> function
    ) {
        if (ofType.isInstance(this) && getLayer() == layer) {
            R result = function.apply(ofType.cast(this));
            if (result != null) {
                return result;
            }
        }
        for (final GuiElement<?> element : getChildren()) {
            R result = null;
            if (element instanceof ComposedGuiElement) {
                ComposedGuiElement<?> composed = (ComposedGuiElement<?>) element;
                if (layer <= composed.getMaxLayer()) {
                    result = recurse.apply(composed, layer - composed.getLayer());
                }
            } else if (ofType.isInstance(element) && element.getLayer() == layer) {
                result = function.apply(ofType.cast(element));
            }
            if (result != null) {
                return result;
            }
        }
        return null;
    }
} public abstract class AbstractGuiElement<T extends AbstractGuiElement<T>> implements GuiElement<T> {
    protected static final Identifier TEXTURE = new Identifier("jgui", "gui.png");


    private final MinecraftClient minecraft = MCVer.getMinecraft();

    private GuiContainer container;

    private GuiElement tooltip;

    private boolean enabled = true;

    protected Dimension minSize, maxSize;

    /**
     * The last size this element was render at layer 0.
     * May be {@code null} when this element has not yet been rendered.
     */
    private ReadableDimension lastSize;

    public AbstractGuiElement() {
    }

    public AbstractGuiElement(GuiContainer container) {
        container.addElements(null, this);
    }

    protected abstract T getThis();

    @Override
    public void layout(ReadableDimension size, RenderInfo renderInfo) {
        if (size == null) {
            if (getContainer() == null) {
                throw new RuntimeException("Any top containers must implement layout(null, ...) themselves!");
            }
            getContainer().layout(size, renderInfo.layer(renderInfo.layer + getLayer()));
            return;
        }
        if (renderInfo.layer == 0) {
            lastSize = size;
        }
    }

    @Override
    public void draw(GuiRenderer renderer, ReadableDimension size, RenderInfo renderInfo) {
    }

    @Override
    public T setEnabled(boolean enabled) {
        this.enabled = enabled;
        return getThis();
    }

    @Override
    public T setEnabled() {
        return setEnabled(true);
    }

    @Override
    public T setDisabled() {
        return setEnabled(false);
    }

    @Override
    public GuiElement getTooltip(RenderInfo renderInfo) {
        if (tooltip != null && lastSize != null) {
            Point mouse = new Point(renderInfo.mouseX, renderInfo.mouseY);
            if (container != null) {
                container.convertFor(this, mouse);
            }
            if (mouse.getX() > 0
                    && mouse.getY() > 0
                    && mouse.getX() < lastSize.getWidth()
                    && mouse.getY() < lastSize.getHeight()) {
                return tooltip;
            }
        }
        return null;
    }

    @Override
    public T setTooltip(GuiElement tooltip) {
        this.tooltip = tooltip;
        return getThis();
    }

    @Override
    public T setContainer(GuiContainer container) {
        this.container = container;
        return getThis();
    }

    public T setMinSize(ReadableDimension minSize) {
        this.minSize = new Dimension(minSize);
        return getThis();
    }

    public T setMaxSize(ReadableDimension maxSize) {
        this.maxSize = new Dimension(maxSize);
        return getThis();
    }

    public T setSize(ReadableDimension size) {
        setMinSize(size);
        return setMaxSize(size);
    }

    public T setSize(int width, int height) {
        return setSize(new Dimension(width, height));
    }

    public T setWidth(int width) {
        if (minSize == null) {
            minSize = new Dimension(width, 0);
        } else {
            minSize.setWidth(width);
        }
        if (maxSize == null) {
            maxSize = new Dimension(width, Integer.MAX_VALUE);
        } else {
            maxSize.setWidth(width);
        }
        return getThis();
    }

    public T setHeight(int height) {
        if (minSize == null) {
            minSize = new Dimension(0, height);
        } else {
            minSize.setHeight(height);
        }
        if (maxSize == null) {
            maxSize = new Dimension(Integer.MAX_VALUE, height);
        } else {
            maxSize.setHeight(height);
        }
        return getThis();
    }

    public int getLayer() {
        return 0;
    }

    @Override
    public ReadableDimension getMinSize() {
        ReadableDimension calcSize = calcMinSize();
        if (minSize == null) {
            return calcSize;
        } else {
            if (minSize.getWidth() >= calcSize.getWidth() && minSize.getHeight() >= calcSize.getHeight()) {
                return minSize;
            } else {
                return new Dimension(
                        Math.max(calcSize.getWidth(), minSize.getWidth()),
                        Math.max(calcSize.getHeight(), minSize.getHeight())
                );
            }
        }
    }

    protected abstract ReadableDimension calcMinSize();

    @Override
    public ReadableDimension getMaxSize() {
        return maxSize == null ? new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE) : maxSize;
    }

    public MinecraftClient getMinecraft() {
        return this.minecraft;
    }

    public GuiContainer getContainer() {
        return this.container;
    }

    public boolean isEnabled() {
        return this.enabled;
    }

    protected ReadableDimension getLastSize() {
        return this.lastSize;
    }
} public interface GuiElement<T extends GuiElement<T>> {

    MinecraftClient getMinecraft();

    GuiContainer getContainer();
    T setContainer(GuiContainer container);

    void layout(ReadableDimension size, RenderInfo renderInfo);
    void draw(GuiRenderer renderer, ReadableDimension size, RenderInfo renderInfo);

    ReadableDimension getMinSize();
    ReadableDimension getMaxSize();

    T setMaxSize(ReadableDimension maxSize);

    boolean isEnabled();
    T setEnabled(boolean enabled);
    T setEnabled();
    T setDisabled();

    GuiElement getTooltip(RenderInfo renderInfo);
    T setTooltip(GuiElement tooltip);

    /**
     * Returns the layer this element takes part in.
     * The standard layer is layer 0. Event handlers will be called for this layer.
     * @return The layer of this element
     */
    int getLayer();

} public interface Clickable {
    boolean mouseClick(ReadablePoint position, int button);
} public interface Draggable extends Clickable {
    boolean mouseDrag(ReadablePoint position, int button, @Deprecated long timeSinceLastCall);
    boolean mouseRelease(ReadablePoint position, int button);
} public interface Consumer<T> {
    void consume(T obj);
}
import de.johni0702.minecraft.gui.element.GuiElement;
import de.johni0702.minecraft.gui.function.Clickable;
import de.johni0702.minecraft.gui.function.Draggable;
import de.johni0702.minecraft.gui.utils.Consumer;
import de.johni0702.minecraft.gui.utils.lwjgl.Color;
import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import de.johni0702.minecraft.gui.utils.lwjgl.Point;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableColor;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadablePoint;
import java.util.Collection;
import java.util.Collections;
import de.johni0702.minecraft.gui.GuiRenderer;
import de.johni0702.minecraft.gui.OffsetGuiRenderer;
import de.johni0702.minecraft.gui.RenderInfo;
import de.johni0702.minecraft.gui.container.GuiContainer;
import de.johni0702.minecraft.gui.element.AbstractComposedGuiElement;
import de.johni0702.minecraft.gui.element.AbstractGuiElement;
/*
 * This file is part of jGui API, licensed under the MIT License (MIT).
 *
 * Copyright (c) 2016 johni0702 <https://github.com/johni0702>
 * Copyright (c) contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package de.johni0702.minecraft.gui.element.advanced;



public abstract class AbstractGuiColorPicker<T extends AbstractGuiColorPicker<T>>
        extends AbstractComposedGuiElement<T> implements IGuiColorPicker<T>, Clickable {
    protected static final int PICKER_SIZE = 100;
    private static final ReadableColor OUTLINE_COLOR = new Color(255, 255, 255);

    private Color color = new Color();

    private boolean opened;

    private Consumer<ReadableColor> onSelection;

    private GuiPicker picker = new GuiPicker();

    public AbstractGuiColorPicker() {
    }

    public AbstractGuiColorPicker(GuiContainer container) {
        super(container);
    }

    @Override
    public int getMaxLayer() {
        return opened ? 1 : 0;
    }

    @Override
    protected ReadableDimension calcMinSize() {
        return new Dimension(3, 3);
    }

    @Override
    public void layout(ReadableDimension size, RenderInfo renderInfo) {
        super.layout(size, renderInfo);
        if (size == null) return;
        if (renderInfo.layer == 1) {
            ReadableDimension offsetSize = new Dimension(PICKER_SIZE, PICKER_SIZE);
            picker.layout(offsetSize, renderInfo);
        }
    }

    @Override
    public void draw(GuiRenderer renderer, ReadableDimension size, RenderInfo renderInfo) {
        super.draw(renderer, size, renderInfo);
        if (renderInfo.layer == 0) {
            int width = size.getWidth();
            int height = size.getHeight();

            // Draw outline
            renderer.drawRect(0, 0, width, height, OUTLINE_COLOR);
            // Draw color
            renderer.drawRect(1, 1, width - 2, height - 2, color);
        } else if (renderInfo.layer == 1) {
            ReadablePoint offsetPoint = new Point(0, size.getHeight());
            ReadableDimension offsetSize = new Dimension(PICKER_SIZE, PICKER_SIZE);
            OffsetGuiRenderer offsetRenderer = new OffsetGuiRenderer(renderer, offsetPoint, offsetSize);
            offsetRenderer.startUsing();
            try {
                picker.draw(offsetRenderer, offsetSize, renderInfo);
            } finally {
                offsetRenderer.stopUsing();
            }
        }
    }

    protected void getColorAtPosition(int x, int y, Color color) {
        if (x < 0 || y < 0 || x >= PICKER_SIZE || y >= PICKER_SIZE) {
            throw new IndexOutOfBoundsException();
        }

        if (x < 5) { // Grey
            int intensity = 255 - y * 255 / PICKER_SIZE;
            color.set(intensity, intensity, intensity);
        } else { // Colored
            float hue = (x - 5f) / (PICKER_SIZE - 5f);
            float saturation = Math.min(y / (PICKER_SIZE / 2f), 1);
            float brightness = Math.min(2 - y / (PICKER_SIZE / 2f), 1);
            color.fromHSB(hue, saturation, brightness);
        }
    }

    @Override
    public T setColor(ReadableColor color) {
        this.color.setColor(color);
        return getThis();
    }

    @Override
    public T setOpened(boolean opened) {
        this.opened = opened;
        return getThis();
    }

    @Override
    public Collection<GuiElement> getChildren() {
        return opened ? Collections.<GuiElement>singleton(picker) : Collections.<GuiElement>emptyList();
    }

    @Override
    public T onSelection(Consumer<ReadableColor> consumer) {
        this.onSelection = consumer;
        return getThis();
    }

    public void onSelection(Color oldColor) {
        if (onSelection != null) {
            onSelection.consume(oldColor);
        }
    }

    @Override
    public boolean mouseClick(ReadablePoint position, int button) {
        Point pos = new Point(position);
        if (getContainer() != null) {
            getContainer().convertFor(this, pos);
        }

        if (isEnabled()) {
            if (isMouseHovering(pos)) {
                setOpened(!isOpened());
                return true;
            }
        }
        return false;
    }

    protected boolean isMouseHovering(ReadablePoint pos) {
        return pos.getX() > 0 && pos.getY() > 0
                && pos.getX() < getLastSize().getWidth() && pos.getY() < getLastSize().getHeight();
    }

    public Color getColor() {
        return this.color;
    }

    public boolean isOpened() {
        return this.opened;
    }

    protected class GuiPicker extends AbstractGuiElement<GuiPicker> implements Clickable, Draggable {