import { File } from "../file";
import { BaseXmlComponent } from "./base";
import { IXmlableObject } from "./xmlable-object";

export const EMPTY_OBJECT = Object.seal({});

export abstract class XmlComponent extends BaseXmlComponent {
    // tslint:disable-next-line:readonly-keyword no-any
    protected root: Array<BaseXmlComponent | string | any>;

    constructor(rootKey: string) {
        super(rootKey);
        this.root = new Array<BaseXmlComponent | string>();
    }

    public prepForXml(file?: File): IXmlableObject | undefined {
        const children = this.root
            .filter((c) => {
                if (c instanceof BaseXmlComponent) {
                    return !c.IsDeleted;
                }
                return c !== undefined;
            })
            .map((comp) => {
                if (comp instanceof BaseXmlComponent) {
                    return comp.prepForXml(file);
                }
                return comp;
            })
            .filter((comp) => comp !== undefined); // Exclude undefined
        // If we only have a single IXmlableObject in our children array and it
        // represents our attributes, use the object itself as our children to
        // avoid an unneeded XML close element.  (Note: We have to use this
        // function to get typescript to allow our check.)
        // Additionally, if the array is empty, use an empty object as our
        // children in order to get an empty XML element generated.
        const onlyAttrs = (c) => typeof c === "object" && c._attr;
        return {
            [this.rootKey]: children.length ? (children.length === 1 && onlyAttrs(children[0]) ? children[0] : children) : EMPTY_OBJECT,
        };
    }

    public addChildElement(child: XmlComponent | string): XmlComponent {
        this.root.push(child);

        return this;
    }

    public delete(): void {
        this.deleted = true;
    }
}

export abstract class IgnoreIfEmptyXmlComponent extends XmlComponent {
    public prepForXml(): IXmlableObject | undefined {
        const result = super.prepForXml();
        // Ignore the object if its falsey or is an empty object (would produce
        // an empty XML element if allowed to be included in the output).
        if (result && (typeof result[this.rootKey] !== "object" || Object.keys(result[this.rootKey]).length)) {
            return result;
        }
    }
}
