001package squidpony.squidgrid.gui.gdx;
002
003import com.badlogic.gdx.assets.AssetManager;
004import com.badlogic.gdx.graphics.Color;
005import com.badlogic.gdx.graphics.Texture.TextureFilter;
006import com.badlogic.gdx.graphics.g2d.TextureRegion;
007
008import squidpony.IColorCenter;
009
010/**
011 * An almost-concrete implementation of {@link IPanelBuilder}. This class makes
012 * the assumption that font files are only available for square and even sizes.
013 * The only method to implement is {@link #fontfile(int)} that must return the
014 * name of the file where a font of a given size lives.
015 * 
016 * <p>
017 * I think this class should be generalized so that it supports cell width and
018 * cell height that are proportional (for example: a height that is the double
019 * of the width), but not to arbitrary combinations. In that case
020 * {@link #adjustCellSize(int)} would become impossible to implement.
021 * </p>
022 * 
023 * @author smelC
024 */
025public abstract class SquidPanelBuilder extends IPanelBuilder.Skeleton {
026
027        protected final /* @Nullable */ IColorCenter<Color> icc;
028        public final /* @Nullable */ AssetManager assetManager;
029
030        protected final int smallestFont;
031        protected final int largestFont;
032
033        protected final int fontOffset;
034
035        /**
036         * The color passed to {@link SquidPanel#setDefaultForeground(Color)} when
037         * building a new panel, if non-{@code null}.
038         */
039        protected /* @Nullable */ Color defaultForegroundColor;
040
041        /**
042         * @param smallestFont
043         *            The smallest font size available.
044         * @param largestFont
045         *            The largest font size available.
046         * @param fontOffset
047         *            This offset is added to the cell size when computing the font
048         *            size for a given cell size.
049         * @param icc
050         *            The color center to give to
051         *            {@link SquidPanel#setColorCenter(IColorCenter)}, or
052         *            {@code null} not to call this method.
053         * @param assetManager
054         */
055        public SquidPanelBuilder(int smallestFont, int largestFont, int fontOffset,
056                        /* @Nullable */IColorCenter<Color> icc, /* @Nullable */ AssetManager assetManager) {
057                this.icc = icc;
058                this.assetManager = assetManager;
059
060                this.smallestFont = smallestFont;
061                this.largestFont = largestFont;
062                this.fontOffset = fontOffset;
063        }
064
065        @Override
066        public SquidPanel buildScreenWide(int screenWidth, int screenHeight, int desiredCellSize,
067                        /* @Nullable */ TextCellFactory tcf) {
068                final int sz = adjustCellSize(desiredCellSize);
069                final int hcells = screenWidth / sz;
070                final int vcells = screenHeight / sz;
071                final SquidPanel result = buildByCells(hcells, vcells, sz, sz, tcf);
072                final int vmargin = screenHeight - (vcells * sz);
073                final int hmargin = screenWidth - (hcells * sz);
074                result.setPosition(hmargin / 2, vmargin / 2);
075                /* TODO smelC Draw margins ? */
076                return result;
077        }
078
079        @Override
080        public SquidPanel buildByCells(int hCells, int vCells, int cellWidth, int cellHeight,
081                        /*@Nullable*/ TextCellFactory tcf_) {
082                final TextCellFactory tcf;
083                final boolean freshTCF;
084                if (tcf_ != null && tcf_.width() == cellWidth && tcf_.height() == cellHeight) {
085                        /* Can reuse */
086                        tcf = tcf_;
087                        freshTCF = false;
088                } else {
089                        tcf = new TextCellFactory(assetManager);
090                        freshTCF = true;
091                }
092
093                if (cellWidth == cellHeight) {
094                        final int fontSize = fontSizeForCellSize(cellWidth);
095                        if (!hasFontOfSize(fontSize))
096                                throw new IllegalStateException("No font of size " + fontSize);
097
098                        final String fontFile = fontfile(fontSize);
099                        if (freshTCF) {
100                                /* Initialize it */
101                                tcf.font(fontFile);
102                                for (TextureRegion region : tcf.font().getRegions())
103                                        region.getTexture().setFilter(TextureFilter.Linear, TextureFilter.Linear);
104                                tcf.initByFont();
105                                tcf.width(cellWidth).height(cellHeight);
106                        }
107                        final SquidPanel result = new SquidPanel(hCells, vCells, tcf);
108                        if (icc != null)
109                                result.setColorCenter(icc);
110                        if (defaultForegroundColor != null)
111                                result.setDefaultForeground(defaultForegroundColor);
112                        return result;
113                } else
114                        throw new IllegalStateException("Non square cells aren't supported");
115        }
116
117        /**
118         * @param sz
119         * @return A valid cell size, as close as possible to {@code sz}.
120         */
121        @Override
122        public int adjustCellSize(int sz) {
123                int result = sz % 2 == 0 ? sz : sz - 1;
124                if (hasFontForCellOfSize(result))
125                        return result;
126                if (cellSizeTooBig(sz)) {
127                        final int offset = -2;
128                        while (0 < result) {
129                                result += offset;
130                                if (hasFontForCellOfSize(result))
131                                        return result;
132                        }
133                        throw new IllegalStateException("There's a bug in the computation of the cell size");
134                } else if (cellSizeTooSmall(result)) {
135                        final int offset;
136                        offset = 2;
137                        while (/* It's better to stop one day */ result < 512) {
138                                result += offset;
139                                if (hasFontForCellOfSize(result))
140                                        return result;
141                        }
142                        throw new IllegalStateException("There's a bug in the computation of the cell size");
143                } else
144                        throw new IllegalStateException("There's a bug in the computation of the cell size");
145
146        }
147
148        @Override
149        public boolean cellSizeTooBig(int cellSize) {
150                return largestFont < fontSizeForCellSize(cellSize);
151        }
152
153        @Override
154        public boolean cellSizeTooSmall(int cellSize) {
155                return fontSizeForCellSize(cellSize) < smallestFont;
156        }
157
158        @Override
159        public boolean hasFontForCellOfSize(int cellSize) {
160                return hasFontOfSize(fontSizeForCellSize(cellSize));
161        }
162
163        @Override
164        public int fontSizeForCellSize(int cellSize) {
165                return cellSize + fontOffset;
166        }
167
168        @Override
169        public boolean hasFontOfSize(int sz) {
170                return sz % 2 == 0 && smallestFont <= sz && sz <= largestFont;
171        }
172
173        /**
174         * @param c
175         *            The default foreground color that freshly created panels will
176         *            have. Can be {@code null} to use {@link SquidPanel}'s default.
177         */
178        public void setDefaultForegroundColor(/* @Nullable */Color c) {
179                this.defaultForegroundColor = c;
180        }
181
182        /**
183         * @param sz
184         * @return The name of the file where the font of size {@code sz} lives.
185         */
186        protected abstract String fontfile(int sz);
187}