/*
 * This file is part of Pufferfish (https://github.com/pufferfish-gg/Pufferfish)
 *
 * Pufferfish is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Pufferfish is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Pufferfish. If not, see <https://www.gnu.org/licenses/>.
 */

package gg.pufferfish.pufferfish.simd;

import jdk.incubator.vector.FloatVector;
import jdk.incubator.vector.IntVector;
import jdk.incubator.vector.VectorMask;
import jdk.incubator.vector.VectorSpecies;
import org.bukkit.map.MapPalette;

import java.awt.*;

@Deprecated
public class VectorMapPalette {

    private static final VectorSpecies<Integer> I_SPEC = IntVector.SPECIES_PREFERRED;
    private static final VectorSpecies<Float> F_SPEC = FloatVector.SPECIES_PREFERRED;

    @Deprecated
    public static void matchColorVectorized(int[] in, byte[] out) {
        int speciesLength = I_SPEC.length();
        int i;
        for (i = 0; i < in.length - speciesLength; i += speciesLength) {
            float[] redsArr = new float[speciesLength];
            float[] bluesArr = new float[speciesLength];
            float[] greensArr = new float[speciesLength];
            int[] alphasArr = new int[speciesLength];

            for (int j = 0; j < speciesLength; j++) {
                alphasArr[j] = (in[i + j] >> 24) & 0xFF;
                redsArr[j] = (in[i + j] >> 16) & 0xFF;
                greensArr[j] = (in[i + j] >> 8) & 0xFF;
                bluesArr[j] = (in[i + j] >> 0) & 0xFF;
            }

            IntVector alphas = IntVector.fromArray(I_SPEC, alphasArr, 0);
            FloatVector reds = FloatVector.fromArray(F_SPEC, redsArr, 0);
            FloatVector greens = FloatVector.fromArray(F_SPEC, greensArr, 0);
            FloatVector blues = FloatVector.fromArray(F_SPEC, bluesArr, 0);
            IntVector resultIndex = IntVector.zero(I_SPEC);
            VectorMask<Integer> modificationMask = VectorMask.fromLong(I_SPEC, 0xffffffff);

            modificationMask = modificationMask.and(alphas.lt(128).not());
            FloatVector bestDistances = FloatVector.broadcast(F_SPEC, Float.MAX_VALUE);

            for (int c = 4; c < MapPalette.colors.length; c++) {
                // We're using 32-bit floats here because it's 2x faster and nobody will know the difference.
                // For correctness, the original algorithm uses 64-bit floats instead. Completely unnecessary.
                FloatVector compReds = FloatVector.broadcast(F_SPEC, MapPalette.colors[c].getRed());
                FloatVector compGreens = FloatVector.broadcast(F_SPEC, MapPalette.colors[c].getGreen());
                FloatVector compBlues = FloatVector.broadcast(F_SPEC, MapPalette.colors[c].getBlue());

                FloatVector rMean = reds.add(compReds).div(2.0f);
                FloatVector rDiff = reds.sub(compReds);
                FloatVector gDiff = greens.sub(compGreens);
                FloatVector bDiff = blues.sub(compBlues);

                FloatVector weightR = rMean.div(256.0f).add(2);
                FloatVector weightG = FloatVector.broadcast(F_SPEC, 4.0f);
                FloatVector weightB = FloatVector.broadcast(F_SPEC, 255.0f).sub(rMean).div(256.0f).add(2.0f);

                FloatVector distance = weightR.mul(rDiff).mul(rDiff).add(weightG.mul(gDiff).mul(gDiff)).add(weightB.mul(bDiff).mul(bDiff));

                // Now we compare to the best distance we've found.
                // This mask contains a "1" if better, and a "0" otherwise.
                VectorMask<Float> bestDistanceMask = distance.lt(bestDistances);
                bestDistances = bestDistances.blend(distance, bestDistanceMask); // Update the best distances

                // Update the result array
                // We also AND with the modification mask because we don't want to interfere if the alpha value isn't large enough.
                resultIndex = resultIndex.blend(c, bestDistanceMask.cast(I_SPEC).and(modificationMask)); // Update the results
            }

            for (int j = 0; j < speciesLength; j++) {
                int index = resultIndex.lane(j);
                out[i + j] = (byte) (index < 128 ? index : -129 + (index - 127));
            }
        }

        // For the final ones, fall back to the regular method
        for (; i < in.length; i++) {
            out[i] = MapPalette.matchColor(new Color(in[i], true));
        }
    }

}
