import DataType from "./DataType";
import InnerNode from "./InnerNode";
import DataNode, {ChangeEvent, ServerSideErrorEvent, ValidEvent} from "./DataNode";
import EventHandler from "../Utility/EventHandler";

export default class DataEndNode<T> implements DataNode<T>{
    public onChange: EventHandler<ChangeEvent<T>> = new EventHandler();
    public onReset: EventHandler<null> = new EventHandler();
    public onValidChange: EventHandler<ValidEvent> = new EventHandler();
    public onServerSideErrorsChange: EventHandler<ServerSideErrorEvent> = new EventHandler();
    public onMessagesChange: EventHandler<MessageEvent> = new EventHandler();

    public parent: null|InnerNode = null;
    public index: string|number;
    protected _serverSideErrors: any[] = [];

    protected _messages: string[] = [];
    protected _isValid: boolean = true;
    protected _value: T|null|undefined = undefined;
    protected _defaultValue: T|null = null;
    protected _isVisible:boolean = true;

    constructor(
        protected type: DataType<T>
    ) {
        this.type.onChange.add(() => this.validate());
    }

    public validate(): void{
        let result = this.type.validate(this.isChanged ? (this.value ?? null) : this.valueOrDefault);
        if (result !== true) {
            this.isValid = false;
            this.messages = result;
        } else {
            this.messages = [];
            this.isValid = true;
        }
    }

    public resetValue(): void{
        this.value = undefined;
        this.onReset.emit(null);
    }

    set isVisible(value: boolean){
        if( this._isVisible === value) return;
        this._isVisible = value;
        this.onValidChange.emit(new ValidEvent(this._isValid, this._isVisible));
    }

    set isValid(value: boolean){
        if( this._isValid === value) return;
        this._isValid = value;
        this.onValidChange.emit(new ValidEvent(this._isValid, this._isVisible));
    }

    set value(value: any){
        try{
            let newValue = this.type.sanitize(value);
            if (newValue !== this._value) {
                this._value = newValue;
                this.validate();
                this.onChange.emit(new ChangeEvent(this, this._value));
            }
        } catch(error: any){
            this.messages = [error.message];
            this.isValid = false;
        }
    }

    set serverSideErrors(errors: any){
        if (typeof errors !== "object" || errors == null) {
            this._serverSideErrors = [];
            this.onServerSideErrorsChange.emit(new ServerSideErrorEvent([]));
            return;
        }

        this._serverSideErrors = [];

        for (const index in errors) {
            this._serverSideErrors.push(errors[index]);
        }

        this.onServerSideErrorsChange.emit(new ServerSideErrorEvent(this._serverSideErrors));
    }

    get serverSideErrors(): any{
        return this._serverSideErrors;
    }

    set defaultValue(defaultValue: any){
        let value = this.type.sanitize(defaultValue);
        if (value === undefined ) value = null;
        this._defaultValue = value;
    }

    set messages(value: string[]){
        this._messages = value;
        this.onMessagesChange.emit(new MessageEvent(value));
    }

    get isChanged(): boolean{
        return this._value !== undefined;
    }

    get isValid(): boolean{
        return !this._isVisible || this._isValid;
    }

    get value(): T|null|undefined{
        return this._value;
    }

    get valueOrDefault(): T|null{
        return this._value ?? this._defaultValue;
    }

    get messages(): string[]{
        return this._messages;
    }
}

export class MessageEvent {
    public constructor(
        public readonly messages: string[],
    ) {}
}