001package squidpony.squidgrid.gui.gdx;
002
003import com.badlogic.gdx.graphics.Color;
004import com.badlogic.gdx.scenes.scene2d.Group;
005import squidpony.panel.IColoredString;
006import squidpony.panel.ICombinedPanel;
007import squidpony.panel.ISquidPanel;
008
009import java.util.LinkedList;
010import java.util.List;
011
012
013/**
014 * An implementation of {@link ICombinedPanel} that extends libGDX's Group.
015 * If you're a new user or need only a foreground and background, it's
016 * likely what you should use.
017 *
018 * this is a concrete implementation of {@link ICombinedPanel} that you should
019 * use if you're concretely in need of a panel to display/write to, without
020 * doing fancy GUI stuff. Because it extends libGDX's {@link Group}, it
021 * offers a lot of features.
022 * 
023 * @see SquidLayers for a more advanced Group that supports multiple layers.
024 * @author smelC
025 */
026public class GroupCombinedPanel<T> extends Group implements ICombinedPanel<T> {
027
028    protected/* @Nullable */ISquidPanel<T> bg;
029    protected/* @Nullable */ISquidPanel<T> fg;
030
031    /** The width, in cell sizes */
032    protected int gridWidth = -1;
033
034    /** The height, in cell sizes */
035    protected int gridHeight = -1;
036
037    /**
038     * @param bg
039     *            The backing background panel. Typically a SquidPanel from this package.
040     * @param fg
041     *            The backing foreground panel. Typically a SquidPanel from this package.
042     * @param gridWidth
043     *            The width of this panel, used for {@link #fillBG(Object)}
044     *            (so that it fills within {@code [0, width)}).
045     * @param gridHeight
046     *            The height of this panel, used for {@link #fillBG(Object)}
047     *            (so that it fills within {@code [0, height)}).
048     * @throws IllegalStateException
049     *             In various cases of errors regarding sizes of panels.
050     */
051    public GroupCombinedPanel(ISquidPanel<T> bg, ISquidPanel<T> fg,
052                              int gridWidth, int gridHeight) {
053        if (bg.gridWidth() != fg.gridWidth())
054            throw new IllegalStateException(
055                    "Cannot build a combined panel with backers of different widths");
056        if (bg.gridHeight() != fg.gridHeight())
057            throw new IllegalStateException(
058                    "Cannot build a combined panel with backers of different heights");
059
060        this.bg = bg;
061        this.fg = fg;
062        if (gridWidth < 0)
063            throw new IllegalStateException("Cannot create a panel with a negative width");
064        this.gridWidth = gridWidth;
065        if (gridHeight < 0)
066            throw new IllegalStateException("Cannot create a panel with a negative height");
067        this.gridHeight = gridHeight;
068
069        addActors();
070    }
071
072    /**
073     * Constructor that defer providing the backing panels. Useful for
074     * subclasses that compute their size after being constructed. Use
075     * {@link #setPanels(ISquidPanel, ISquidPanel)} to set the panels
076     * (required before calling any {@code put} method).
077     *
078     * <p>
079     * Width and height are computed using the provided panels.
080     * </p>
081     */
082    public GroupCombinedPanel() {
083    }
084
085    /**
086     * Sets the backing panels.
087     *
088     * @param bg Typically a SquidPanel from this package.
089     * @param fg Typically a SquidPanel from this package.
090     */
091    public void setPanels(ISquidPanel<T> bg, ISquidPanel<T> fg) {
092        if (this.bg != null)
093            throw new IllegalStateException("Cannot change the background panel");
094        this.bg = bg;
095
096        if (this.fg != null)
097            throw new IllegalStateException("Cannot change the foreground panel");
098        this.fg = fg;
099
100        if (bg.gridWidth() != fg.gridWidth())
101            throw new IllegalStateException(
102                    "Cannot build a combined panel with backers of different widths");
103        if (bg.gridHeight() != fg.gridHeight())
104            throw new IllegalStateException(
105                    "Cannot build a combined panel with backers of different heights");
106
107        this.gridWidth = bg.gridWidth();
108        this.gridHeight = bg.gridHeight();
109
110        addActors();
111    }
112
113    @Override
114    public void putFG(int x, int y, char c) {
115        checkFG();
116        fg.put(x, y, c);
117    }
118
119    @Override
120    public void putFG(int x, int y, char c, T color) {
121        checkFG();
122        fg.put(x, y, c, color);
123    }
124
125    @Override
126    public void putFG(int x, int y, String string, T foreground) {
127        checkFG();
128        fg.put(x, y, string, foreground);
129    }
130
131    @Override
132    public void putFG(int x, int y, IColoredString<? extends T> cs) {
133        checkFG();
134        fg.put(x, y, cs);
135    }
136
137    @Override
138    public void putBG(int x, int y, T color) {
139        checkBG();
140        bg.put(x, y, color);
141    }
142
143    public void put(int x, int y, char c, T foreground, T background)
144    {
145        checkFG();
146        checkBG();
147        bg.put(x, y, background);
148        fg.put(x, y, c, foreground);
149    }
150    public void put(int x, int y, IColoredString<? extends T> cs, T background)
151    {
152        checkFG();
153        checkBG();
154        for (int i = x; i < cs.length() && i < gridWidth; i++) {
155            bg.put(i, y, background);
156        }
157        fg.put(x, y, cs);
158    }
159    public void put(int x, int y, String s, T foreground, T background)
160    {
161        checkFG();
162        checkBG();
163        for (int i = x; i < s.length() && i < gridWidth; i++) {
164            bg.put(i, y, background);
165        }
166        fg.put(x, y, s, foreground);
167    }
168    
169    @Override
170    public void fillBG(T color) {
171        if (gridWidth < 0 || gridHeight < 0)
172            throw new IllegalStateException("Width and height must be set before calling fillBG");
173        for (int x = 0; x < gridWidth; x++) {
174            for (int y = 0; y < gridHeight; y++)
175                putBG(x, y, color);
176        }
177    }
178
179    @Override
180    public void refresh() {
181        bg.refresh();
182        fg.refresh();
183    }
184
185    @Override
186    public List<ISquidPanel<?>> getBackers() {
187        final List<ISquidPanel<?>> backers = new LinkedList<ISquidPanel<?>>();
188        backers.add(fg.getBacker());
189        backers.add(bg.getBacker());
190        return backers;
191    }
192
193    protected void addActors() {
194        addActor((SquidPanel) bg.getBacker());
195        addActor((SquidPanel) fg.getBacker());
196    }
197
198    protected void checkFG() {
199        if (fg == null)
200            throw new NullPointerException("The foreground panel must be set before writing to it");
201    }
202
203    protected void checkBG() {
204        if (bg == null)
205            throw new NullPointerException("The background panel must be set before writing to it");
206    }
207
208    @Override
209    public String toString() {
210        return String.format("%s@%s", this.getClass().getSimpleName(), Integer.toHexString(hashCode()));
211    }
212
213}