/* Copyright CloudLinux Zug GmbH 2010-2017 All Rights Reserved
 *
 * Licensed under CLOUD LINUX LICENSE AGREEMENT
 * http://cloudlinux.com/docs/LICENSE.TXT
 */

import { Input, Output, ElementRef, EventEmitter } from '@angular/core';
import { ControlValueAccessor } from '@angular/forms';
import { Validator } from '../validator';
import { Requirements } from '../row';

export abstract class InputAccessor implements ControlValueAccessor {

    public elementRef: ElementRef;
    public validator: Validator;

    protected _innerValue: any;

    protected changed = new Array<(value: any) => void>();
    protected touched = new Array<() => void>();

    @Output('action') actionEmitter = new EventEmitter();

    @Input() title: string = '';
    @Input() description: string = '';
    @Input() id: string = '';
    @Input() name: string = '';
    @Input() disabled: boolean;
    @Input() validatorData: any;

    @Input() highlightPhrase: string = '';

    @Input()
    public set requirements(requirements: (Requirements|string)[]) {
        let newRequirements: Requirements[] = [];
        requirements.forEach((requirement) => {
            if (typeof requirement === 'object') {
                requirement.m = Validator[requirement.m];
                newRequirements.push(requirement);

            } else if (typeof requirement === 'string') {
                newRequirements.push({m: Validator[requirement]});
            }
        });
        if (!this.validator) {
             this.validator = new Validator();
        }
        this.validator.defineRequirements(newRequirements);
    }

    @Input() set validatorFabric(value: Function) {
        if (typeof value === 'function') {
            this.validator = value();
        }
    }

    get innerValue(): any {
        return this._innerValue;
    }

    set innerValue(value: any) {
        this.updateValue(value);
    }

    public abstract prepareValueHook(): void;

    protected updateValue(value: any) {
        if (typeof value !== 'undefined' && value !== null && this.validator
            && this.validator.hasRequirements
        ) {
            this.validator.validate(value, this.innerValue, this.elementRef,
                this.validatorData);
        }
        if (this._innerValue !== value) {
            this._innerValue = value;
            this.prepareValueHook();
            this.changed.forEach(f => f(this._innerValue));
        }
    }

    protected action(...args) {
        this.actionEmitter.emit(args);
    }

    writeValue(value: any) {
        this.innerValue = value;
    }

    registerOnChange(fn: (value: any) => void) {
        this.changed.push(fn);
    }

    registerOnTouched(fn: () => void) {
        this.touched.push(fn);
    }

    ngAfterContentInit(): void {}

}
