import { Value } from "./values";
import { Annotated } from "./annotated";
import { Record, Tuple } from "./record";
import { Set, Dictionary } from "./dictionary";
import type { GenericEmbedded } from "./embedded";

export function unannotate<T = GenericEmbedded>(v: Value<T>): Value<T> {
    return Annotated.isAnnotated<T>(v) ? v.item : v;
}

export function peel<T = GenericEmbedded>(v: Value<T>): Value<T> {
    return strip(v, 1);
}

export function strip<T = GenericEmbedded>(
    v: Value<T>,
    depth: number = Infinity): Value<T>
{
    function step(v: Value<T>, depth: number): Value<T> {
        if (depth === 0) return v;
        if (!Annotated.isAnnotated<T>(v)) return v;

        const nextDepth = depth - 1;
        function walk(v: Value<T>): Value<T> { return step(v, nextDepth); }

        if (Record.isRecord<Value<T>, Tuple<Value<T>>, T>(v.item)) {
            return Record(step(v.item.label, depth), v.item.map(walk));
        } else if (Annotated.isAnnotated(v.item)) {
            throw new Error("Improper annotation structure");
        } else if (nextDepth === 0) {
            return v.item;
        } else if (Array.isArray(v.item)) {
            return (v.item as Value<T>[]).map(walk);
        } else if (Set.isSet<T>(v.item)) {
            return v.item.map(walk);
        } else if (Dictionary.isDictionary<T>(v.item)) {
            return v.item.mapEntries((e) => [walk(e[0]), walk(e[1])]);
        } else {
            return v.item;
        }
    }
    return step(v, depth);
}
