import {
    AstCreator,
    ETypeKind,
    Expr,
    ExprKind,
    Stmt,
    StmtKind,
} from "../ast.ts";
import { AstVisitor, visitExpr, VisitRes, visitStmts } from "../ast_visitor.ts";
import { Pos } from "../token.ts";

export class ArrayLiteralDesugarer implements AstVisitor {
    public constructor(
        private astCreator: AstCreator,
    ) {}

    public desugar(stmts: Stmt[]) {
        visitStmts(stmts, this);
    }

    visitArrayExpr(expr: Expr): VisitRes {
        if (expr.kind.type !== "array") {
            throw new Error();
        }
        const npos: Pos = { index: 0, line: 1, col: 1 };
        const Expr = (kind: ExprKind, pos = npos) =>
            this.astCreator.expr(kind, pos);
        const Stmt = (kind: StmtKind, pos = npos) =>
            this.astCreator.stmt(kind, pos);
        const EType = (kind: ETypeKind, pos = npos) =>
            this.astCreator.etype(kind, pos);

        const std = (ident: string): Expr =>
            Expr({
                type: "path",
                subject: Expr({
                    type: "ident",
                    ident: "std",
                }),
                ident,
            });

        if (expr.kind.exprs.length < 1) {
            throw new Error("");
        }

        expr.kind = {
            type: "block",
            stmts: [
                Stmt({
                    type: "let",
                    param: this.astCreator.param({
                        ident: "::value",
                        mut: true,
                        pos: npos,
                    }),
                    value: Expr({
                        type: "call",
                        subject: Expr({
                            type: "etype_args",
                            subject: std("array_new"),
                            etypeArgs: [
                                EType({
                                    type: "type_of",
                                    expr: expr.kind.exprs[0],
                                }),
                            ],
                        }),
                        args: [],
                    }),
                }),
                ...expr.kind.exprs
                    .map((expr) =>
                        Stmt({
                            type: "expr",
                            expr: Expr({
                                type: "call",
                                subject: std("array_push"),
                                args: [
                                    Expr({ type: "ident", ident: "::value" }),
                                    expr,
                                ],
                            }),
                        })
                    ),
            ],
            expr: Expr({ type: "ident", ident: "::value" }),
        };

        visitExpr(expr, this);

        return "stop";
    }
}
