package net.ranides.assira.collection;

import net.ranides.assira.collection.lists.IntArrayList;
import net.ranides.assira.collection.lists.IntRange;
import org.junit.Test;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import static org.junit.Assert.*;

public class GridTest {

    List<? extends Map<String, Object>> source = Arrays.asList(
            new Record<>().add("x", 2).add("y", 1).add("name", "first").add("description", "Curabitur vel felis.").add("time", asDateTime("2018-02-04 15:20")),
            new Record<>().add("x", 234).add("y", 12).add("name", "second").add("description", "Duis viverra.").add("time", asDateTime("2018-02-04 15:20")),
            new Record<>().add("x", 642).add("y", 31).add("name", "random").add("description", "Class aptent taciti.").add("time", asDateTime("2018-02-04 15:20")),
            new Record<>().add("x", 832).add("y", 341).add("name", "big").add("description", "Sed lorem metus, iaculis at diam sit amet, blandit eleifend leo.").add("time", asDateTime("2018-03-04 17:30")),
            new Record<>().add("x", 32).add("y", 1400).add("name", "virtual").add("description", "Fusce sed diam justo.").add("time", asDateTime("2018-03-24 18:15")),
            new Record<>().add("x", 754).add("y", 1.5).add("name", "last").add("description", "ed finibus.").add("time", asDateTime("2018-02-12 11:05"))
    );

    private static ZonedDateTime asDateTime(String dt) {
        return LocalDateTime.from(DateTimeFormatter.ISO_DATE_TIME.parse(dt.replace(' ','T'))).atZone(ZoneId.systemDefault());
    }

    @Test
    public void asGrid() {
        Grid<Object> grid = Grid.fromMap(source);

        List<String> header = Arrays.asList("x", "y", "name", "description", "time");

        List<Object> rows = Arrays.asList(
                Arrays.asList(2, 1, "first", "Curabitur vel felis.", asDateTime("2018-02-04 15:20")),
                Arrays.asList(234, 12, "second", "Duis viverra.", asDateTime("2018-02-04 15:20")),
                Arrays.asList(642, 31, "random", "Class aptent taciti.", asDateTime("2018-02-04 15:20")),
                Arrays.asList(832, 341, "big", "Sed lorem metus, iaculis at diam sit amet, blandit eleifend leo.", asDateTime("2018-03-04 17:30")),
                Arrays.asList(32, 1400, "virtual", "Fusce sed diam justo.", asDateTime("2018-03-24 18:15")),
                Arrays.asList(754, 1.5, "last", "ed finibus.", asDateTime("2018-02-12 11:05"))
        );

        assertEquals(header, grid.header().list());
        assertEquals(rows, grid.list());
    }

    @Test
    public void selectColumns() {
        List<List<Object>> rows1 = Arrays.asList(
                Arrays.asList(1, "first", "Curabitur vel felis."),
                Arrays.asList(12, "second", "Duis viverra."),
                Arrays.asList(31, "random", "Class aptent taciti."),
                Arrays.asList(341, "big", "Sed lorem metus, iaculis at diam sit amet, blandit eleifend leo."),
                Arrays.asList(1400, "virtual", "Fusce sed diam justo."),
                Arrays.asList(1.5, "last", "ed finibus.")
        );

        Grid<Object> grid = Grid.fromMap(source);

        assertEquals(rows1, grid.columns(new IntArrayList(new int[]{1,2,3})).list() );

        assertEquals(Arrays.asList("y", "name","description"), grid.columns(new IntArrayList(new int[]{1,2,3})).header().list());

        List<List<Object>> rows2 = Arrays.asList(
                Arrays.asList("second"),Arrays.asList("random"),Arrays.asList("big"),Arrays.asList("virtual"),Arrays.asList("last")
        );

        List<String> header2 = Arrays.asList("name");

        assertEquals(rows2, grid.columns(new IntArrayList(new int[]{2})).rows(new IntRange(1,6)).list());
        assertEquals(header2, grid.columns(new IntArrayList(new int[]{2})).rows(new IntRange(1,6)).header().list());
    }

    @Test
    public void sort() {

        List<Object> sortName = Arrays.asList(
                Arrays.asList(832, 341, "big", "Sed lorem metus, iaculis at diam sit amet, blandit eleifend leo.", asDateTime("2018-03-04 17:30")),
                Arrays.asList(2, 1, "first", "Curabitur vel felis.", asDateTime("2018-02-04 15:20")),
                Arrays.asList(754, 1.5, "last", "ed finibus.", asDateTime("2018-02-12 11:05")),
                Arrays.asList(642, 31, "random", "Class aptent taciti.", asDateTime("2018-02-04 15:20")),
                Arrays.asList(234, 12, "second", "Duis viverra.", asDateTime("2018-02-04 15:20")),
                Arrays.asList(32, 1400, "virtual", "Fusce sed diam justo.", asDateTime("2018-03-24 18:15"))
        );

        List<Object> sortDateX = Arrays.asList(
                Arrays.asList(2, 1, "first", "Curabitur vel felis.", asDateTime("2018-02-04 15:20")),
                Arrays.asList(234, 12, "second", "Duis viverra.", asDateTime("2018-02-04 15:20")),
                Arrays.asList(642, 31, "random", "Class aptent taciti.", asDateTime("2018-02-04 15:20")),
                Arrays.asList(754, 1.5, "last", "ed finibus.", asDateTime("2018-02-12 11:05")),
                Arrays.asList(832, 341, "big", "Sed lorem metus, iaculis at diam sit amet, blandit eleifend leo.", asDateTime("2018-03-04 17:30")),
                Arrays.asList(32, 1400, "virtual", "Fusce sed diam justo.", asDateTime("2018-03-24 18:15"))
        );

        List<Object> sortDateName = Arrays.asList(
                Arrays.asList(2, 1, "first", "Curabitur vel felis.", asDateTime("2018-02-04 15:20")),
                Arrays.asList(642, 31, "random", "Class aptent taciti.", asDateTime("2018-02-04 15:20")),
                Arrays.asList(234, 12, "second", "Duis viverra.", asDateTime("2018-02-04 15:20")),
                Arrays.asList(754, 1.5, "last", "ed finibus.", asDateTime("2018-02-12 11:05")),
                Arrays.asList(832, 341, "big", "Sed lorem metus, iaculis at diam sit amet, blandit eleifend leo.", asDateTime("2018-03-04 17:30")),
                Arrays.asList(32, 1400, "virtual", "Fusce sed diam justo.", asDateTime("2018-03-24 18:15"))
        );

        Grid<Object> grid = Grid.fromMap(source);

        assertEquals(sortName, grid.sort(new IntArrayList(new int[]{2})).list() );

        assertEquals(sortDateX, grid.sort(new IntArrayList(new int[]{4,1})).list());

        assertEquals(sortDateName, grid.sort(new IntArrayList(new int[]{4,2})).list());

    }

    @Test
    public void numbers() {
        List<? extends Map<String, Number>> list = Arrays.asList(
                new Record<Number>().add("x", 16).add("y", 30).add("uid", 10701),
                new Record<Number>().add("x", 84).add("y", 40).add("uid", 10702),
                new Record<Number>().add("x", 73).add("y", 21).add("uid", 10825),
                new Record<Number>().add("x", 27).add("y", 19).add("uid", 10649),
                new Record<Number>().add("x", 83).add("y", 63).add("uid", 10383)
        );

        Grid<Number> g1 = Grid.fromMap(list);


        assertEquals(21, g1.list().get(2).get(1));
        assertEquals(21, g1.rows().get(2).get("y") );
        assertEquals(21, g1.get(2, 1) );
        assertEquals(21, g1.get(2, "y") );

        Integer sum1 = g1.stream().map(c -> c.get().intValue()).reduce(Integer::sum);
        assertEquals(53716, sum1.intValue());

        Integer sum2 = g1.stream().filter(c -> c.column()!=2).map(c -> c.get().intValue()).reduce(Integer::sum);
        assertEquals(456, sum2.intValue());

        Integer sum3 = g1.stream().filter(c -> c.column()==1).map(c -> c.get().intValue()).reduce(Integer::sum);
        assertEquals(173, sum3.intValue());

        List<List<String>> text = Arrays.asList(
                Arrays.asList("16/1/1", "30/1/2", "10701/1/3"),
                Arrays.asList("84/2/1", "40/2/2", "10702/2/3"),
                Arrays.asList("73/3/1", "21/3/2", "10825/3/3"),
                Arrays.asList("27/4/1", "19/4/2", "10649/4/3"),
                Arrays.asList("83/5/1", "63/5/2", "10383/5/3")
        );

        Grid<String> g2 = g1.map(c -> c.get() + "/" + (c.row()+1) + "/" + (c.column()+1));

        assertEquals(text, g2.list());
    }

    private static final class Record<V> extends LinkedHashMap<String, V> {

        Record<V> add(String key, V value) {
            put(key, value);
            return this;
        }

    }

}