package com.knecon.fforesight.service.layoutparser.server.model;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Collections;
import java.util.List;
import java.util.stream.IntStream;

import org.junit.jupiter.api.Test;

import com.knecon.fforesight.service.layoutparser.processor.model.table.CleanRulings;
import com.knecon.fforesight.service.layoutparser.processor.model.table.Ruling;

class CleanRulingsTest {

    @Test
    public void testLineBetween() {

        List<Ruling> verticals = List.of(new Ruling(new Point2D.Double(10, 0), new Point2D.Double(10, 10)));
        List<Ruling> horizontals = List.of(new Ruling(new Point2D.Double(0, 5), new Point2D.Double(10, 5)));
        CleanRulings cleanRulings = new CleanRulings(horizontals, verticals);
        Rectangle2D a = new Rectangle2D.Double(1, 6, 3, 3);
        Rectangle2D b = new Rectangle2D.Double(5, 6, 3, 3);
        Rectangle2D c = new Rectangle2D.Double(11, 6, 3, 3);
        Rectangle2D d = new Rectangle2D.Double(1, 1, 3, 3);
        Rectangle2D e = new Rectangle2D.Double(5, 1, 3, 3);
        Rectangle2D f = new Rectangle2D.Double(11, 1, 3, 3);

        assertFalse(cleanRulings.lineBetween(a, a));
        assertFalse(cleanRulings.lineBetween(a, b));
        assertTrue(cleanRulings.lineBetween(a, c));
        assertTrue(cleanRulings.lineBetween(a, d));
        assertTrue(cleanRulings.lineBetween(a, e));
        assertTrue(cleanRulings.lineBetween(a, f));
    }


    @Test
    public void testSingleLineInRange() {

        List<Ruling> horizontals = List.of(new Ruling(new Point2D.Float(0, 1), new Point2D.Float(100, 1)));
        List<Ruling> verticals = List.of(new Ruling(new Point2D.Float(1, 0), new Point2D.Float(1, 100)));

        CleanRulings cleanRulings = new CleanRulings(horizontals, verticals);

        assertEquals(0, cleanRulings.getVerticalsInXInterval(-2, -1).size());
        assertEquals(0, cleanRulings.getVerticalsInXInterval(-2, -5).size());
        assertEquals(0, cleanRulings.getVerticalsInXInterval(-2, Float.NaN).size());
        assertEquals(1, cleanRulings.getVerticalsInXInterval(1, 10).size());
        assertEquals(0, cleanRulings.getVerticalsInXInterval(100, 101).size());
        assertEquals(verticals.size(), cleanRulings.getVerticalsInXInterval(Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY).size());
        assertEquals(1, cleanRulings.getVerticalsInXInterval(1 - 1e-5f, 1 + 1e-5f).size());
        assertEquals(0, cleanRulings.getVerticalsInXInterval(1e-5f, 1 - 1e-5f).size());

        assertEquals(0, cleanRulings.getHorizontalsInYInterval(-2, -1).size());
        assertEquals(1, cleanRulings.getHorizontalsInYInterval(1, 10).size());
        assertEquals(0, cleanRulings.getHorizontalsInYInterval(100, 1001).size());
    }


    @Test
    public void testLinesInRange() {

        List<Ruling> horizontals = IntStream.range(0, 101).boxed()
                .map(y -> new Ruling(new Point2D.Float(0, y), new Point2D.Float(100, y)))
                .toList();
        List<Ruling> verticals = IntStream.range(0, 101).boxed()
                .map(x -> new Ruling(new Point2D.Float(x, 0), new Point2D.Float(x, 100)))
                .toList();
        CleanRulings cleanRulings = new CleanRulings(horizontals, verticals);

        assertEquals(0, cleanRulings.getVerticalsInXInterval(-2, -1).size());
        assertEquals(0, cleanRulings.getVerticalsInXInterval(-2, -5).size());
        assertEquals(0, cleanRulings.getVerticalsInXInterval(-2, Float.NaN).size());
        assertEquals(10, cleanRulings.getVerticalsInXInterval(1, 10).size());
        assertEquals(1, cleanRulings.getVerticalsInXInterval(100, 101).size());
        assertEquals(verticals.size(), cleanRulings.getVerticalsInXInterval(Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY).size());
        assertEquals(1, cleanRulings.getVerticalsInXInterval(-1e-5f, 1e-5f).size());
        assertEquals(1, cleanRulings.getVerticalsInXInterval(0, 0).size());
        assertEquals(0, cleanRulings.getVerticalsInXInterval(1e-5f, 1 - 1e-5f).size());

        assertEquals(0, cleanRulings.getHorizontalsInYInterval(-2, -1).size());
        assertEquals(10, cleanRulings.getHorizontalsInYInterval(1, 10).size());
        assertEquals(1, cleanRulings.getHorizontalsInYInterval(100, 1001).size());
    }


    @Test
    public void testLinesInRangePerformance() {

        List<Ruling> horizontals = IntStream.range(0, (int) 1e6).boxed()
                .map(y -> new Ruling(new Point2D.Float(0, y), new Point2D.Float(100, y)))
                .toList();
        CleanRulings cleanRulings = new CleanRulings(horizontals, Collections.emptyList());

        float startY = 29;
        float endY = 3000;
        long start = System.currentTimeMillis();
        var result = cleanRulings.getHorizontalsInYInterval(startY, endY);
        long time = System.currentTimeMillis() - start;

        start = System.currentTimeMillis();
        var result2 = cleanRulings.getHorizontals()
                .stream()
                .filter(ruling -> ruling.getY1() >= startY && ruling.getY1() <= endY)
                .toList();
        long time2 = System.currentTimeMillis() - start;

        assertEquals(result, result2);
        assertTrue(time < time2);

    }

}