package net.hamnaberg.jsonstat.table;

import net.hamnaberg.funclite.*;
import net.hamnaberg.jsonstat.Category;
import net.hamnaberg.jsonstat.Data;
import net.hamnaberg.jsonstat.Dataset;
import net.hamnaberg.jsonstat.Dimension;
import net.hamnaberg.jsonstat.util.CollectionUtils;
import net.hamnaberg.jsonstat.util.IntCartesianProduct;

import java.util.*;

import static net.hamnaberg.funclite.Optional.none;
import static net.hamnaberg.funclite.Optional.some;
import static net.hamnaberg.jsonstat.util.CollectionUtils.*;

public final class Table {
    private Optional<String> title;
    private final List<TableHeader> headers = new ArrayList<>();
    private final List<List<Data>> rows = new ArrayList<>();

    public Table(Optional<String> title, List<TableHeader> headers, List<List<Data>> rows) {
        this.title = title;
        this.headers.addAll(headers);
        for (List<Data> row : rows) {
            this.rows.add(CollectionOps.newArrayList(row));
        }
    }

    public static Table fromDataset(Dataset dataset) {
        List<Dimension> dimensions = dataset.getDimensions();
        Dimension rowDimension = findRowDimension(dataset);
        List<TableHeader> headers = buildHeader(dimensions, rowDimension.getId());
        List<List<Data>> rows = dataset.getRows();
        //TODO: maybe this should really be part of dataset.getRows()...
        int i = 0;
        for (String s : rowDimension.getCategory()) {
            List<Data> row = rows.get(i);
            int j = 0;
            row.add(j, new Data(rowDimension.getCategory().getLabel(s).getOrElse(s), Optional.<String>none()));
            for (Dimension dimension : dimensions) {
                if (dimension.isConstant()) {
                    boolean added = false;
                    for (String id : dimension.getCategory()) {
                        row.add(j, new Data(dimension.getCategory().getLabel(id).getOrElse(id), Optional.<String>none()));
                        added = true;
                    }
                    if (!added) {
                        row.add(j, new Data(dimension.getLabel().getOrElse(dimension.getId()), Optional.<String>none()));
                    }
                    j++;
                }
            }
            i++;
        }

        return new Table(dataset.getLabel(), headers, rows);
    }

    private static List<TableHeader> buildHeader(List<Dimension> dimensions, String rowDimension) {
        //TODO: This is stupid. Fix it.
        List<List<String>> categories = new ArrayList<>();
        List<TableHeader> headers = new ArrayList<>();
        for (Dimension dimension : dimensions) {
            boolean isRow = rowDimension.equals(dimension.getId());
            if (dimension.isRequired() && !isRow) {
                Category category = dimension.getCategory();
                List<String> cats = new ArrayList<>();
                for (String id : category) {
                    cats.add(category.getLabel(id).getOrElse(id));
                }
                categories.add(cats);
            }
            else if (dimension.isConstant()) {
                headers.add(new TableHeader(CollectionOps.headOption(dimension.getCategory()), dimension.getLabel()));
            }
            if (isRow) {
                headers.add(new TableHeader(Optional.<String>none(), dimension.getLabel()));
            }
        }

        List<List<String>> combinations = product(categories);

        for (List<String> combination : combinations) {
            String label = join(combination, " ");
            headers.add(new TableHeader(Optional.<String>none(), some(label)));
        }

        return headers;
    }


    private static Dimension findRowDimension(Dataset ds) {
        IntCartesianProduct p = ds.asCartasianProduct();
        return ds.getDimensions().get(p.getMaxIndex());
    }


    public Optional<String> getTitle() {
        return title;
    }

    public TableHeader getHeader(int index) {
        return headers.get(index);
    }

    public TableHeader getHeader(String id) {
        return getHeader(getHeaderIndex(id));
    }

    public int getHeaderIndex(String id) {
        for (int i = 0; i < headers.size(); i++) {
            TableHeader h = headers.get(i);
            if (h.getId().equals(some(id))) {
                return i;
            }
        }
        return -1;
    }

    public List<TableHeader> getHeaders() {
        return headers;
    }

    public List<Data> getRow(int index) {
        if (index < rows.size()) {
            return rows.get(index);
        }
        return Collections.emptyList();
    }

    public List<List<Data>> getRows() {
        return rows;
    }

    public <A> A render(Renderer<A> renderer) {
        return renderer.render(this);
    }
}

@@ -62,17 +62,69 @@
 unclite.
-*
+CollectionOps;%0Aimport net.hamnaberg.funclite.Optional
 ;%0Aimport
@@ -277,60 +277,8 @@
 on;%0A
-import net.hamnaberg.jsonstat.util.CollectionUtils;%0A
 impo
@@ -351,63 +351,72 @@
 til.
-*;%0A%0Aimport static net.hamnaberg.funclite.Optional.none;
+ArrayList;%0Aimport java.util.Collections;%0Aimport java.util.List;%0A
 %0Aimp
@@ -526,9 +526,79 @@
 ils.
-*
+join;%0Aimport static net.hamnaberg.jsonstat.util.CollectionUtils.product
 ;%0A%0Ap

Fix imports.