package com.alphawallet.app.util;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;

import java.util.Arrays;

public class Blockies {
    private static final int size = 8;
    private static long[] randSeed = new long[4];

    public static Bitmap createIcon(String address) {
        return createIcon(address, 16);
    }

    public static Bitmap createIcon(String address, int scale) {
        seedRand(address);
        HSL color = createColor();
        HSL bgColor = createColor();
        HSL spotColor = createColor();
        return createCanvas(createImageData(), color, bgColor, spotColor, scale);
    }

    private static Bitmap createCanvas(double[] imgData, HSL color, HSL bgColor, HSL spotColor, int scale) {
        int width = (int) Math.sqrt(imgData.length);

        int w = width * scale;
        int h = width * scale;

        Bitmap.Config conf = Bitmap.Config.ARGB_8888;
        Bitmap bmp = Bitmap.createBitmap(w, h, conf);
        Canvas canvas = new Canvas(bmp);

        int background = toRGB((int) bgColor.h, (int) bgColor.s, (int) bgColor.l);

        Paint paint = new Paint();
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(background);
        canvas.drawRect(0, 0, w, h, paint);

        int main = toRGB((int) color.h, (int) color.s, (int) color.l);
        int scolor = toRGB((int) spotColor.h, (int) spotColor.s, (int) spotColor.l);

        for (int i = 0; i < imgData.length; i++) {
            int row = (int) Math.floor(i / width);
            int col = i % width;
            paint = new Paint();

            paint.setColor((imgData[i] == 1.0d) ? main : scolor);

            if (imgData[i] > 0d) {
                canvas.drawRect(col * scale, row * scale, (col * scale) + scale, (row * scale) + scale, paint);
            }
        }
        return getCroppedBitmap(bmp);
    }

    private static double rand() {
        int t = (int) (randSeed[0] ^ (randSeed[0] << 11));
        randSeed[0] = randSeed[1];
        randSeed[1] = randSeed[2];
        randSeed[2] = randSeed[3];
        randSeed[3] = (randSeed[3] ^ (randSeed[3] >> 19) ^ t ^ (t >> 8));
        double t1 = Math.abs(randSeed[3]);
        return (t1 / Integer.MAX_VALUE);
    }

    private static HSL createColor() {
        double h = Math.floor(rand() * 360d);
        double s = ((rand() * 60d) + 40d);
        double l = ((rand() + rand() + rand() + rand()) * 25d);
        return new HSL(h, s, l);
    }

    private static double[] createImageData() {
        int width = size;
        int height = size;

        double dataWidth = Math.ceil(width / 2);
        double mirrorWidth = width - dataWidth;

        double[] data = new double[size * size];
        int dataCount = 0;
        for (int y = 0; y < height; y++) {
            double[] row = new double[(int) dataWidth];
            for (int x = 0; x < dataWidth; x++) {
                row[x] = Math.floor(rand() * 2.3d);

            }
            double[] r = Arrays.copyOfRange(row, 0, (int) mirrorWidth);
            r = reverse(r);
            row = concat(row, r);

            for (double v : row) {
                data[dataCount] = v;
                dataCount++;
            }
        }

        return data;
    }

    public static double[] concat(double[] a, double[] b) {
        int aLen = a.length;
        int bLen = b.length;
        double[] c = new double[aLen + bLen];
        System.arraycopy(a, 0, c, 0, aLen);
        System.arraycopy(b, 0, c, aLen, bLen);
        return c;
    }

    private static double[] reverse(double[] data) {
        for (int i = 0; i < data.length / 2; i++) {
            double temp = data[i];
            data[i] = data[data.length - i - 1];
            data[data.length - i - 1] = temp;
        }
        return data;
    }

    private static void seedRand(String seed) {
        for (int i = 0; i < Blockies.randSeed.length; i++) {
            Blockies.randSeed[i] = 0;
        }
        for (int i = 0; i < seed.length(); i++) {
            long test = Blockies.randSeed[i % 4] << 5;
            if (test > Integer.MAX_VALUE << 1 || test < Integer.MIN_VALUE << 1)
                test = (int) test;

            long test2 = test - Blockies.randSeed[i % 4];
            Blockies.randSeed[i % 4] = (test2 + Character.codePointAt(seed, i));
        }

        for (int i = 0; i < Blockies.randSeed.length; i++)
            Blockies.randSeed[i] = (int) Blockies.randSeed[i];
    }

    private static int toRGB(float h, float s, float l) {
        h = h % 360.0f;
        h /= 360f;
        s /= 100f;
        l /= 100f;

        float q = 0;

        if (l < 0.5)
            q = l * (1 + s);
        else
            q = (l + s) - (s * l);

        float p = 2 * l - q;

        float r = Math.max(0, hueToRGB(p, q, h + (1.0f / 3.0f)));
        float g = Math.max(0, hueToRGB(p, q, h));
        float b = Math.max(0, hueToRGB(p, q, h - (1.0f / 3.0f)));

        r = Math.min(r, 1.0f);
        g = Math.min(g, 1.0f);
        b = Math.min(b, 1.0f);

        int red = (int) (r * 255);
        int green = (int) (g * 255);
        int blue = (int) (b * 255);
        return Color.rgb(red, green, blue);
    }

    private static float hueToRGB(float p, float q, float h) {
        if (h < 0) h += 1;
        if (h > 1) h -= 1;
        if (6 * h < 1) {
            return p + ((q - p) * 6 * h);
        }
        if (2 * h < 1) {
            return q;
        }
        if (3 * h < 2) {
            return p + ((q - p) * 6 * ((2.0f / 3.0f) - h));
        }
        return p;
    }

    private static Bitmap getCroppedBitmap(Bitmap bitmap) {
        Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
                bitmap.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(output);

        final int color = 0xff424242;
        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());

        paint.setAntiAlias(true);
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(color);
        canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2,
                bitmap.getWidth() / 2, paint);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(bitmap, rect, rect, paint);
        return output;
    }

    static class HSL {
        double h, s, l;

        HSL(double h, double s, double l) {
            this.h = h;
            this.s = s;
            this.l = l;
        }
    }
}