@Mod(modid = WearableBackpacks.MOD_ID, name = WearableBackpacks.MOD_NAME,
     version = WearableBackpacks.VERSION, dependencies = "required-after:forge@[14.21.0.2375,)",
     guiFactory = "net.mcft.copy.backpacks.client.BackpacksGuiFactory")
public class WearableBackpacks {
	
	public static final String MOD_ID   = "wearablebackpacks";
	public static final String MOD_NAME = "Wearable Backpacks";
	public static final String VERSION  = "@VERSION@";
	
	@Instance
	public static WearableBackpacks INSTANCE;
	
	@SidedProxy(serverSide = "net.mcft.copy.backpacks.ProxyCommon",
	            clientSide = "net.mcft.copy.backpacks.ProxyClient")
	public static ProxyCommon PROXY;
	
	public static Logger LOG;
	public static BackpacksChannel CHANNEL;
	public static BackpacksConfig CONFIG;
	public static BackpacksContent CONTENT;
	
	@EventHandler
	public void preInit(FMLPreInitializationEvent event) {
		LOG = event.getModLog();
		CHANNEL = new BackpacksChannel();
		
		CONFIG = new BackpacksConfig(event.getSuggestedConfigurationFile());
		CONFIG.load();
		CONFIG.save();
		
		CONTENT = new BackpacksContent();
		CONTENT.registerRecipes();
		
		PROXY.preInit();
	}
	
	@EventHandler
	public void init(FMLInitializationEvent event) {
		CONFIG.init();
		PROXY.init();
	}
	
} @SideOnly(Side.CLIENT)
public class GuiTextureResource extends ResourceLocation {
	
	public final int defaultWidth;
	public final int defaultHeight;
	
	public GuiTextureResource(String location, int defaultWidth, int defaultHeight) {
		super(WearableBackpacks.MOD_ID, "textures/gui/" + location + ".png");
		this.defaultWidth = defaultWidth;
		this.defaultHeight = defaultHeight;
	}
	
	public void bind() {
		Minecraft.getMinecraft().getTextureManager().bindTexture(this);
	}
	
	/** Draws part of the texture to the screen. */
	public void drawQuad(int x, int y, int u, int v, int w, int h, float zLevel) {
		float scaleX = 1.0F / defaultWidth;
		float scaleY = 1.0F / defaultHeight;
		Tessellator tess = Tessellator.getInstance();
		BufferBuilder vb = tess.getBuffer();
		vb.begin(7, DefaultVertexFormats.POSITION_TEX);
		vb.pos(x + 0, y + h, zLevel).tex((u + 0) * scaleX, (v + h) * scaleY).endVertex();
		vb.pos(x + w, y + h, zLevel).tex((u + w) * scaleX, (v + h) * scaleY).endVertex();
		vb.pos(x + w, y + 0, zLevel).tex((u + w) * scaleX, (v + 0) * scaleY).endVertex();
		vb.pos(x + 0, y + 0, zLevel).tex((u + 0) * scaleX, (v + 0) * scaleY).endVertex();
		tess.draw();
	}
	
	/** Draws part of the texture to the screen. */
	public void drawQuad(int x, int y, int u, int v, int w, int h) {
		drawQuad(x, y, u, v, w, h, 0);
	}
	
} @SideOnly(Side.CLIENT)
public enum Direction {
	
	HORIZONTAL,
	VERTICAL;
	
	public Direction perpendicular()
		{ return (this == HORIZONTAL) ? VERTICAL : HORIZONTAL; }
	
} @SideOnly(Side.CLIENT)
public abstract class GuiElementBase {
	
	public static final String ELLIPSIS    = "...";
	public static final int ELLIPSIS_WIDTH = getStringWidth(ELLIPSIS);
	public static final int LINE_HEIGHT    = getFontRenderer().FONT_HEIGHT;
	
	public static final int COLOR_CONTROL           = 0xFFE0E0E0;
	public static final int COLOR_CONTROL_HIGHLIGHT = 0xFFFFFFA0;
	public static final int COLOR_CONTROL_DISABLED  = 0xFFA0A0A0;
	
	
	private GuiContext _context;
	private GuiContainer _parent;
	
	private boolean _visible = true, _enabled = true;

	private int _width, _height;
	
	private Alignment _horizontalAlign = new Alignment.Min(0);
	private Alignment _verticalAlign = new Alignment.Min(0);
	
	private List<String> _tooltip = null;
	private Supplier<List<String>> _tooltipFunc = null;
	private int _tooltipDelay = TooltipDelay.NORMAL;
	
	private int _dragStartX, _dragStartY;
	private int _dragPrevX, _dragPrevY;
	
	
	void setContext(GuiContext value) { _context = value; }
	void setParent(GuiContainer value) { _parent = value; }
	
	public final GuiContext getContext() { return _context; }
	public final GuiContainer getParent() { return _parent; }
	
	public boolean isVisible() { return _visible; }
	public void setVisible(boolean value) { _visible = value; }
	
	public boolean isEnabled() { return _enabled &&
		((getParent() != null) ? getParent().isEnabled() : true); }
	public void setEnabled(boolean value) { _enabled = value; }
	
	// Size related
	
	public int getSize(Direction direction)
		{ return (direction == Direction.HORIZONTAL) ? _width : _height; }
	public final int getWidth() { return getSize(Direction.HORIZONTAL); }
	public final int getHeight() { return getSize(Direction.VERTICAL); }
	
	public void setSize(Direction direction, int value) {
		if (value == getSize(direction)) return;
		if (direction == Direction.HORIZONTAL) _width = value;
		else _height = value;
		onSizeChanged(direction);
		if (_parent != null) _parent.onChildSizeChanged(this, direction);
	}
	public final void setWidth(int value) { setSize(Direction.HORIZONTAL, value); }
	public final void setHeight(int value) { setSize(Direction.VERTICAL, value); }
	public final void setSize(int width, int height) { setWidth(width); setHeight(height); }
	
	// Alignment related
	
	public Alignment getAlign(Direction direction)
		{ return (direction == Direction.HORIZONTAL) ? _horizontalAlign : _verticalAlign; }
	public final Alignment getHorizontalAlign() { return getAlign(Direction.HORIZONTAL); }
	public final Alignment getVerticalAlign() { return getAlign(Direction.VERTICAL); }
	
	public void setAlign(Direction direction, Alignment value) {
		if (value == null) throw new NullPointerException("Argument must be non-null");
		if (direction == Direction.HORIZONTAL) _horizontalAlign = value;
		else _verticalAlign = value;
		if (_parent != null) _parent.onChildAlignChanged(this, direction);
	}
	public final void setAlignHorizontal(Alignment value) { setAlign(Direction.HORIZONTAL, value); }
	public final void setAlignVertical(Alignment value) { setAlign(Direction.VERTICAL, value); }
	public final void setAlign(Alignment horizontal, Alignment vertical)
		{ setAlignHorizontal(horizontal); setAlignVertical(vertical); }
	
	// Useful alignment shortcuts
	
	public final void setPosition(int left, int top)
		{ setAlign(new Alignment.Min(left), new Alignment.Min(top)); }
	
	public final void setLeft(int value) { setAlignHorizontal(new Alignment.Min(value)); }
	public final void setRight(int value) { setAlignHorizontal(new Alignment.Max(value)); }
	public final void setTop(int value) { setAlignVertical(new Alignment.Min(value)); }
	public final void setBottom(int value) { setAlignVertical(new Alignment.Max(value)); }
	
	public final void setLeftRight(int value) { setLeftRight(value, value); }
	public final void setLeftRight(int left, int right)
		{ setAlignHorizontal(new Alignment.Both(left, right)); }
	public final void setTopBottom(int value) { setTopBottom(value, value); }
	public final void setTopBottom(int top, int bottom)
		{ setAlignVertical(new Alignment.Both(top, bottom)); }
	
	public final void setCenteredHorizontal() { setAlignHorizontal(new Alignment.Center()); }
	public final void setCenteredHorizontal(int width) { setCenteredHorizontal(); setWidth(width); }
	public final void setCenteredVertical() { setAlignVertical(new Alignment.Center()); }
	public final void setCenteredVertical(int height) { setCenteredVertical(); setHeight(height); }
	
	public final void setFillHorizontal() { setLeftRight(0); }
	public final void setFillVertical() { setTopBottom(0); }
	public final void setFill() { setFillHorizontal(); setFillVertical(); }
	
	// Tooltips
	
	public boolean hasTooltip() { return !getTooltip().isEmpty(); }
	
	public List<String> getTooltip() {
		List<String> tooltip = (_tooltipFunc != null) ? _tooltipFunc.get() : _tooltip;
		if (tooltip == null) tooltip = Collections.emptyList();
		return tooltip;
	}
	public int getTooltipDelay() { return _tooltipDelay; }
	
	public void setTooltip(List<String> value)
		{ setTooltip(TooltipDelay.NORMAL, value); }
	public void setTooltip(int delay, List<String> value)
		{ _tooltip = value; _tooltipDelay = delay; }
	public void setTooltip(Supplier<List<String>> value)
		{ setTooltip(TooltipDelay.NORMAL, value); }
	public void setTooltip(int delay, Supplier<List<String>> value)
		{ _tooltipFunc = value; _tooltipDelay = delay; }
	
	public static final class TooltipDelay {
		public static final int SHORT = 200;
		public static final int NORMAL = 400;
		public static final int LONG = 800;
		private TooltipDelay() {  }
	}
	
	// Element focus
	
	/** Returns if this element can be focused. */
	public boolean canFocus() { return false; }
	/** Returns if this element is currently focused. */
	public boolean isFocused() { return (getContext().getFocused() == this); }
	
	/** Makes this element the currently focused element. */
	public void setFocused() { setFocused(true); }
	/** Sets whether this element is the currently focused element. */
	public void setFocused(boolean value) {
		if (value && !canFocus())
			throw new UnsupportedOperationException("This element can't be focused");
		if (!isVisible()) return;
		getContext().setFocused(value ? this : null);
	}
	
	// Press / drag related
	
	/** Returns if this element can be pressed. */
	public boolean canPress() { return canDrag() || canFocus(); }
	/** Returns if this element is currently being pressed down. */
	public boolean isPressed() { return (getContext() != null) && (getContext().getPressed() == this); }
	/** Called when this element is pressed with the left mouse button.
	 *  Mouse position is relative to the element's position. */
	public void onPressed(int mouseX, int mouseY) {  }
	
	/** Returns if this element can be dragged. */
	public boolean canDrag() { return false; }
	/** Returns if this element is currently being dragged. */
	public boolean isDragged() { return (canDrag() && isPressed()); }
	/** Called when the mouse moves while this element is being dragged.
	 *  Mouse and start position are relative to the element's position.
	 *  Delta position is relative to the previous mouse position. */
	public void onDragged(int mouseX, int mouseY, int deltaX, int deltaY, int startX, int startY) {  }
	
	// Basic events
	
	/** Called when this element is resized. */
	public void onSizeChanged(Direction direction) {  }
	
	// Mouse events
	
	/** Called when the mouse is clicked in this element.
	 *  Mouse position is relative to the element's position.
	 *  Returns if the mouse action was handled. */
	public boolean onMouseDown(int mouseButton, int mouseX, int mouseY) {
		if (!isVisible() || !isEnabled()) return false;
		if ((mouseButton == MouseButton.LEFT) && canPress()) {
			GuiContext context = getContext();
			context.setPressed(this);
			if (canDrag()) {
				_dragStartX = mouseX; _dragStartY = mouseY;
				_dragPrevX  = mouseX; _dragPrevY  = mouseY;
			}
			if (canFocus()) setFocused();
			onPressed(mouseX, mouseY);
			return true;
		} else return false;
	}
	
	/** Called when the mouse is moved over this element or being dragged.
	 *  Mouse position is relative to the element's position. */
	public void onMouseMove(int mouseX, int mouseY) {
		if (isVisible() && isDragged()) {
			onDragged(mouseX, mouseY,
			          mouseX - _dragPrevX, mouseY - _dragPrevY,
			          _dragStartX, _dragStartY);
			_dragPrevX = mouseX; _dragPrevY = mouseY;
		}
	}
	
	/** Called when the mouse button is being released
	 *  while in this element or while it's being dragged.
	 *  Mouse position is relative to the element's position. */
	public void onMouseUp(int mouseButton, int mouseX, int mouseY) {  }
	
	/** Called when the mouse wheel is being scrolled over this element.
	 *  Scroll is either 1 (down) or -1 (up) depending on the scroll direction.
	 *  Mouse position is relative to the element's position. */
	public boolean onMouseScroll(int scroll, int mouseX, int mouseY) { return false; }
	
	// Keyboard events
	
	/** Called when this element is focused and a key is typed. */
	public void onKey(int keyCode, char keyChar) {  }
	
	// Draw events
	
	/** Draws this element on the screen. Rendered relative to its position.
	 *  Mouse position is relative to the element's position. */
	public void draw(int mouseX, int mouseY, float partialTicks) {  }
	
	/** Draws this element's tooltip on the screen. Rendering and mouse
	 *  position are global, not relative to this element's position. */
	public void drawTooltip(int mouseX, int mouseY, int screenWidth, int screenHeight, float partialTicks) {
		if (!hasTooltip()) return;
		GuiUtils.drawHoveringText(getTooltip(),
			mouseX, mouseY, screenWidth, screenHeight,
			300, getFontRenderer());
	}
	
	
	// Utility methods
	
	/** Returns whether the specified region (x, y, width, height)
	 *  contains the specified point (testX, testY). */
	public static boolean regionContains(int x, int y, int width, int height,
	                                     int testX, int testY) {
		return (testX >= x) && (testX < x + width) &&
		       (testY >= y) && (testY < y + height);
	}
	/** Returns whether the specified relative
	 *  position is within the element's region. */
	public boolean contains(int x, int y)
		{ return regionContains(0, 0, getWidth(), getHeight(), x, y); }
	
	/** Returns whether this element should be appear highlighted when drawn.
	 *  By default, the element must either be pressed down, or no other
	 *  element must be pressed down be while the cursor is over it. */
	public boolean isHighlighted(int mouseX, int mouseY) {
		GuiElementBase pressed = getContext().getPressed();
		return isEnabled() && (pressed == this)
		    || (pressed == null) && contains(mouseX, mouseY);
	}
	
	public static Minecraft getMC() { return Minecraft.getMinecraft(); }
	public static void display(GuiScreen screen) { getMC().displayGuiScreen(screen); }
	public static GuiScreen getCurrentScreen() { return getMC().currentScreen; }
	
	public static FontRenderer getFontRenderer() { return getMC().fontRenderer; }
	public static int getStringWidth(String text) { return getFontRenderer().getStringWidth(text); }
	
	public static void bindTexture(ResourceLocation resource)
		{ getMC().getTextureManager().bindTexture(resource); }
	
	public static void setRenderColor(float red, float green, float blue)
		{ setRenderColor(red, green, blue, 1.0F); }
	public static void setRenderColor(float red, float green, float blue, float alpha)
		{ GlStateManager.color(red, green, blue, alpha); }
	
	public static void setRenderColorRGB(int color)
		{ setRenderColorARGB(color | 0xFF000000); }
	public static void setRenderColorARGB(int color) {
		setRenderColor((color >> 16 & 0xFF) / 255.0f,
		               (color >> 8 & 0xFF) / 255.0f,
		               (color & 0xFF) / 255.0f,
		               (color >> 24 & 0xFF) / 255.0f); }
	
	public static void enableBlendAlphaStuffs() {
		GlStateManager.enableBlend();
		GlStateManager.tryBlendFuncSeparate(
			SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA,
			SourceFactor.ZERO, DestFactor.ONE);
		GlStateManager.disableTexture2D();
		GlStateManager.shadeModel(GL11.GL_SMOOTH);
	}
	public static void disableBlendAlphaStuffs() {
		GlStateManager.shadeModel(GL11.GL_FLAT);
		GlStateManager.enableTexture2D();
		GlStateManager.disableBlend();
	}
	
	public static void drawRect(int x, int y, int width, int height) {
		Tessellator tessellator = Tessellator.getInstance();
		BufferBuilder buffer = tessellator.getBuffer();
		buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION);
		buffer.pos(x        , y         , 0).endVertex();
		buffer.pos(x        , y + height, 0).endVertex();
		buffer.pos(x + width, y + height, 0).endVertex();
		buffer.pos(x + width, y         , 0).endVertex();
		tessellator.draw();
	}
	public static void drawOutline(int x, int y, int width, int height) {
		drawRect(x,             y,              width, 1);
		drawRect(x,             y + height - 1, width, 1);
		drawRect(x,             y + 1,          1,     height - 2);
		drawRect(x + width - 1, y + 1,          1,     height - 2);
	}
	
	public static void drawRect(int x, int y, int u, int v, int width, int height, int texSize)
		{ drawRect(x, y, u, v, width, height, texSize, texSize); }
	public static void drawRect(int x, int y, int u, int v, int width, int height, int texWidth, int texHeight) {
		float u1 = u / (float)texWidth;
		float v1 = v / (float)texHeight;
		float u2 = (u + width) / (float)texWidth;
		float v2 = (v + height) / (float)texHeight;
		drawRect(x, y, width, height, u1, v1, u2, v2);
	}
	public static void drawRect(int x, int y, int width, int height,
	                            float u1, float v1, float u2, float v2) {
		Tessellator tessellator = Tessellator.getInstance();
		BufferBuilder buffer = tessellator.getBuffer();
		buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
		buffer.pos(x        , y         , 0).tex(u1, v1).endVertex();
		buffer.pos(x        , y + height, 0).tex(u1, v2).endVertex();
		buffer.pos(x + width, y + height, 0).tex(u2, v2).endVertex();
		buffer.pos(x + width, y         , 0).tex(u2, v1).endVertex();
		tessellator.draw();
	}
	
	public static void drawColoredRectRGB(int x, int y, int width, int height, int color)
		{ drawColoredRectARGB(x, y, width, height, color | 0xFF000000); }
	public static void drawColoredRectARGB(int x, int y, int width, int height, int color)
		{ drawColoredRectARGB(x, y, width, height, color, color, color, color); }
	public static void drawColoredRectARGB(int x, int y, int width, int height,
		                                   int colorTL, int colorTR, int colorBL, int colorBR) {
		Tessellator tessellator = Tessellator.getInstance();
		BufferBuilder buffer = tessellator.getBuffer();
		buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR);
		buffer.pos(x        , y         , 0).color(colorTL >> 16 & 0xFF, colorTL >> 8 & 0xFF, colorTL & 0xFF, colorTL >> 24 & 0xFF).endVertex();
		buffer.pos(x        , y + height, 0).color(colorBL >> 16 & 0xFF, colorBL >> 8 & 0xFF, colorBL & 0xFF, colorBL >> 24 & 0xFF).endVertex();
		buffer.pos(x + width, y + height, 0).color(colorBR >> 16 & 0xFF, colorBR >> 8 & 0xFF, colorBR & 0xFF, colorBR >> 24 & 0xFF).endVertex();
		buffer.pos(x + width, y         , 0).color(colorTR >> 16 & 0xFF, colorTR >> 8 & 0xFF, colorTR & 0xFF, colorTR >> 24 & 0xFF).endVertex();
		tessellator.draw();
	}
	
	public static void drawColoredRectRGB(int x, int y, int width, int height,
	                                      float u1, float v1, float u2, float v2, int color)
		{ drawColoredRectARGB(x, y, width, height, u1, v1, u2, v2, color | 0xFF000000); }
	public static void drawColoredRectARGB(int x, int y, int width, int height,
	                                       float u1, float v1, float u2, float v2, int color)
		{ drawColoredRectARGB(x, y, width, height, u1, v1, u2, v2, color, color, color, color); }
	public static void drawColoredRectARGB(int x, int y, int width, int height,
	                                       float u1, float v1, float u2, float v2,
		                                   int colorTL, int colorTR, int colorBL, int colorBR) {
		Tessellator tessellator = Tessellator.getInstance();
		BufferBuilder buffer = tessellator.getBuffer();
		buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR);
		buffer.pos(x        , y         , 0).tex(u1, v1).color(colorTL >> 16 & 0xFF, colorTL >> 8 & 0xFF, colorTL & 0xFF, colorTL >> 24 & 0xFF).endVertex();
		buffer.pos(x        , y + height, 0).tex(u1, v2).color(colorBL >> 16 & 0xFF, colorBL >> 8 & 0xFF, colorBL & 0xFF, colorBL >> 24 & 0xFF).endVertex();
		buffer.pos(x + width, y + height, 0).tex(u2, v2).color(colorBR >> 16 & 0xFF, colorBR >> 8 & 0xFF, colorBR & 0xFF, colorBR >> 24 & 0xFF).endVertex();
		buffer.pos(x + width, y         , 0).tex(u2, v1).color(colorTR >> 16 & 0xFF, colorTR >> 8 & 0xFF, colorTR & 0xFF, colorTR >> 24 & 0xFF).endVertex();
		tessellator.draw();
	}
	
	// Utility classes
	
	public static final class MouseButton {
		private MouseButton() {  }
		
		public static int LEFT   = 0;
		public static int RIGHT  = 1;
		public static int MIDDLE = 2;
	}
	
	public static final class Color {
		private Color() {  }
		
		public static int WHITE       = 0xFFFFFFFF;
		public static int BLACK       = 0xFF000000;
		public static int TRANSPARENT = 0x00000000;
	}
	
} @SideOnly(Side.CLIENT)
public class GuiImage extends GuiElementBase {
	
	private GuiTextureResource _texture;
	private int _texU, _texV;
	
	public GuiImage(int width, int height, GuiTextureResource texture)
		{ this(width, height, texture, 0, 0); }
	public GuiImage(int width, int height, GuiTextureResource texture, int u, int v)
		{ this(0, 0, width, height, texture, u, v); }
	public GuiImage(int x, int y, int width, int height,
	                GuiTextureResource texture, int u, int v) {
		setPosition(x, y);
		setSize(width, height);
		setTexture(texture, u, v);
		setTextureUV(u, v);
	}
	
	public GuiTextureResource getTexture() { return _texture; }
	public void setTexture(GuiTextureResource value) { _texture = value; }
	
	public int getTextureU() { return _texU; }
	public int getTextureV() { return _texV; }
	public void setTextureUV(int u, int v) { _texU = u; _texV = v; }
	
	public void setTexture(GuiTextureResource texture, int u, int v)
		{ setTexture(texture); setTextureUV(u, v); }
	
	@Override
	public void draw(int mouseX, int mouseY, float partialTicks) {
		GuiTextureResource texture = getTexture();
		if (texture == null) return;
		setRenderColorARGB(Color.WHITE);
		texture.bind();
		texture.drawQuad(0, 0, getTextureU(), getTextureV(), getWidth(), getHeight());
	}
	
} @SideOnly(Side.CLIENT)
public class GuiLabel extends GuiElementBase {
	
	public static final int SHADOW_DISABLED = -1;
	public static final int SHADOW_AUTOMATIC = -2;
	
	
	private String _text = "";
	private TextAlign _align = TextAlign.LEFT;
	private boolean _expands;
	
	private int _color         = Color.WHITE;
	private int _disabledColor = COLOR_CONTROL_DISABLED;
	private int _shadowColor   = SHADOW_AUTOMATIC;
	
	
	public GuiLabel(String text)
		{ this(text, TextAlign.LEFT); }
	public GuiLabel(String text, TextAlign align)
		{ this(0, 0, text, align); }
	public GuiLabel(int x, int y, String text)
		{ this(x, y, text, TextAlign.LEFT); }
	public GuiLabel(int x, int y, String text, TextAlign align) {
		_expands = true;
		setPosition(x, y);
		setText(text);
		setTextAlign(align);
	}
	
	public GuiLabel(int width, String text)
		{ this(width, text, TextAlign.LEFT); }
	public GuiLabel(int width, String text, TextAlign align)
		{ this(0, 0, width, text, align); }
	public GuiLabel(int x, int y, int width, String text)
		{ this(x, y, width, LINE_HEIGHT, text, TextAlign.LEFT); }
	public GuiLabel(int x, int y, int width, String text, TextAlign align)
		{ this(x, y, width, LINE_HEIGHT, text, align); }
	public GuiLabel(int x, int y, int width, int height, String text)
		{ this(x, y, width, height, text, TextAlign.LEFT); }
	public GuiLabel(int x, int y, int width, int height, String text, TextAlign align) {
		_expands = false;
		setPosition(x, y);
		setSize(width, height);
		setText(text);
		setTextAlign(align);
	}
	
	
	/** Returns if this label expands its size depending on text contents. */
	public boolean getExpands() { return _expands; }
	
	public TextAlign getTextAlign() { return _align; }
	public void setTextAlign(TextAlign value) { _align = value; }
	
	
	public String getText() { return _text; }
	public void setText(String text) {
		if (text == null) throw new NullPointerException("Argument text can't be null");
		if (text.equals(_text)) return;
		_text = text;
		if (_expands) {
			FontRenderer fontRenderer = getFontRenderer();
			int width  = fontRenderer.getStringWidth(_text);
			int height = fontRenderer.getWordWrappedHeight(_text, Integer.MAX_VALUE);
			setSize(width, height);
			_expands = true;
		}
	}
	
	public int getColor() { return _color; }
	public void setColor(int value) { _color = value; }
	
	public int getDisabledColor() { return _disabledColor; }
	public void setDisabledColor(int value) { _disabledColor = value; }
	
	public int getShadowColor() { return _shadowColor; }
	public void setShadowColor(int value) {
		if (value < SHADOW_AUTOMATIC) throw new IllegalArgumentException(
			String.format("'%d' is not a valid special shadow value", value));
		_shadowColor = value;
	}
	public void setShadowDisabled() { setShadowColor(SHADOW_DISABLED); }
	
	
	@Override
	public void onSizeChanged(Direction direction) {
		super.onSizeChanged(direction);
		_expands = false;
	}
	
	@Override
	public void draw(int mouseX, int mouseY, float partialTicks) {
		if (!isVisible()) return;
		FontRenderer fontRenderer = getFontRenderer();
		List<String> lines = fontRenderer.listFormattedStringToWidth(_text, Integer.MAX_VALUE);
		int yPos = 1;
		for (String line : lines) {
			int lineWidth = fontRenderer.getStringWidth(line);
			if (!_expands && (lineWidth > getWidth())) {
				line = fontRenderer.trimStringToWidth(line, getWidth() - ELLIPSIS_WIDTH) + ELLIPSIS;
				lineWidth = fontRenderer.getStringWidth(line);
			}
			int xPos = 0;
			switch (_align) {
				case LEFT:   xPos = 0; break;
				case RIGHT:  xPos = getWidth() - lineWidth; break;
				case CENTER: xPos = (getWidth() - lineWidth) / 2; break;
			}
			
			int color = isEnabled() ? getColor() : getDisabledColor();
			int shadowColor = getShadowColor();
			if (shadowColor >= 0)
				fontRenderer.drawString(line, xPos + 1, yPos + 1, shadowColor);
			if (shadowColor == SHADOW_AUTOMATIC)
				fontRenderer.drawStringWithShadow(line, xPos, yPos, color);
			else fontRenderer.drawString(line, xPos, yPos, color);
			
			yPos += LINE_HEIGHT;
		}
	}
	
	public enum TextAlign {
		LEFT,
		RIGHT,
		CENTER
	}
	
} @SideOnly(Side.CLIENT)
public class GuiLayout extends GuiContainer {
	
	public final Direction direction;
	
	private int[] _spacing = { 2 };
	
	public GuiLayout(Direction direction)
		{ this.direction = direction; }
	
	public int[] getSpacing() { return _spacing; }
	public void setSpacing(int... value) { _spacing = value; }
	
	
	// Getting / adding / inserting elements
	
	/** Gets the element at the specified index in this layout container. */
	public GuiElementBase get(int index)
		{ return children.get(index); }
	
	@Override
	public void add(GuiElementBase element) { addWeighted(element); }
	
	public void addFixed(GuiElementBase element, int size)
		{ insertFixed(children.size(), element, size); }
	public void addFixed(GuiElementBase element)
		{ insertFixed(children.size(), element); }
	
	public void addWeighted(GuiElementBase element)
		{ insertWeighted(children.size(), element); }
	public void addWeighted(GuiElementBase element, double weight)
		{ insertWeighted(children.size(), element, weight); }
	public void addWeighted(GuiElementBase element, double weight, int minSize)
		{ insertWeighted(children.size(), element, weight, minSize); }
	
	public void insertFixed(int index, GuiElementBase element, int size)
		{ element.setSize(direction, size); insertFixed(index, element); }
	public void insertFixed(int index, GuiElementBase element)
		{ insert(index, element, new LayoutAlignment.Fixed()); }
	
	public void insertWeighted(int index, GuiElementBase element)
		{ insertWeighted(index, element, 1.0); }
	public void insertWeighted(int index, GuiElementBase element, double weight)
		{ insertWeighted(index, element, weight, 0); }
	public void insertWeighted(int index, GuiElementBase element, double weight, int minSize)
		{ insert(index, element, new LayoutAlignment.Weighted(weight, minSize)); }
	
	protected void insert(int index, GuiElementBase element, LayoutAlignment alignment) {
		if (element.getContext() != null)
			throw new UnsupportedOperationException("The specified element already has a context set");
		element.setAlign(direction, alignment);
		super.insert(index, element);
	}
	
	
	@Override
	public void onChildSizeChanged(GuiElementBase element, Direction direction) {
		super.onChildSizeChanged(element, direction);
		if (direction == this.direction) updateChildSizes(direction);
	}
	
	@Override
	public void onChildAlignChanged(GuiElementBase element, Direction direction) {
		if ((direction == this.direction) && !(getAlign(direction) instanceof LayoutAlignment))
			throw new UnsupportedOperationException(
				"Unsupported Alignment '" + getAlign(direction).getClass() + "' in GuiLayout");
		super.onChildAlignChanged(element, direction);
	}
	
	@Override
	public int getChildPos(GuiElementBase element, Direction direction) {
		return (direction == this.direction)
			? getPaddingMin(direction) + ((LayoutAlignment)element.getAlign(direction))._childPos
			: super.getChildPos(element, direction);
	}
	
	
	@Override
	protected void updateChildSizes(Direction direction) {
		if (direction != this.direction)
			{ super.updateChildSizes(direction); return; }
		
		int[] spacing = getSpacing();
		double remainingWeight = 0.0;
		int availableSize = getSize(direction) - getPadding(direction);
		for (int i = 0; i < children.size(); i++) {
			GuiElementBase child = children.get(i);
			
			Alignment align = child.getAlign(direction);
			if (align instanceof LayoutAlignment.Weighted) {
				LayoutAlignment.Weighted weighted = (LayoutAlignment.Weighted)align;
				remainingWeight += weighted.weight;
				availableSize   -= weighted.minSize;
			} else availableSize -= child.getSize(direction);
			
			if (i < children.size() - 1)
				availableSize -= spacing[Math.min(spacing.length - 1, i)];
		}
		
		int currentPos = 0;
		for (int i = 0; i < children.size(); i++) {
			GuiElementBase child = children.get(i);
			
			int size = 0;
			LayoutAlignment align = (LayoutAlignment)child.getAlign(direction);
			if (align instanceof LayoutAlignment.Weighted) {
				LayoutAlignment.Weighted weighted = (LayoutAlignment.Weighted)align;
				size = Math.max(0, (int)(availableSize * weighted.weight / remainingWeight));
				remainingWeight -= weighted.weight;
				availableSize   -= size;
				size += weighted.minSize;
				child.setSize(direction, size);
			} else size = child.getSize(direction);
			align._childPos = currentPos;
			currentPos += size;
			
			if (i < children.size() - 1)
				currentPos += spacing[Math.min(spacing.length - 1, i)];
		}
		
		if (doesExpand(direction))
			setSize(direction, currentPos + getPadding(direction));
	}
	
	
	public static abstract class LayoutAlignment extends Alignment {
		
		private int _childPos;
		private LayoutAlignment() {  }
		
		public static final class Fixed extends LayoutAlignment {  }
		
		public static final class Weighted extends LayoutAlignment {
			public final double weight;
			public final int minSize;
			public Weighted(double weight, int minSize)
				{ this.weight = weight; this.minSize = minSize; }
			@Override public boolean canExpand() { return false; }
		}
		
	}
	
} @SideOnly(Side.CLIENT)
public class GuiButton extends GuiElementBase {
	
	public static final ResourceLocation BUTTON_TEX = new ResourceLocation("textures/gui/widgets.png");
	public static final int DEFAULT_WIDTH  = 200;
	public static final int DEFAULT_HEIGHT = 20;
	public static final int MIN_TEXT_PADDING     = 6;
	public static final int DEFAULT_TEXT_PADDING = 20;
	
	
	private String _text     = "";
	private int _textColor   = -1;
	private Runnable _action = null;
	
	private boolean _isSwitch = false;
	private boolean _switchOn = false;
	
	public GuiButton() { this(DEFAULT_WIDTH); }
	public GuiButton(int width) { this(width, DEFAULT_HEIGHT); }
	public GuiButton(int width, int height) { this(width, height, ""); }
	
	public GuiButton(String text)
		{ this(getStringWidth(text) + DEFAULT_TEXT_PADDING, text); }
	public GuiButton(int width, String text)
		{ this(width, DEFAULT_HEIGHT, text); }
	public GuiButton(int width, int height, String text)
		{ this(0, 0, width, height, text); }
	
	public GuiButton(int x, int y, int width, int height, String text)
		{ setPosition(x, y); setSize(width, height); setText(text); }
	
	
	public String getText() { return _text; }
	public void setText(String value) {
		if (value == null) throw new NullPointerException("Argument can't be null");
		_text = value;
	}
	public final void addText(String value) {
		if (value == null) throw new NullPointerException("Argument can't be null");
		setText(getText() + value);
	}
	
	public int getTextColor() { return getTextColor(false); }
	public int getTextColor(boolean isHighlighted) {
		return (_textColor >= 0) ? _textColor
			: !isEnabled() ? COLOR_CONTROL_DISABLED
			: (isHighlighted) ? COLOR_CONTROL_HIGHLIGHT
			: COLOR_CONTROL;
	}
	public final void unsetTextColor() { setTextColor(-1); }
	public void setTextColor(int value) { _textColor = value; }
	
	public boolean isSwitch() { return _isSwitch; }
	public boolean isSwitchOn() { return _switchOn; }
	public void setSwitch() { setSwitch(false); }
	public void setSwitch(boolean value) { _isSwitch = true; _switchOn = value; }
	
	public void setAction(Runnable value) { _action = value; }
	
	
	@Override
	public boolean canPress() { return true; }
	
	@Override
	public void onPressed(int mouseX, int mouseY) {
		if (!isEnabled()) return;
		playPressSound();
		if (isSwitch()) setSwitch(!isSwitchOn());
		if (_action != null) _action.run();
	}
	
	public void playPressSound() {
		getMC().getSoundHandler().playSound(
			PositionedSoundRecord.getMasterRecord(SoundEvents.UI_BUTTON_CLICK, 1.0F));
	}
	
	
	// Drawing
	
	@Override
	public void draw(int mouseX, int mouseY, float partialTicks) {
		if (!isVisible()) return;
		boolean isHighlighted = isHighlighted(mouseX, mouseY);
		if (isSwitchOn()) {
			GlStateManager.disableCull();
			GlStateManager.pushMatrix();
			GlStateManager.translate(0, getHeight(), 0);
			GlStateManager.scale(1, -1, 1);
			drawButtonBackground(isHighlighted, partialTicks);
			GlStateManager.popMatrix();
			GlStateManager.enableCull();
			
			GlStateManager.pushMatrix();
			GlStateManager.translate(0, 1, 0);
			drawButtonForeground(isHighlighted, partialTicks);
			GlStateManager.popMatrix();
		} else {
			drawButtonBackground(isHighlighted, partialTicks);
			drawButtonForeground(isHighlighted, partialTicks);
		}
	}
	
	/** Draws the actual button texture. */
	protected void drawButtonBackground(boolean isHighlighted, float partialTicks) {
		int buttonIndex = !isEnabled() ? 0
		                : !isHighlighted ? 1
		                : 2;
		int ty = 46 + buttonIndex * 20;
		GuiUtils.drawContinuousTexturedBox(BUTTON_TEX, 0, 0, 0, ty, getWidth(), getHeight(),
		                                   DEFAULT_WIDTH, DEFAULT_HEIGHT, 2, 3, 2, 2, 0);
	}
	
	/** Draws the content on the button, such as text. */
	protected void drawButtonForeground(boolean isHighlighted, float partialTicks) {
		String text = getText();
		if (text.isEmpty()) return;
		FontRenderer fontRenderer = getFontRenderer();
		
		int textWidth = fontRenderer.getStringWidth(text);
		int maxTextWidth = getWidth() - MIN_TEXT_PADDING;
		if ((textWidth > maxTextWidth) && (textWidth > ELLIPSIS_WIDTH)) {
			text = fontRenderer.trimStringToWidth(text, maxTextWidth - ELLIPSIS_WIDTH).trim() + ELLIPSIS;
			textWidth = fontRenderer.getStringWidth(text);
		}
		
		fontRenderer.drawStringWithShadow(text, getWidth() / 2 - textWidth / 2,
		                                        (getHeight() - 8) / 2, getTextColor(isHighlighted));
	}
	
} @SideOnly(Side.CLIENT)
public class GuiButtonGlyph extends GuiButton {
	
	public static final int GLYPH_SPACING = getStringWidth(" ");
	public static final int DEFAULT_WIDTH = DEFAULT_HEIGHT;
	public static final float DEFAULT_GLYPH_SCALE = 2.0f;
	
	private String _glyph;
	private float _glyphScale;
	private int _glyphWidth;
	
	
	public GuiButtonGlyph(String glyph)
		{ this(glyph, DEFAULT_GLYPH_SCALE); }
	public GuiButtonGlyph(String glyph, float glyphScale)
		{ this(DEFAULT_WIDTH, glyph, glyphScale); }
	public GuiButtonGlyph(int width, String glyph, float glyphScale)
		{ this(width, DEFAULT_HEIGHT, glyph, glyphScale); }
	public GuiButtonGlyph(int width, int height, String glyph, float glyphScale)
		{ this(width, height, glyph, glyphScale, ""); }
	
	public GuiButtonGlyph(String glyph, String text)
		{ this((int)(getStringWidth(glyph) * 2.0f) + GLYPH_SPACING + getStringWidth(text) + DEFAULT_TEXT_PADDING, glyph, text); }
	public GuiButtonGlyph(int width, String glyph, String text)
		{ this(width, DEFAULT_HEIGHT, glyph, text); }
	public GuiButtonGlyph(int width, int height, String glyph, String text)
		{ this(width, height, glyph, DEFAULT_GLYPH_SCALE, text); }
	
	public GuiButtonGlyph(int width, String glyph, float glyphScale, String text)
		{ this(width, DEFAULT_HEIGHT, glyph, glyphScale, text); }
	public GuiButtonGlyph(int width, int height, String glyph, float glyphScale, String text)
		{ this(0, 0, width, height, glyph, glyphScale, text); }
	public GuiButtonGlyph(int x, int y, int width, int height, String glyph, float glyphScale, String text)
		{ super(x, y, width, height, text); setGlyph(glyph, glyphScale); }
	
	
	public String getGlyph() { return _glyph; }
	public float getGlyphScale() { return _glyphScale; }
	public int getGlyphWidth() { return _glyphWidth; }
	
	public void setGlyph(String glyph) { setGlyph(glyph, getGlyphScale()); }
	public void setGlyph(String glyph, float glyphScale) {
		_glyph = glyph;
		_glyphScale = glyphScale;
		_glyphWidth = (int)(getStringWidth(glyph) * glyphScale);
	}
	
	
	@Override
	protected void drawButtonForeground(boolean isHighlighted, float partialTicks) {
		String text = getText();
		FontRenderer fontRenderer = getFontRenderer();
		
		int contentWidth = getGlyphWidth();
		if (!text.isEmpty()) {
			int textWidth = fontRenderer.getStringWidth(text);
			int maxTextWidth = getWidth() - getGlyphWidth() - GLYPH_SPACING - MIN_TEXT_PADDING;
			if ((textWidth > maxTextWidth) && (textWidth > ELLIPSIS_WIDTH)) {
				text = fontRenderer.trimStringToWidth(text, maxTextWidth - ELLIPSIS_WIDTH).trim() + ELLIPSIS;
				textWidth = fontRenderer.getStringWidth(text);
			}
			contentWidth += GLYPH_SPACING + textWidth;
		}
		
		float scale = getGlyphScale();
		GlStateManager.pushMatrix();
		GlStateManager.scale(scale, scale, 1.0F);
		fontRenderer.drawStringWithShadow(getGlyph(),
			(int)((getWidth() - contentWidth) / 2 / scale),
			(int)((((getHeight() - 8) / scale / 2) - 1) / scale), getTextColor(isHighlighted));
		GlStateManager.popMatrix();
		
		if (!text.isEmpty())
			fontRenderer.drawStringWithShadow(text,
				(getWidth() - contentWidth) / 2 + getGlyphWidth() + GLYPH_SPACING,
				(getHeight() - 8) / 2, getTextColor(isHighlighted));
	}
	
} public class Status {
	
	public static final Status NONE = FINE();
	public static final Status EMPTY = ERROR();
	public static final Status INVALID = ERROR();
	public static Status OUT_OF_RANGE(Object min, Object max)
		{ return ERROR("general", "outOfRange", min, max); }
	public static Status REQUIRED(Setting<?> value)
		{ return ERROR("general", "required", value.getFullName()); }
	public static Status RECOMMENDED(Setting<?> value, String key)
		{ return HINT("general", key, value.getFullName()); }
	
	
	public final Severity severity;
	private final String _translateKey;
	private final Object[] _translateParams;
	
	private Status(Severity severity, String translateKey, Object[] translateParams)
		{ this.severity = severity; _translateKey = translateKey; _translateParams = translateParams; }
	
	
	private static Status constructStatus(Severity severity, String category, String key, Object... args)
		{ return new Status(severity, "config." + WearableBackpacks.MOD_ID + "." + category + ".status." + key, args); }
	
	public static Status FINE() { return new Status(Severity.FINE, null, null); }
	public static Status FINE(String category, String key, Object... args)
		{ return constructStatus(Severity.FINE, category, key, args); }
	
	public static Status HINT() { return new Status(Severity.HINT, null, null); }
	public static Status HINT(String category, String key, Object... args)
		{ return constructStatus(Severity.HINT, category, key, args); }
	
	public static Status WARN() { return new Status(Severity.WARN, null, null); }
	public static Status WARN(String category, String key, Object... args)
		{ return constructStatus(Severity.WARN, category, key, args); }
	
	public static Status ERROR() { return new Status(Severity.ERROR, null, null); }
	public static Status ERROR(String category, String key, Object... args)
		{ return constructStatus(Severity.ERROR, category, key, args); }
	
	
	
	public static Severity getSeverity(List<Status> status) {
		return status.stream()
			.map(s -> s.severity)
			.max(Severity::compareTo)
			.orElse(Severity.FINE);
	}
	
	@SideOnly(Side.CLIENT)
	public static List<String> getMessage(List<Status> status) {
		return status.stream()
			.filter(s -> (s._translateKey != null))
			.sorted(Comparator.comparingInt(s -> -s.severity.ordinal()))
			.map(s -> "\u00a7" + s.severity.colorChar +
			          I18n.format(s._translateKey, s._translateParams))
			.collect(Collectors.toList());
	}
	
	
	public enum Severity {
		FINE('f', 0x00000000),
		HINT('2', 0x2000D000),
		WARN('e', 0x30E0D000),
		ERROR('c', 0x40D00000);
		
		public final char colorChar;
		public final int foregroundColor;
		public final int backgroundColor;
		public final int guiIconIndex;
		
		private Severity(char chr, int background) {
			colorChar = chr;
			foregroundColor = 0xFF000000 | GuiUtils.getColorCode(chr, true);
			backgroundColor = background;
			guiIconIndex = ordinal();
		}
	}
	
} public enum ChangeRequiredAction {
	None,
	RejoinWorld,
	RestartMinecraft
} public enum Severity {
	FINE('f', 0x00000000),
	HINT('2', 0x2000D000),
	WARN('e', 0x30E0D000),
	ERROR('c', 0x40D00000);
		
	public final char colorChar;
	public final int foregroundColor;
	public final int backgroundColor;
	public final int guiIconIndex;
		
	private Severity(char chr, int background) {
		colorChar = chr;
		foregroundColor = 0xFF000000 | GuiUtils.getColorCode(chr, true);
		backgroundColor = background;
		guiIconIndex = ordinal();
	}
} public final class LangUtils {
	
	private LangUtils() {  }
	
	
	/** Formats a language key and returns it as a list. */
	@SideOnly(Side.CLIENT)
	public static List<String> format(String langKey, Object... args)
		{ return formatPrepend("", langKey, args); }
	/** Formats a language key and adds it to the specified
	 *  list, prepending each line with the specified string. */
	@SideOnly(Side.CLIENT)
	public static void format(List<String> list, String langKey, Object... args)
		{ formatPrepend(list, "", langKey, args); }
	
	/** Formats a language key and returns it as a list. */
	@SideOnly(Side.CLIENT)
	public static List<String> formatPrepend(String prepend, String langKey, Object... args) {
		ArrayList<String> list = new ArrayList<String>();
		formatPrepend(list, prepend, langKey, args);
		return list;
	}
	/** Formats a language key and adds it to the specified
	 *  list, prepending each line with the specified string. */
	@SideOnly(Side.CLIENT)
	public static void formatPrepend(List<String> list, String prepend, String langKey, Object... args) {
		String translated = I18n.format(langKey, args);
		for (String line : translated.split("\\\\n"))
			list.add(prepend + line);
	}
	
	
	/** Formats a tooltip translation key (<code> "tooltip.modid.key" </code>)
	 *  and adds it to the tooltip list. Used in {@link Item#addInformation}. */
	@SideOnly(Side.CLIENT)
	public static void formatTooltip(List<String> tooltip, String tooltipKey, Object... args)
		{ format(tooltip, "tooltip." + WearableBackpacks.MOD_ID + "." + tooltipKey, args); }
	/** Formats a tooltip translation key (<code> "tooltip.modid.key" </code>)
	 *  and adds it to the tooltip list, prepending each line with the specified
	 *  string. Used in {@link Item#addInformation}. */
	@SideOnly(Side.CLIENT)
	public static void formatTooltipPrepend(List<String> tooltip, String prepend, String tooltipKey, Object... args)
		{ formatPrepend(tooltip, prepend, "tooltip." + WearableBackpacks.MOD_ID + "." + tooltipKey, args); }
	
	/** Formats a tooltip translation key containing a single
	 *  key binding argument and adds it to the tooltip list.
	 *  Doesn't do anything if key is unbound. */
	@SideOnly(Side.CLIENT)
	public static void formatTooltipKey(List<String> tooltip, String langKey, KeyBinding keyBinding) {
		if (keyBinding.getKeyCode() == Keyboard.KEY_NONE) return;
		formatTooltip(tooltip, langKey, "\u00A76" + keyBinding.getDisplayName() + "\u00A77");
	}
	
	/** If shift is not held down, adds a formatted "Hold SHIFT for more info" tooltip
	 *  to the tooltip list and returns false. Otherwise returns true, adding nothing. */
	@SideOnly(Side.CLIENT)
	public static boolean tooltipIsShiftKeyDown(List<String> tooltip) {
		boolean shift = GuiScreen.isShiftKeyDown();
		if (!shift) formatTooltip(tooltip, "moreInfo", "\u00A76SHIFT\u00A77");
		return shift;
	}
	
	
	/** Formats a chat translation key (<code> "chat.modid.key" </code>) and sends
	 *  it to the player. Can also be used client-side to display a chat message. */
	public static void chatMessage(EntityPlayer player, String langKey, Object... args) {
		player.sendMessage(new TextComponentTranslation(
			"chat." + WearableBackpacks.MOD_ID + "." + langKey, args));
	}
	/** Formats a chat translation key (<code> "chat.modid.key" </code>) and displays it. */
	@SideOnly(Side.CLIENT)
	public static void displayChatMessage(String langKey, Object... args) {
		chatMessage(ClientUtils.getPlayer(), langKey, args);
	}
	
}
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.text.TextFormatting;
import net.minecraftforge.fml.client.config.GuiUtils;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import net.mcft.copy.backpacks.WearableBackpacks;
import net.mcft.copy.backpacks.client.GuiTextureResource;
import net.mcft.copy.backpacks.client.gui.Direction;
import net.mcft.copy.backpacks.client.gui.GuiElementBase;
import net.mcft.copy.backpacks.client.gui.GuiImage;
import net.mcft.copy.backpacks.client.gui.GuiLabel;
import net.mcft.copy.backpacks.client.gui.GuiLayout;
import net.mcft.copy.backpacks.client.gui.control.GuiButton;
import net.mcft.copy.backpacks.client.gui.control.GuiButtonGlyph;
import net.mcft.copy.backpacks.config.Status;
import net.mcft.copy.backpacks.config.Setting.ChangeRequiredAction;
import net.mcft.copy.backpacks.config.Status.Severity;
import net.mcft.copy.backpacks.misc.util.LangUtils;
package net.mcft.copy.backpacks.client.gui.config;





@SideOnly(Side.CLIENT)
public abstract class BaseEntry extends GuiLayout implements IConfigEntry {
	
	public static final GuiTextureResource TEXTURE_CONFIG_ICONS = new GuiTextureResource("config_icons", 64, 64);
	
	public final GuiImage iconStatus;
	public final GuiLabel label;
	public final GuiButton buttonUndo;
	public final GuiButton buttonReset;
	
	public BaseEntry() {
		super(Direction.HORIZONTAL);
		setFillHorizontal();
		
		iconStatus = new GuiImage(16, 16, TEXTURE_CONFIG_ICONS);
		iconStatus.setCenteredVertical();
		
		label = new GuiLabel("");
		label.setCenteredVertical();
		label.setShadowDisabled();
		
		buttonUndo = new GuiButtonGlyph(DEFAULT_ENTRY_HEIGHT, DEFAULT_ENTRY_HEIGHT, GuiUtils.UNDO_CHAR, 1.0f);
		buttonUndo.setCenteredVertical();
		buttonUndo.setAction(this::undoChanges);
		
		buttonReset = new GuiButtonGlyph(DEFAULT_ENTRY_HEIGHT, DEFAULT_ENTRY_HEIGHT, GuiUtils.RESET_CHAR, 1.0f);
		buttonReset.setCenteredVertical();
		buttonReset.setAction(this::setToDefault);
	}
	
	public boolean hasLabel() { return (label.getParent() == this); }
	
	public void setLabel(String languageKey)
		{ label.setText(I18n.format(languageKey)); }
	public void setLabelAndTooltip(String languageKey)
		{ setLabelAndTooltip(languageKey, null, null); }
	public void setLabelAndTooltip(String languageKey, String def, String warn) {
		languageKey = "config." + WearableBackpacks.MOD_ID + "." + languageKey;
		setLabel(languageKey);
		label.setTooltip(formatTooltip(languageKey, languageKey + ".tooltip", def, warn));
	}
	protected List<String> formatTooltip(String title, String text, String def, String warn) {
		List<String> tooltip = new ArrayList<String>();
		tooltip.add(TOOLTIP_TITLE + I18n.format(title));
		if (text != null) {
			LangUtils.format(tooltip, text);
			if (text.equals(tooltip.get(1))) tooltip.remove(1); // Remove non-translated text.
			else tooltip.set(1, TOOLTIP_TEXT + tooltip.get(1)); // Only first line should be yellow.
		}
		if (def != null)  tooltip.add(TOOLTIP_DEFAULT + I18n.format("fml.configgui.tooltip.default", def));
		if (warn != null) tooltip.add(TOOLTIP_WARN + "[" + I18n.format(warn) + "]");
		return tooltip;
	}
	
	/** Returns a list of statuses for this entry. These include
	 *  hints, warnings and errors about the current entry value. */
	public abstract List<Status> getStatus();