import { Element } from '../index'
import { expect } from 'chai'
import arc from './tests/arc'
import arcTo from './tests/arcTo'
import arcTo2 from './tests/arcTo2'
import arcToScaled from './tests/arcToScaled'
import emptyArc from './tests/emptyArc'
import ellipse from './tests/ellipse'
import ellipse2 from './tests/ellipse2'
import fillstyle from './tests/fillstyle'
import globalAlpha from './tests/globalalpha'
import gradient from './tests/gradient'
import linecap from './tests/linecap'
import linewidth from './tests/linewidth'
import scaledLine from './tests/scaledLine'
import rgba from './tests/rgba'
import rotate from './tests/rotate'
import saveandrestore from './tests/saveandrestore'
import setLineDash from './tests/setLineDash'
import text from './tests/text'
import tiger from './tests/tiger'
import transform from './tests/transform'
import pattern from "./tests/pattern";

const tests = {
    tiger,
    arc,
    arcTo,
    arcTo2,
    arcToScaled,
    emptyArc,
    ellipse,
    ellipse2,
    fillstyle,
    globalAlpha,
    gradient,
    linecap,
    linewidth,
    scaledLine,
    rgba,
    rotate,
    saveandrestore,
    setLineDash,
    text,
    transform,
    pattern
};

const config = {
    pixelDensity: 3 // for 200% and 150%
}

class RenderingTester {
    constructor(name, fn) {
        this.name = name;
        this.fn = fn;
        this.width = 600;
        this.height = 600;
    }

    async test() {
        const canvas = document.createElement('canvas');
        const svgcanvas = new Element();

        [canvas, svgcanvas].forEach((canvas) => {
            canvas.width = this.width
            canvas.height = this.height
            const ctx = canvas.getContext('2d')
            this.fn(ctx)
        })

        // Pixels
        const svg = svgcanvas.toDataURL("image/svg+xml");
        const svgImage = await new Promise((resolve) => {
            var svgImage = new Image();
            svgImage.onload = function () {
                resolve(svgImage)
            }
            svgImage.src = svg;
        })
        const svgPixels = this.getPixels(svgImage);
        const canvasPixels = this.getPixels(canvas);
        const diffPixels = this.diffPixels(svgPixels, canvasPixels);
        const removeThinLinesPixels = this.removeThinLines(this.removeThinLines(diffPixels));
        const svgPixelsCount = this.countPixels(svgPixels);
        const canvasPixelsCount = this.countPixels(canvasPixels)
        const count = Math.max(svgPixelsCount, canvasPixelsCount);
        const diffPixelsCount = this.countPixels(removeThinLinesPixels);
        console.log({ fn: this.name, count, diffCount: diffPixelsCount, svgPixelsCount, canvasPixelsCount })
        if (count === 0 && diffPixelsCount === 0) {
            return 0
        }
        const diffRate = diffPixelsCount / count;
        return diffRate;
    }

    getPixels(image) {
        const canvas = document.createElement('canvas');
        const width = this.width;
        const height = this.height;
        canvas.width = width;
        canvas.height = height;
        const ctx = canvas.getContext('2d');
        ctx.drawImage(image, 0, 0, width, height);
        return ctx.getImageData(0, 0, width, height);
    }

    // count non transparent pixels
    countPixels(imgData) {
        var count = 0;
        for (var i = 3; i < imgData.data.length; i += 4) {
            if (imgData.data[i] > 0) {
                count++;
            }
        }
        return count;
    };


    diffPixels(imgData1, imgData2) {
        const canvas = document.createElement('canvas');
        const width = this.width;
        const height = this.height;
        const diffImgData = canvas.getContext('2d').getImageData(0, 0, width, height);
        for (var i = 0; i < imgData1.data.length; i += 4) {
            var indexes = [i, i + 1, i + 2, i + 3];
            indexes.forEach(function (i) {
                diffImgData.data[i] = 0;
            });
            if (indexes.some(function (i) {
                return Math.abs(imgData1.data[i] - imgData2.data[i]) > 0;
            })) {
                diffImgData.data[i + 3] = 255; // set black
            }
        }
        return diffImgData;
    }

    removeThinLines(imageData) {
        const canvas = document.createElement('canvas');
        const width = this.width;
        const height = this.height;
        canvas.width = width;
        canvas.height = height;
        const ctx = canvas.getContext('2d');
        ctx.putImageData(imageData, 0, 0);
        var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        var imgDataCopy = ctx.getImageData(0, 0, canvas.width, canvas.height);

        var getPixelIndex = function (x, y) {
            return (y * width + x) * 4 + 3;
        };

        var getPixel = function (x, y) {
            var alphaIndex = getPixelIndex(x, y);
            return imgDataCopy.data[alphaIndex];
        };

        var setPixel = function (x, y, value) {
            imgData.data[getPixelIndex(x, y)] = value;
        };

        for (var x = 1; x < width - 1; x++) {
            for (var y = 1; y < height - 1; y++) {
                if (getPixel(x, y) == 0) {
                    continue; // ignore transparents
                }
                var links = [
                    { x: x - 1, y: y - 1 },
                    { x: x, y: y - 1 },
                    { x: x + 1, y: y - 1 },
                    { x: x - 1, y: y },
                    { x: x + 1, y: y },
                    { x: x - 1, y: y + 1 },
                    { x: x, y: y + 1 },
                    { x: x + 1, y: y + 1 }
                ].map(function (p) {
                    return getPixel(p.x, p.y);
                }).filter(function (val) {
                    return val > 0; // not transparent?
                }).length;

                if (links < 5) { // is a thin line
                    setPixel(x, y, 0); // make it transparent
                }
            }
        }
        return imgData;
    }
}

describe('RenderTest', () => {
    for (let fn of Object.keys(tests)) {
        it(`should render same results for ${fn}`, async () => {
            const tester = new RenderingTester(fn, tests[fn]);
            const diffRate = await tester.test();
            expect(diffRate).to.lessThan(0.05);
        })
    }
})