package expression.generic;

import java.math.BigInteger;
import java.util.List;

/**
 * @author Doschennikov Nikita (me@fymio.us)
 */
public class GenericTabulator implements Tabulator {

    @Override
    public Object[][][] tabulate(
        String mode,
        String expression,
        int x1,
        int x2,
        int y1,
        int y2,
        int z1,
        int z2
    ) throws Exception {
        return switch (mode) {
            case "i" -> compute(
                IntCheckedType.INSTANCE,
                expression,
                x1,
                x2,
                y1,
                y2,
                z1,
                z2
            );
            case "d" -> compute(
                DoubleType.INSTANCE,
                expression,
                x1,
                x2,
                y1,
                y2,
                z1,
                z2
            );
            case "bi" -> compute(
                BigIntegerType.INSTANCE,
                expression,
                x1,
                x2,
                y1,
                y2,
                z1,
                z2
            );
            case "u" -> compute(
                IntUncheckedType.INSTANCE,
                expression,
                x1,
                x2,
                y1,
                y2,
                z1,
                z2
            );
            case "s" -> compute(
                ShortType.INSTANCE,
                expression,
                x1,
                x2,
                y1,
                y2,
                z1,
                z2
            );
            case "f" -> compute(
                FloatType.INSTANCE,
                expression,
                x1,
                x2,
                y1,
                y2,
                z1,
                z2
            );
            case "t", "it" -> compute(
                IntTruncType.INSTANCE,
                expression,
                x1,
                x2,
                y1,
                y2,
                z1,
                z2
            );
            default -> throw new IllegalArgumentException(
                "Unknown mode: " + mode
            );
        };
    }

    private <T> Object[][][] compute(
        ArithmeticType<T> type,
        String expression,
        int x1,
        int x2,
        int y1,
        int y2,
        int z1,
        int z2
    ) {
        GenericExpr<T> expr = new GenericParser().parse(expression);

        int xLen = x2 - x1 + 1;
        int yLen = y2 - y1 + 1;
        int zLen = z2 - z1 + 1;
        Object[][][] result = new Object[xLen][yLen][zLen];

        for (int xi = 0; xi < xLen; xi++) {
            for (int yi = 0; yi < yLen; yi++) {
                for (int zi = 0; zi < zLen; zi++) {
                    T x = type.fromInt(x1 + xi);
                    T y = type.fromInt(y1 + yi);
                    T z = type.fromInt(z1 + zi);
                    try {
                        result[xi][yi][zi] = expr.evaluate(
                            type,
                            List.of(x, y, z)
                        );
                    } catch (Exception e) {
                        result[xi][yi][zi] = null;
                    }
                }
            }
        }
        return result;
    }
}
