import { Arr, Fun, Obj, Result } from '@ephox/katamari';
import * as AlloyLogger from '../../log/AlloyLogger';
import * as AlloyParts from '../../parts/AlloyParts';
import * as PartType from '../../parts/PartType';
import { Composing } from '../behaviour/Composing';
import { Representing } from '../behaviour/Representing';
import * as SketchBehaviours from '../component/SketchBehaviours';
import * as GuiTypes from './GuiTypes';
import * as UiSketcher from './UiSketcher';
const owner = 'form';
const schema = [
    SketchBehaviours.field('formBehaviours', [Representing])
];
const getPartName = (name) => '<alloy.field.' + name + '>';
const sketch = (fSpec) => {
    const parts = (() => {
        const record = [];
        const field = (name, config) => {
            record.push(name);
            return AlloyParts.generateOne(owner, getPartName(name), config);
        };
        return {
            field,
            record: Fun.constant(record)
        };
    })();
    const spec = fSpec(parts);
    const partNames = parts.record();
    // Unlike other sketches, a form does not know its parts in advance (as they represent each field
    // in a particular form). Therefore, it needs to calculate the part names on the fly
    const fieldParts = Arr.map(partNames, (n) => PartType.required({ name: n, pname: getPartName(n) }));
    return UiSketcher.composite(owner, schema, fieldParts, make, spec);
};
const toResult = (o, e) => o.fold(() => Result.error(e), Result.value);
const make = (detail, components) => ({
    uid: detail.uid,
    dom: detail.dom,
    components,
    // Form has an assumption that every field must have composing, and that the composed element has representing.
    behaviours: SketchBehaviours.augment(detail.formBehaviours, [
        Representing.config({
            store: {
                mode: 'manual',
                getValue: (form) => {
                    const resPs = AlloyParts.getAllParts(form, detail);
                    return Obj.map(resPs, (resPThunk, pName) => resPThunk().bind((v) => {
                        const opt = Composing.getCurrent(v);
                        return toResult(opt, new Error(`Cannot find a current component to extract the value from for form part '${pName}': ` + AlloyLogger.element(v.element)));
                    }).map(Representing.getValue));
                },
                setValue: (form, values) => {
                    Obj.each(values, (newValue, key) => {
                        AlloyParts.getPart(form, detail, key).each((wrapper) => {
                            Composing.getCurrent(wrapper).each((field) => {
                                Representing.setValue(field, newValue);
                            });
                        });
                    });
                }
            }
        })
    ]),
    apis: {
        getField: (form, key) => {
            // Returns an Optional (not a result);
            return AlloyParts.getPart(form, detail, key).bind(Composing.getCurrent);
        }
    }
});
const Form = {
    getField: GuiTypes.makeApi((apis, component, key) => apis.getField(component, key)),
    sketch
};
export { Form };
//# sourceMappingURL=Form.js.map