
import { makeAutoObservable, reaction } from "mobx";
import { Doc, DocListCast, FieldType } from "../../../../../fields/Doc";
import { Docs } from "../../../../documents/Documents";
import { Col } from "./DocCreatorMenu";
import { DynamicField } from "./FieldTypes/DynamicField";
import { Field, FieldSettings, ViewType } from "./FieldTypes/Field";
import {  } from "./FieldTypes/FieldUtils";
import { } from "./FieldTypes/StaticField";
import { observer } from "mobx-react";
import { IDisposer } from "mobx-utils";

export class Template {

    mainField: DynamicField;
    private settings: FieldSettings;

    constructor(templateInfo: FieldSettings) {
        makeAutoObservable(this);
        this.mainField = this.setupMainField(templateInfo);
        this.settings = templateInfo;
    }

    get childFields(): Field[] { return this.mainField.getSubfields };
    get allFields(): Field[] { return this.mainField.getAllSubfields };
    get contentFields(): Field[] { return this.allFields.filter(field => field.getViewType === ViewType.STATIC) };
    get doc(){ return this.mainField.renderedDoc; };
    get title() { return this.mainField.getTitle() };

    cleanup = () => {
        //dispose each subfields disposers, etc.
    }

    cloneBase = () => {
        const clone: Template = new Template(this.settings);
        clone.allFields.forEach(field => {
            const matchingField: Field = this.allFields.filter(f => f.getID === field.getID)[0];
            matchingField.applyAttributes(field);
        })
        return clone;
    }

    getRenderedDoc = () => {
        const doc: Doc = this.doc;
        this.contentFields.forEach(field => {
            const title: string = field.getTitle();
            const val: FieldType = field.getContent() as FieldType;
            if (!title || !val) return;
            doc[title] = val;
        });
        return doc;
    }

    getFieldByID = (id: number): Field => {
        return this.allFields.filter(field => field.getID === id)[0];
    }

    getFieldByTitle = (title: string) => {
        return this.allFields.filter(field => field.getTitle() === title)[0];
    }

    setupMainField = (templateInfo: FieldSettings) => {
        return new DynamicField(templateInfo, 1);
    }

    get descriptionSummary(): string {
        let summary: string = '';
        this.contentFields.forEach(field => {
            summary += `--- Field #${field.getID} (title: ${field.getTitle()}): ${field.getDescription ?? ''} ---`;
        });
        return summary;
    }

    get compiledContent(): string {
        let summary: string = '';
        this.contentFields.forEach(field => {
            summary += `--- Field #${field.getID} (title: ${field.getTitle()}): ${field.getContent() ?? ''} ---`;
        });
        return summary;
    }

    renderUpdates = () => {
        this.allFields.forEach(field => {
            field.updateRenderedDoc(field.renderedDoc);
        });
    };

    resetToBase = () => {
        this.allFields.forEach(field => {
            field.updateRenderedDoc();
        })
    }

    isValidTemplate = (cols: Col[]) => {
        const matches: number[][] = this.getMatches(cols);
        const maxMatches: number = this.maxMatches(matches);
        return maxMatches === this.contentFields.length;
    }

    getMatches = (cols: Col[]): number[][] => {
        const numFields = this.contentFields.length;

        if (cols.length !== numFields) return [];

        const matches: number[][] = Array(numFields)
            .fill([])
            .map(() => []);

        this.contentFields.forEach((field, i) => {
            matches[i] = (field.matches(cols));
        });

        return matches;
    }

    maxMatches = (matches: number[][]) => {
        if (matches.length === 0) return 0;

        const fieldsCt = this.contentFields.length;
        const used: boolean[] = Array(fieldsCt).fill(false);
        const mt: number[] = Array(fieldsCt).fill(-1);

        const augmentingPath = (v: number): boolean => {
            if (used[v]) return false;
            used[v] = true;

            for (const to of matches[v]) {
                if (mt[to] === -1 || augmentingPath(mt[to])) {
                    mt[to] = v;
                    return true;
                }
            }
            return false;
        };

        for (let v = 0; v < fieldsCt; ++v) {
            used.fill(false);
            augmentingPath(v);
        }

        let count: number = 0;

        for (let i = 0; i < fieldsCt; ++i) {
            if (mt[i] !== -1) ++count;
        }

        return count;
    };

}