001package squidpony.squidgrid.zone;
002
003import squidpony.squidgrid.zone.Zone.Skeleton;
004import squidpony.squidmath.Coord;
005import squidpony.squidmath.CoordPacker;
006import squidpony.squidmath.CrossHash;
007
008import java.util.*;
009
010/**
011 * A zone constructed by {@link CoordPacker}.
012 *
013 * @author smelC
014 */
015public class CoordPackerZone extends Skeleton implements Collection<Coord>, ImmutableZone {
016
017    protected final short[] shorts;
018
019    protected transient List<Coord> unpacked;
020
021    private static final long serialVersionUID = -3718415979846804238L;
022
023    public CoordPackerZone(short[] shorts) {
024        this.shorts = shorts;
025    }
026
027    @Override
028    public boolean isEmpty() {
029        return CoordPacker.isEmpty(shorts);
030    }
031
032    /**
033     * Returns <tt>true</tt> if this collection contains the specified element.
034     * More formally, returns <tt>true</tt> if and only if this collection
035     * contains at least one element <tt>e</tt> such that
036     * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>.
037     *
038     * @param o element whose presence in this collection is to be tested
039     * @return <tt>true</tt> if this collection contains the specified
040     * element
041     * @throws ClassCastException   if the type of the specified element
042     *                              is incompatible with this collection
043     *                              (<a href="#optional-restrictions">optional</a>)
044     * @throws NullPointerException if the specified element is null and this
045     *                              collection does not permit null elements
046     *                              (<a href="#optional-restrictions">optional</a>)
047     */
048    @Override
049    public boolean contains(Object o) {
050        return (o instanceof Coord) && CoordPacker.queryPacked(shorts, ((Coord) o).x, ((Coord) o).y);
051    }
052
053    /**
054     * Returns an array containing all of the elements in this collection.
055     * If this collection makes any guarantees as to what order its elements
056     * are returned by its iterator, this method must return the elements in
057     * the same order.
058     * <p>
059     * <p>The returned array will be "safe" in that no references to it are
060     * maintained by this collection.  (In other words, this method must
061     * allocate a new array even if this collection is backed by an array).
062     * The caller is thus free to modify the returned array.
063     * <p>
064     * <p>This method acts as bridge between array-based and collection-based
065     * APIs.
066     *
067     * @return an array containing all of the elements in this collection
068     */
069    @Override
070    public Object[] toArray() {
071        return CoordPacker.allPacked(shorts);
072    }
073
074    /**
075     * Returns an array containing all of the elements in this collection;
076     * the runtime type of the returned array is that of the specified array.
077     * If the collection fits in the specified array, it is returned therein.
078     * Otherwise, a new array is allocated with the runtime type of the
079     * specified array and the size of this collection.
080     * <p>
081     * <p>If this collection fits in the specified array with room to spare
082     * (i.e., the array has more elements than this collection), the element
083     * in the array immediately following the end of the collection is set to
084     * <tt>null</tt>.  (This is useful in determining the length of this
085     * collection <i>only</i> if the caller knows that this collection does
086     * not contain any <tt>null</tt> elements.)
087     * <p>
088     * <p>If this collection makes any guarantees as to what order its elements
089     * are returned by its iterator, this method must return the elements in
090     * the same order.
091     * <p>
092     * <p>Like the {@link #toArray()} method, this method acts as bridge between
093     * array-based and collection-based APIs.  Further, this method allows
094     * precise control over the runtime type of the output array, and may,
095     * under certain circumstances, be used to save allocation costs.
096     * <p>
097     * <p>Suppose <tt>x</tt> is a collection known to contain only strings.
098     * The following code can be used to dump the collection into a newly
099     * allocated array of <tt>String</tt>:
100     * <p>
101     * <pre>
102     *     String[] y = x.toArray(new String[0]);</pre>
103     * <p>
104     * Note that <tt>toArray(new Object[0])</tt> is identical in function to
105     * <tt>toArray()</tt>.
106     *
107     * @param a the array into which the elements of this collection are to be
108     *          stored, if it is big enough; otherwise, a new array of the same
109     *          runtime type is allocated for this purpose.
110     * @return an array containing all of the elements in this collection
111     * @throws ArrayStoreException  if the runtime type of the specified array
112     *                              is not a supertype of the runtime type of every element in
113     *                              this collection
114     * @throws NullPointerException if the specified array is null
115     */
116    @Override
117    @SuppressWarnings("unchecked")
118    public <T> T[] toArray(T[] a) {
119        if (a == null)
120            throw new NullPointerException("Array passed to CoordPackerZone.toArray() must not be null");
121        final int size = a.length, ssize = CoordPacker.count(shorts);
122        if (ssize == size)
123            return (T[]) CoordPacker.allPacked(shorts);
124        a = Arrays.copyOf(a, ssize);
125        for (int i = 0; i < ssize; i++) {
126            a[i] = (T) CoordPacker.nth(shorts, i);
127        }
128        return a;
129    }
130
131    /**
132     * Does nothing (this Zone is immutable).
133     */
134    @Override
135    public boolean add(Coord coord) {
136        return false;
137    }
138
139    /**
140     * Does nothing (this Zone is immutable).
141     */
142    @Override
143    public boolean remove(Object o) {
144        return false;
145    }
146
147    /**
148     * Returns <tt>true</tt> if this collection contains all of the elements
149     * in the specified collection.
150     *
151     * @param c collection to be checked for containment in this collection
152     * @return <tt>true</tt> if this collection contains all of the elements
153     * in the specified collection
154     * @throws ClassCastException if the types of one or more elements
155     *                            in the specified collection are not Coord
156     * @see #contains(Object)
157     */
158    @Override
159    @SuppressWarnings("unchecked")
160    public boolean containsAll(Collection<?> c) {
161        return CoordPacker.count(shorts) == CoordPacker.count(CoordPacker.insertSeveralPacked(shorts, (Collection) c));
162    }
163
164    /**
165     * Does nothing (this Zone is immutable).
166     */
167    @Override
168    public boolean addAll(Collection<? extends Coord> c) {
169        return false;
170    }
171
172    /**
173     * Does nothing (this Zone is immutable).
174     */
175    @Override
176    public boolean removeAll(Collection<?> c) {
177        return false;
178    }
179
180    /**
181     * Does nothing (this Zone is immutable).
182     */
183    @Override
184    public boolean retainAll(Collection<?> c) {
185        return false;
186    }
187
188    /**
189     * Does nothing (this Zone is immutable).
190     */
191    @Override
192    public void clear() {
193
194    }
195
196    @Override
197    public int size() {
198        return CoordPacker.count(shorts);
199    }
200
201    @Override
202    public boolean contains(int x, int y) {
203        return CoordPacker.regionsContain(shorts, CoordPacker.packOne(x, y));
204    }
205
206    @Override
207    public boolean contains(Coord c) {
208        return CoordPacker.regionsContain(shorts, CoordPacker.packOne(c));
209    }
210
211    @Override
212    public List<Coord> getAll() {
213        if (unpacked == null) {
214            final Coord[] allPacked = CoordPacker.allPacked(shorts);
215            unpacked = new ArrayList<Coord>(allPacked.length);
216            Collections.addAll(unpacked, allPacked);
217        }
218        return unpacked;
219    }
220
221    @Override
222    public CoordPackerZone expand(int distance) {
223        return new CoordPackerZone(CoordPacker.expand(shorts, distance, 256, 256));
224    }
225
226    @Override
227    public CoordPackerZone expand8way(int distance) {
228        return new CoordPackerZone(CoordPacker.expand(shorts, distance, 256, 256, true));
229    }
230
231    @Override
232    public boolean contains(Zone other) {
233        return CoordPacker.count(shorts) == CoordPacker.count(CoordPacker.insertSeveralPacked(shorts, other.getAll()));
234    }
235
236    @Override
237    public boolean intersectsWith(Zone other) {
238        if (other instanceof CoordPackerZone)
239            return CoordPacker.intersects(shorts, ((CoordPackerZone) other).shorts);
240        for (Coord c : other) {
241            if (CoordPacker.queryPacked(shorts, c.x, c.y))
242                return true;
243        }
244        return false;
245    }
246
247    @Override
248    public Zone extend() {
249        return new CoordPackerZone(CoordPacker.expand(shorts, 1, 256, 256, true));
250    }
251
252    @Override
253    public Collection<Coord> getInternalBorder() {
254        return new CoordPackerZone(CoordPacker.surface(shorts, 1, 256, 256, true));
255    }
256
257    @Override
258    public Collection<Coord> getExternalBorder() {
259        return new CoordPackerZone(CoordPacker.fringe(shorts, 1, 256, 256, true));
260    }
261
262    @Override
263    public Zone translate(int x, int y) {
264        return new CoordPackerZone(CoordPacker.translate(shorts, x, y, 256, 256));
265    }
266
267    @Override
268    public String toString() {
269        return (unpacked == null ? shorts : unpacked).toString();
270    }
271
272    @Override
273    public boolean equals(Object o) {
274        if (this == o) return true;
275        if (o == null || getClass() != o.getClass()) return false;
276
277        CoordPackerZone that = (CoordPackerZone) o;
278
279        return Arrays.equals(shorts, that.shorts);
280    }
281
282    @Override
283    public int hashCode() {
284        return CrossHash.hash(shorts);
285    }
286}