package com.simagis.matrix.demo.plugin3;

import net.algart.contexts.*;
import net.algart.arrays.*;
import net.algart.math.functions.*;
import com.simagis.images.color.*;
import com.simagis.plugin3.Name;

public class SimplestTutorialDemo {
    public static Image2D toGrayscale(Context context, @Name("image")Image2D image) {
        ImageContext imageContext = context.as(ImageContext.class);
        Matrix<? extends PArray> m = image.i();
        Image2D result = imageContext.newGrayscaleImage2D(imageContext, m);
        return result;
    }

    public static double meanOf_1(Context context, @Name("image")Image2D image) {
        Matrix<? extends PArray> m = image.i();
        PArray a = m.array();
        long dimX = m.dimX();
        long dimY = m.dimY();
        double sum = 0.0;
        for (long i = 0; i < dimY; i++) {
            for (long j = 0; j < dimX; j++) {
                long index = m.index(j, i);
                sum += a.getDouble(index);
            }
        }
        return sum / (dimX * dimY);
    }

    public static double meanOf_2(Context context, @Name("image")Image2D image) {
        Matrix<? extends PArray> m = image.i();
        PArray a = m.array();
        long length = a.length();
        double sum = 0.0;
        for (long k = 0; k < length; k++) {
            sum += a.getDouble(k);
        }
        return sum / length;
    }

    public static double meanOf_3(Context context, @Name("image")Image2D image) {
        ProgressUpdater pu = context.as(ProgressUpdater.class);
        InterruptionContext ic = context.as(InterruptionContext.class);
        Matrix<? extends PArray> m = image.i();
        PArray a = m.array();
        long length = a.length();
        double sum = 0.0;
        for (long k = 0; k < length; k++) {
            sum += a.getDouble(k);
            if ((k & 0xFFFF) == 0) { // equivalent to k%65536==0
                ic.checkInterruption();
                pu.updateProgress((double)(k + 1) / (double)length, false);
            }
        }
        return sum / length;
    }

    public static double meanOf_4(Context context, @Name("image")Image2D image) {
        ProgressUpdater pu = context.as(ProgressUpdater.class);
        InterruptionContext ic = context.as(InterruptionContext.class);
        Matrix<? extends PArray> m = image.i();
        PArray a = m.array();
        DataBuffer buf = a.buffer(DataBuffer.AccessMode.READ);
        double sum = 0.0;
        for (buf.map(0); buf.hasData(); buf.mapNext()) {
            ic.checkInterruption();
            pu.updateProgress((double)buf.position() / (double)a.length(), false);
            if (a instanceof BitArray) {
                long[] data = (long[])buf.data();
                sum += PackedBitArrays.cardinality(data, buf.fromIndex(), buf.toIndex());
            } else if (a instanceof CharArray) {
                char[] data = (char[])buf.data();
                for (int k = buf.from(), kMax = buf.to(); k < kMax; k++)
                    sum += data[k];
            } else if (a instanceof ByteArray) {
                byte[] data = (byte[])buf.data();
                for (int k = buf.from(), kMax = buf.to(); k < kMax; k++)
                    sum += data[k] & 0xFF;
            } else if (a instanceof ShortArray) {
                short[] data = (short[])buf.data();
                for (int k = buf.from(), kMax = buf.to(); k < kMax; k++)
                    sum += data[k] & 0xFFFF;
            } else if (a instanceof IntArray) {
                int[] data = (int[])buf.data();
                for (int k = buf.from(), kMax = buf.to(); k < kMax; k++)
                    sum += data[k];
            } else if (a instanceof LongArray) {
                long[] data = (long[])buf.data();
                for (int k = buf.from(), kMax = buf.to(); k < kMax; k++)
                    sum += data[k];
            } else if (a instanceof FloatArray) {
                float[] data = (float[])buf.data();
                for (int k = buf.from(), kMax = buf.to(); k < kMax; k++)
                    sum += data[k];
            } else if (a instanceof DoubleArray) {
                double[] data = (double[])buf.data();
                for (int k = buf.from(), kMax = buf.to(); k < kMax; k++)
                    sum += data[k];
            } else {
                throw new AssertionError("Must not occur");
            }
        }
        return sum / a.length();
    }

    public static double meanOf_5(Context context, @Name("image")Image2D image) {
        ArrayContext arrayContext = new DefaultArrayContext(context);
        Matrix<? extends PArray> m = image.i();
        PArray a = m.array();
        double sum = Arrays.sumOf(arrayContext, a);
        return sum / a.length();
    }

    public static Image2D constant_1(Context context,
        @Name("dimX")long dimX, @Name("dimY")long dimY,
        @Name("value")float value)
    {
        ArrayMemoryContext amc = context.as(ArrayMemoryContext.class);
        MemoryModel mm = amc.getMemoryModel();
        Matrix<UpdatableFloatArray> m = mm.newFloatMatrix(dimX, dimY);
        UpdatableFloatArray a = m.array();
        long length = a.length();
        for (long k = 0; k < length; k++) {
            a.setFloat(k, value);
        }
        ImageContext imageContext = context.as(ImageContext.class);
        Image2D result = imageContext.newGrayscaleImage2D(imageContext, m);
        return result;
    }

    public static Image2D constant_2(Context context,
        @Name("dimX")long dimX, @Name("dimY")long dimY,
        @Name("elementType")String elementType, @Name("value")double value)
    {
        ArrayMemoryContext amc = context.as(ArrayMemoryContext.class);
        MemoryModel mm = amc.getMemoryModel();
        ProgressUpdater pu = context.as(ProgressUpdater.class);
        InterruptionContext ic = context.as(InterruptionContext.class);
        Class<?> eType = typeOf(elementType);
        Matrix<? extends UpdatablePArray> m = mm.newMatrix(UpdatablePArray.class, eType, dimX, dimY);
        UpdatablePArray a = m.array();
        long length = a.length();
        for (long k = 0; k < length; k++) {
            a.setDouble(k, value);
            if ((k & 0xFFFF) == 0) { // equivalent to k%65536==0
                ic.checkInterruption();
                pu.updateProgress((double)k / (double)length, false);
            }
        }
        ImageContext imageContext = context.as(ImageContext.class);
        Image2D result = imageContext.newGrayscaleImage2D(imageContext, m);
        return result;
    }

    public static Image2D constant_3(Context context,
        @Name("dimX")long dimX, @Name("dimY")long dimY,
        @Name("elementType")String elementType, @Name("value")double value)
    {
//        long t1 = System.nanoTime();
        Class<?> eType = typeOf(elementType);
        if (dimX < 0)
            throw new IllegalArgumentException("Negative dimX");
        if (dimY < 0)
            throw new IllegalArgumentException("Negative dimY");
        long length = Arrays.longMul(dimX, dimY);
        if (length == Long.MIN_VALUE)
            throw new TooLargeArrayException();
        PArray a;
        if (eType == boolean.class) {
            a = Arrays.nBitCopies(length, value != 0.0);
        } else if (eType == char.class) {
            a = Arrays.nCharCopies(length, (char)value);
        } else if (eType == byte.class) {
            a = Arrays.nByteCopies(length, (byte)value);
        } else if (eType == short.class) {
            a = Arrays.nShortCopies(length, (short)value);
        } else if (eType == int.class) {
            a = Arrays.nIntCopies(length, (int)value);
        } else if (eType == long.class) {
            a = Arrays.nLongCopies(length, (long)value);
        } else if (eType == float.class) {
            a = Arrays.nFloatCopies(length, (float)value);
        } else if (eType == double.class) {
            a = Arrays.nDoubleCopies(length, (double)value);
        } else {
            throw new AssertionError("Must not occur");
        }
        Matrix<? extends PArray> m = Matrices.matrix(a, dimX, dimY);
        ImageContext imageContext = context.as(ImageContext.class);
//        long t2 = System.nanoTime();
//        System.out.println((t2 - t1) * 0.001);
        Image2D result = imageContext.newGrayscaleImage2D(imageContext, m);
        return result;
    }

    public static Image2D constant_4(Context context,
        @Name("dimX")long dimX, @Name("dimY")long dimY,
        @Name("elementType")String elementType, @Name("value")double value)
    {
        Class<?> eType = typeOf(elementType);
        Class<? extends PArray> aType = Arrays.type(PArray.class, eType);
        Func f = ConstantFunc.getInstance(value);
        Matrix<? extends PArray> m = Matrices.asCoordFuncMatrix(f, aType, dimX, dimY);
        ImageContext imageContext = context.as(ImageContext.class);
        Image2D result = imageContext.newGrayscaleImage2D(imageContext, m);
        return result;
    }

    public static Image2D constant_5(Context context,
        @Name("dimX")long dimX, @Name("dimY")long dimY,
        @Name("elementType")String elementType, @Name("value")double value)
    {
        ArrayContext arrayContext = new DefaultArrayContext(context);
        MemoryModel mm = arrayContext.getMemoryModel();
        Class<?> eType = typeOf(elementType);
        Class<? extends PArray> aType = Arrays.type(PArray.class, eType);
        Func f = ConstantFunc.getInstance(value);
        Matrix<? extends PArray> lazy = Matrices.asCoordFuncMatrix(f, aType, dimX, dimY);
        Matrix<? extends UpdatablePArray> m = mm.newMatrix(UpdatablePArray.class,
            lazy.elementType(), lazy.dimensions());
        Matrices.copy(arrayContext, m, lazy);
        ImageContext imageContext = context.as(ImageContext.class);
        Image2D result = imageContext.newGrayscaleImage2D(imageContext, m);
        return result;
    }

    public static Image2D constant_6(Context context,
        @Name("dimX")long dimX, @Name("dimY")long dimY,
        @Name("elementType")String elementType,
        @Name("red")double r, @Name("green")double g, @Name("blue")double b)
    {
        ArrayContext arrayContext = new DefaultArrayContext(context);
        MemoryModel mm = arrayContext.getMemoryModel();
        Class<?> eType = typeOf(elementType);
        long[] dim = new long[] {dimX, dimY};
        Class<? extends PArray> aType = Arrays.type(PArray.class, eType);
        Matrix<? extends PArray>
            lazyr = Matrices.asCoordFuncMatrix(ConstantFunc.getInstance(r), aType, dim),
            lazyg = Matrices.asCoordFuncMatrix(ConstantFunc.getInstance(g), aType, dim),
            lazyb = Matrices.asCoordFuncMatrix(ConstantFunc.getInstance(b), aType, dim);
        Matrix<? extends UpdatablePArray> mr = mm.newMatrix(UpdatablePArray.class, eType, dim);
        Matrices.copy(arrayContext.part(0.0, 1.0 / 3.0), mr, lazyr);
        Matrix<? extends UpdatablePArray> mg = mm.newMatrix(UpdatablePArray.class, eType, dim);
        Matrices.copy(arrayContext.part(1.0 / 3.0, 2.0 / 3.0), mg, lazyg);
        Matrix<? extends UpdatablePArray> mb = mm.newMatrix(UpdatablePArray.class, eType, dim);
        Matrices.copy(arrayContext.part(2.0 / 3.0, 1.0), mb, lazyb);
        ImageContext imageContext = context.as(ImageContext.class);
        Image2D result = imageContext.newRGBImage2D(imageContext, mr, mg, mb);
        return result;
    }

    private static Class<?> typeOf(String elementType) {
        if (elementType.equalsIgnoreCase("boolean"))
            return boolean.class;
        if (elementType.equalsIgnoreCase("char"))
            return char.class;
        if (elementType.equalsIgnoreCase("byte"))
            return byte.class;
        if (elementType.equalsIgnoreCase("short"))
            return short.class;
        if (elementType.equalsIgnoreCase("int"))
            return int.class;
        if (elementType.equalsIgnoreCase("long"))
            return long.class;
        if (elementType.equalsIgnoreCase("float"))
            return float.class;
        if (elementType.equalsIgnoreCase("double"))
            return double.class;
        throw new IllegalArgumentException("Unknown type: " + elementType);
    }
}
