import { Component, ContentChildren, Inject, Input, QueryList, ViewChildren } from '@angular/core';
import { FormFieldComponent } from './form-field.component';
import { FORM_OWNER } from './form-owner';
import { PARENT_FORM } from './parent-form';
import { Orderer, OrderingService } from '../services/ordering.service';

@Component({
    selector: 'hm-form-fields',
    template: `
    <ng-container *ngFor="let field of joinedFields; trackBy: tackByNamed">
        <ng-container *ngTemplateOutlet="field.output"></ng-container>
    </ng-container>

    <hm-form-field [named]="field.name" *ngFor="let field of form?.fields; trackBy: tackByName"></hm-form-field>
`,
    providers: [{ provide: FORM_OWNER, useExisting: FormFieldsComponent}]
})
export class FormFieldsComponent {
    orderer: Orderer;
    @Input() set order(orderString: string) {
        this.orderer = this.ordering.parse(orderString);
        this.computeSortedJoinedFields();
    }

    externalFields: QueryList<FormFieldComponent>;
    @ContentChildren(FormFieldComponent)
    set contentFields(newFields: QueryList<FormFieldComponent>) {
        this.externalFields = newFields;
        this.computeSortedJoinedFields();
    }

    defaultFields: QueryList<FormFieldComponent>;
    @ViewChildren(FormFieldComponent)
    set viewFields(newFields: QueryList<FormFieldComponent>) {
        this.defaultFields = newFields;
        this.computeSortedJoinedFields();
    }

    joinedFields: Array<FormFieldComponent>;

    computeSortedJoinedFields(): void {
        const external = this.externalFields && this.externalFields.toArray() || [];
        const defaults = this.defaultFields && this.defaultFields.toArray() || [];

        const externalyDefinedFields = external.filter(({ named }) => !defaults.some(({ named: matchingNamed }) => matchingNamed === named));
        const overrideFields = external.filter(({ named }) => defaults.some(({ named: matchingNamed }) => matchingNamed === named));

        const defaultAndOverridenFields = defaults.map(field => overrideFields.find(({ named }) => named === field.named) || field);
        const joinedFields = [...defaultAndOverridenFields, ...externalyDefinedFields]; // externaly defined fields are placed last

        this.joinedFields =  this.orderer && this.orderer.order(joinedFields) || joinedFields;
    }

    tackByName(_, { name }): any {
        return name;
    }

    tackByNamed(_, { named }): any {
        return named;
    }

    constructor(@Inject(PARENT_FORM) public form, private ordering: OrderingService) { }
}
