package me.blackness.black.pane; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.Objects; import java.util.Queue; import java.util.function.BiConsumer; import java.util.function.BiFunction; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.event.inventory.InventoryInteractEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import me.blackness.black.Element; import me.blackness.black.Pane; import me.blackness.black.element.BasicElement; import me.blackness.black.req.SlotReq; import me.blackness.observer.Source; import me.blackness.observer.Target; import me.blackness.observer.source.BasicSource; /* . . .$" $o. $o. _o" .o$$o. .o$o. .o$o. .o$o. .o$$$$$ .o$$$$$ $$P `4$$$$P' .o$o. .$$| $$$ $$' $$$ $$' $$$ $$' $$$ $$$| $$$ $$$| $$$ ($o $$$: $$$ $$' $$$ """ """ """ """ """ """ """ """ """ """ """ """ " """ """ """ """ .oOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOo. ooo_ ooo ooo. ... ooo. ... ooo. .. `4ooo. .`4ooo. ooo.ooo. ooo ooo. .. $$$"$$$$ $$$| ... $$$| ... $$$$$$ .. "$$o "$$o $$$|$$$| $$$ $$$| . $$$| $$$ $$$| $$$| $$$| $$$: $$$ $$$: $$$ $$$|$$$| $$$ $$$| $$$| $$$ $$$| $o. $$$| $o. $$$| $o. $$$| $$$ $$$| $$$ $$$|$$$| $$$ $$$| $. $$$| $$$ $$$| $$$ $$$| $$$ $$$| $$$ $$$| $$$ $$$| $$$ $$$|$$$| $$$ $$$| $o. $$$| $$$ $$$| $$$ $$$| $$$ $$$| $$$ $$$| $$$ $$$| $$$ $$$|$$$| $$$ $$$| $$$ $$$| $$$ $$. $$$ $$. $$$ $$. $$$ $$$| $$$ $$$| $$$ $$$|$$$| $$$ $$. $$$ $$$: $P' `4$$$Ü'__`4$$$Ü' `4$$$Ü' $$$$$P' $$$$$P' $$$|$$$: $P' __`4$$$Ü' _ _______/∖______/ ∖______/∖______________/|________ "$P' _______/ ∖_____ _ x" personinblack | */ /** * a pane that has all the basic stuff. * * @author personinblack * @see Pane * @since 1.0.0 */ public final class BasicPane implements Pane { private static final String LOC_OUT = "The specified location [%s][%s] is out of bounds"; private final Source<Object> source; private final Element[][] paneElements; private final int locX; private final int locY; /** * ctor. * * @param locX the x location of the top left corner of this pane * @param locY the y location of the top left corner of this pane * @param height height of this pane * @param length length of this pane */ public BasicPane(final int locX, final int locY, final int height, final int length) { source = new BasicSource<>(); this.locX = locX; this.locY = locY; paneElements = new Element[height][length]; clear(); } /** * ctor. * * @param locX the x location of the top left corner of this pane * @param locY the y location of the top left corner of this pane * @param height height of this pane * @param length length of this pane * @param element element to fill the pane with * * @see #replaceAll(Element...) */ public BasicPane(final int locX, final int locY, final int height, final int length, final Element element) { this(locX, locY, height, length); replaceAll(Objects.requireNonNull(element)); } /** * ctor. * * @param locX the x location of the top left corner of this pane * @param locY the y location of the top left corner of this pane * @param height height of this pane * @param length length of this pane * @param elements elements to be added to the pane * * @see #add(Element...) */ public BasicPane(final int locX, final int locY, final int height, final int length, final Element... elements) { this(locX, locY, height, length); add(elements); } private int length() { return paneElements[0].length; } private int height() { return paneElements.length; } private Element emptyElement() { return new BasicElement( new ItemStack(Material.TNT), "emptyElement" ); } private void validate(final int inventorySize) throws IllegalArgumentException { final boolean locXFaulty = locX < 0; final boolean locYFaulty = locY < 0; final boolean heightFaulty = locY + height() > inventorySize / 9 || height() <= 0; final boolean lengthFaulty = locX + length() > 9 || length() <= 0; if (locXFaulty || locYFaulty || heightFaulty || lengthFaulty) { throw new IllegalArgumentException( String.format( "Validation for the newest created Pane failed.%n" + "locX (%s) is faulty: %s, locY (%s) is faulty: %s, " + "height (%s) is faulty: %s, length (%s) is faulty: %s", locX, locXFaulty, locY, locYFaulty, height(), heightFaulty, length(), lengthFaulty ) ); } } private boolean isWithinBounds(final int xToCheck, final int yToCheck) { return xToCheck < length() && yToCheck < height() && xToCheck >= 0 && yToCheck >= 0; } private void shiftElementAt(final int xToShift, final int yToShift) { for (int y = height() - 1; y >= 0; y--) { for (int x = length() - 1; x >= 0; x--) { if (y < yToShift || y == yToShift && x < xToShift) { continue; } else if (x + 1 < length()) { paneElements[y][x + 1] = paneElements[y][x]; } else if (y + 1 < height()) { paneElements[y + 1][0] = paneElements[y][x]; } } } paneElements[yToShift][xToShift] = emptyElement(); } private boolean forEachSlot(final BiFunction<Integer, Integer, Boolean> action) { for (int y = 0; isWithinBounds(0, y); y++) { for (int x = 0; isWithinBounds(x, y); x++) { if (action.apply(y, x)) { return true; } } } return false; } private void forEachSlot(final BiConsumer<Integer, Integer> action) { forEachSlot((y, x) -> { action.accept(y, x); return false; }); } @Override public void fill(final Element element) { fill(new Element[]{Objects.requireNonNull(element)}); this.source.notifyTargets(new Object()); } @Override public void fill(final Element... elements) { final Queue<Element> queue = new LinkedList<>( Arrays.asList(Objects.requireNonNull(elements)) ); forEachSlot((y, x) -> { if (queue.isEmpty()) { queue.addAll(Arrays.asList(elements)); } if (paneElements[y][x].is(emptyElement())) { paneElements[y][x] = queue.poll(); } }); this.source.notifyTargets(new Object()); } @Override public void clear() { replaceAll(emptyElement()); } @Override public boolean add(final Element element) { return forEachSlot((y, x) -> { if (paneElements[y][x].is(emptyElement())) { paneElements[y][x] = Objects.requireNonNull(element); this.source.notifyTargets(new Object()); return true; } else { return false; } }); } @Override public Element[] add(final Element... elements) { final ArrayList<Element> remainings = new ArrayList<>(); for (final Element element : Objects.requireNonNull(elements)) { if (!add(element)) { remainings.add(element); } } return remainings.toArray(new Element[0]); } @Override public void insert(final Element element, final int locX, final int locY, final boolean shift) throws IllegalArgumentException { if (isWithinBounds(locX, locY)) { if (shift && !paneElements[locY][locX].is(emptyElement())) { shiftElementAt(locX, locY); } paneElements[locY][locX] = Objects.requireNonNull(element); } else { throw new IllegalArgumentException( String.format( LOC_OUT, locX, locY ) ); } this.source.notifyTargets(new Object()); } @Override public void replaceAll(final Element... elements) { final Queue<Element> queue = new LinkedList<>( Arrays.asList(Objects.requireNonNull(elements)) ); forEachSlot((y, x) -> { if (queue.isEmpty()) { queue.addAll(Arrays.asList(elements)); } paneElements[y][x] = queue.poll(); }); this.source.notifyTargets(new Object()); } @Override public void remove(final int locX, final int locY) throws IllegalArgumentException { if (isWithinBounds(locX, locY)) { paneElements[locY][locX] = emptyElement(); this.source.notifyTargets(new Object()); } else { throw new IllegalArgumentException( String.format( LOC_OUT, locX, locY ) ); } } @Override public void subscribe(final Target<Object> target) { source.subscribe(Objects.requireNonNull(target)); } @Override public boolean contains(final ItemStack icon) { return forEachSlot((y, x) -> { return paneElements[y][x].is(icon); }); } @Override public void accept(final InventoryInteractEvent event) { forEachSlot((y, x) -> { if (new SlotReq(locX + x + (locY + y) * 9).control(event)) { paneElements[y][x].accept(event); } }); } @Override public void displayOn(final Inventory inventory) { try { validate(inventory.getSize()); } catch (IllegalArgumentException ex) { Bukkit.getLogger().severe(ex.toString()); return; } forEachSlot((y, x) -> { final Element element = paneElements[y][x]; if (!element.is(emptyElement())) { element.displayOn(inventory, locX + x, locY + y); } }); } }