import { SimpleType, Type } from "./type";
import { anglebrackets, braces, Item, keyvalue, opseq, seq } from "./block";
import { ModuleContext } from "./context";

export function variantInitFor(variantName: string | undefined) : Item[] {
    return variantName === void 0 ? [] : [variantFor(variantName)];
}

export function variantFor(variantName: string): Item {
    return keyvalue('_variant', JSON.stringify(variantName));
}

function simpleTypeFields(ctxt: ModuleContext, baseType: Type, t: SimpleType): Item[] {
    switch (t.kind) {
        case 'unit':
            return [];
        case 'ref':
        case 'set':
        case 'dictionary':
        case 'array':
            return [keyvalue('value', renderType(ctxt, t))];
        case 'record':
            return Array.from(t.fields).map(([nn, tt]) => keyvalue(nn, renderType(ctxt, tt)));
        default:
            ((_: never) => {})(t);
            throw new Error("Unreachable");
    }
}

export function renderVariant(
    ctxt: ModuleContext,
    baseType: Type,
    [variantName, t]: [string, SimpleType],
): Item {
    let fields = simpleTypeFields(ctxt, baseType, t);
    return braces(variantFor(variantName), ... fields);
}

export function renderType(ctxt: ModuleContext, t: Type): Item {
    switch (t.kind) {
        case 'union': return opseq('never', ' | ', ...
            Array.from(t.variants).flatMap(entry => renderVariant(ctxt, t, entry)));
        case 'unit': return braces(... simpleTypeFields(ctxt, t, t));
        case 'ref':
            if (t.ref === null && t.typeName === '_embedded') {
                return t.typeName;
            } else {
                return seq(t.typeName, ctxt.genericArgsFor(t));
            }
        case 'set': return seq('_.KeyedSet', anglebrackets(
            renderType(ctxt, t.type),
            '_embedded'));
        case 'dictionary': return seq('_.KeyedDictionary', anglebrackets(
            renderType(ctxt, t.key),
            renderType(ctxt, t.value),
            '_embedded'));
        case 'array': return seq('Array', anglebrackets(renderType(ctxt, t.type)));
        case 'record': return braces(... simpleTypeFields(ctxt, t, t));
        default:
            ((_: never) => {})(t);
            throw new Error("Unreachable");
    }
}
