package dev.fyloz.trial.colorrecipesexplorer.xlsx.component;

import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.Cell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.styles.IBiggerCell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.styles.IColoredCell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.styles.IFontCell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.exception.IndexOutOfTableBoundsException;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.util.Position;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.IndexedColors;

import java.text.DecimalFormat;
import java.util.*;

public class Table {

    private static DecimalFormat format = new DecimalFormat("#.###");

    private int width;
    private int height;
    private int cellWidth;

    private Map<Position, Cell> content;

    private List<TableSubNameCell> columns;
    private List<TableSubNameCell> rows;

    public Table(int width, int height, String name) {
        this.width = width;
        this.height = height;

        cellWidth = Document.WIDTH / width;

        content = new HashMap<>();
        columns = new ArrayList<>();
        rows = new ArrayList<>();

        for (int row = 0; row < this.height; row++) {
            for (int col = 0; col < this.width; col++) {
                Position pos = new Position(col, row);
                Cell cell;

                if (col == 0 && row == 0) { // Titre
                    cell = new TableNameCell(name, cellWidth);
                } else if (col == 0 || row == 0) { // Colonnes/Rangée
                    cell = new TableSubNameCell("", cellWidth);

                    if (col > 0) columns.add((TableSubNameCell) cell);
                    else rows.add((TableSubNameCell) cell);
                } else {
                    cell = new TableStringValueCell("", cellWidth);
                }

                content.put(pos, cell);
            }
        }
    }

    public void setColumnName(int index, String name) {
        int columnnum = columns.size() - 1;
        if (columnnum < index) throw new IndexOutOfTableBoundsException(index, columnnum);

        columns.get(index).setName(name);
    }

    /**
     * @param index Index de la rangée (à partir de 0)
     * @param name  Nouveau nom de la rangée
     * @throws IndexOutOfTableBoundsException La rangée à l'index défini est en dehors de la portée du tableau.
     * @author
     */
    public void setRowName(int index, String name) {
        int rownum = rows.size() - 1;
        if (rownum < index) throw new IndexOutOfTableBoundsException(index, rownum);

        rows.get(index).setName(name);
    }

    public void setContent(Position position, String value) {
        if (isInTableRange(position, this)) {
            content.put(position, new TableStringValueCell(value, cellWidth));
        }
    }

    public void setContent(Position position, double value) {
        if (isInTableRange(position, this)) {
            value = Double.parseDouble(format.format(value).replace(',', '.'));
            content.put(position, new TableNumValueCell(value, cellWidth));
        }
    }

    public int getWidth() {
        return width;
    }

    public int getHeight() {
        return height;
    }

    public Collection<Cell> getCells() {
        int col = 0;
        int row = 0;

        Collection<Cell> cells = new ArrayList<>();
        while (row < height) {

            while (col < width) {
                Position pos = new Position(col, row);

                cells.add(content.get(pos));

                col++;
            }

            col = 0;
            row++;
        }

        return cells;
    }

    public static boolean isInTableRange(Position position, Table table) {
        int col = position.getColumn();
        int columnnum = table.getWidth() - 1;
        if (col > columnnum) throw new IndexOutOfTableBoundsException(col, columnnum);

        int row = position.getRow();
        int rownum = table.getHeight() - 1;
        if (row > rownum) throw new IndexOutOfTableBoundsException(row, rownum);

        return true;
    }

    private class TableNameCell extends Cell implements IColoredCell, IFontCell, IBiggerCell {

        private final IndexedColors COLOR = IndexedColors.GREY_40_PERCENT;

        private String name;
        private int width;
        private int height = 2;

        public TableNameCell(String name, int width) {
            super(CellType.STRING);

            this.name = name;
            this.width = width;
        }

        @Override
        public String getContentText() {
            return name;
        }

        @Override
        public IndexedColors getBackgroundColor() {
            return COLOR;
        }

        @Override
        public int getWidth() {
            return width;
        }

        @Override
        public int getHeight() {
            return height;
        }
    }

    private class TableSubNameCell extends Cell implements IFontCell, IBiggerCell, IColoredCell {

        private String name;
        private int width;
        private int height = 2;

        public TableSubNameCell(String name, int width) {
            super(CellType.STRING);

            this.name = name;
            this.width = width;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String getContentText() {
            return name;
        }

        @Override
        public IndexedColors getBackgroundColor() {
            return IndexedColors.GREY_25_PERCENT;
        }

        @Override
        public int getWidth() {
            return width;
        }

        @Override
        public int getHeight() {
            return height;
        }
    }

    private class TableStringValueCell extends Cell implements IFontCell, IBiggerCell {

        private String value;
        private int width;
        private int height = 2;

        public TableStringValueCell(String value, int width) {
            super(CellType.STRING);

            this.value = value;
            this.width = width;
        }

        @Override
        public String getContentText() {
            return value;
        }

        @Override
        public boolean isBold() {
            return false;
        }

        @Override
        public int getWidth() {
            return width;
        }

        @Override
        public int getHeight() {
            return height;
        }
    }

    private class TableNumValueCell extends Cell implements IFontCell, IBiggerCell {

        private double value;
        private int width;
        private int height = 2;

        public TableNumValueCell(double value, int width) {
            super(CellType.NUMERIC);

            this.value = value;
            this.width = width;
        }

        @Override
        public double getContentNum() {
            return value;
        }

        @Override
        public boolean isBold() {
            return false;
        }

        @Override
        public int getWidth() {
            return width;
        }

        @Override
        public int getHeight() {
            return height;
        }
    }

}
