package mm.structures;

import java.util.Arrays;
import java.util.Random;

/**
 * Square matrix (n x n) of m (colorCount) colors.
 */
public class ColorField extends Matrix {

    private int n, colorCount;
    private int[][] neighbourMask;
    private ColorPixel[] colors;

    /**
     * Initialize mm.structures.ColorField as n x n matrix using given colors array as colors.
     *
     * @param n      - Dimension of matrix.
     * @param colors - Colors to be used in field.
     */
    public ColorField(int n, ColorPixel[] colors) {
        super(n);
        init(colors, true);
    }

    private ColorField(int n, ColorPixel[] colors, int[][] neighbourMask) {
        super(n);
        this.neighbourMask = neighbourMask;
        init(colors, false);
    }

    /**
     * Initialize mm.structures.ColorField as n x n matrix using given colors array as colors.
     *
     * @param array  - Array representing the field matrix.
     * @param colors - Colors to be used in field.
     */
    private ColorField(int[][] array, ColorPixel[] colors) {
        super(array);
        colors = colors != null ? colors : new ColorPixel[0];
        init(colors, true);
    }

    /**
     * Initialize colors array and color map (array of ints representing colors).
     *
     * @param colors - Colors to be used in field.
     */
    private void init(ColorPixel[] colors, boolean calculateNeighbourMask) {
        this.n = super.n;
        this.colors = colors;
        this.colorCount = colors.length;
        // Set codes for each color.
        for (int i = 0; i < colorCount; i++) {
            colors[i].setCode(i);
        }
        if (calculateNeighbourMask) {
            // Calculate neighbourMask array in advance, used to improve performance
            //  compared to calculating indices for every `getNeighbours` lookup.
            neighbourMask = new int[super.length][8];
            for (int i = 0, index = 0; i < m; i++) {
                for (int j = 0; j < n; j++, index++) {
                    int ind = 0;
                    for (int ii = i - 1; ii <= i + 1; ii++) {
                        for (int jj = j - 1; jj <= j + 1; jj++) {
                            if ((ii != i || jj != j) && (ii >= 0 && ii < m) && (jj >= 0 && jj < n)) {
                                neighbourMask[index][ind++] = indexOf(ii, jj);
                            }
                        }
                    }
                    neighbourMask[index] = Arrays.copyOf(neighbourMask[index], ind);
                }
            }
        }
    }

    public int getN() {
        return n;
    }

    public int getColorCount() {
        return colorCount;
    }

    public ColorPixel[] getColors() {
        return colors;
    }

    /**
     * Find values of neighbours of (i, j).
     *
     * @param i - mm.structures.Matrix row.
     * @param j - mm.structures.Matrix column.
     * @return - Vector (1xn) (n = [1, 8]) of existent neighbours.
     */
    public int[] getNeighbours(int i, int j) {
        return getNeighbours(indexOf(i, j));
    }

    public int[] getNeighbours(int i) {
        int[] map = neighbourMask[i];
        int[] neighbours = new int[map.length];
        for (int ii = 0; ii < map.length; ii++) {
            neighbours[ii] = a[map[ii]];
        }
        return neighbours;
    }

    public ColorField updateNeighbours() {
        final ColorField cf = new ColorField(n, colors, neighbourMask);
        final Random r = new Random();

        for (int i = 0; i < length; i++) {
            final int[] neighbours = getNeighbours(i);
            cf.setElement(i, neighbours[r.nextInt(neighbours.length)]);
        }

        return cf;
    }

    public static ColorField GenerateField(int n, ColorPixel[] colors) {
        ColorField cf = new ColorField(n, colors);
        Random r = new Random();
        for (int i = 0; i < cf.length; i++) {
            cf.setElement(i, colors[r.nextInt(cf.colorCount)].getCode());
        }
        return cf;
    }
}

@@ -231,24 +231,60 @@
 ghbourMask;%0A
+    private int%5B%5D neighbourLengths;%0A
     private 
@@ -687,32 +687,56 @@
 %5B%5D neighbourMask
+, int%5B%5D neighbourLengths
 ) %7B%0A        supe
@@ -781,24 +781,74 @@
 ghbourMask;%0A
+        this.neighbourLengths = neighbourLengths;%0A
         init
@@ -1997,16 +1997,70 @@
 th%5D%5B8%5D;%0A
+            neighbourLengths = new int%5Bsuper.length%5D;%0A
         
@@ -2664,16 +2664,67 @@
 , ind);%0A
+                    neighbourLengths%5Bindex%5D = ind;%0A
         
@@ -3525,32 +3525,275 @@
 ghbours;%0A    %7D%0A%0A
+    private int%5B%5D getNextNeighbours() %7B%0A        int%5B%5D nextNeighbours = new int%5Blength%5D;%0A        for (int i = 0; i %3C length; i++) %7B%0A            nextNeighbours%5Bi%5D = r.nextInt(neighbourLengths%5Bi%5D);%0A        %7D%0A        return nextNeighbours;%0A    %7D%0A%0A
     public Color
@@ -3811,32 +3811,71 @@
 eNeighbours() %7B%0A
+        final Random r = new Random();%0A
         final Co
@@ -3927,27 +3927,46 @@
 bourMask
+, neighbourLengths
 );%0A
+%0A
         
 final Ra
@@ -3961,35 +3961,48 @@
     
-f
 in
-al Random r = new Random
+t%5B%5D nextNeighbours = getNextNeighbours
 ();%0A
@@ -4037,32 +4037,34 @@
  length; i++) %7B%0A
+//
             fina
@@ -4098,24 +4098,26 @@
 ghbours(i);%0A
+//
             
@@ -4172,24 +4172,91 @@
 .length)%5D);%0A
+            cf.setElement(i, getNeighbours(i)%5BnextNeighbours%5Bi%5D%5D);%0A
         %7D%0A%0A 

store neighbour lengths add method to generate next neighbours