import { Injectable } from "@angular/core";
import { UtilsService } from "../services/utils.service";

// export interface MessageSource {
//     getFieldDescriptor (obj: string, fieldName: string): string;
//     getFieldDescriptor (obj: string, fieldName, subType: string): string;
//     getGUIElementLabel (baseName: string): string;
//     getGUIElementLabel (baseName: string, defaultValue: string): string;
//     getGUIElement (theClass: string, primaryItemName: string, secondaryItemName: string, guiType: string, defaultValue: string): string;
//     getFieldErrorMsg (obj: string, fieldName: string, tokenReplacements): string;
//     getFieldErrorMsg (obj: string, fieldName: string, errorType, tokenReplacements): string;
//     getDescriptionForEnum (enumv): string;
//     getDescriptionForEnum (clazz: string, enumv): string;
//     getMessageModes (): Set<string>; // Returns a modifieable set of message modes
// }

@Injectable()
export class MessageSourceConfig {

    public LOOKUP_MESSAGE = 'message';
    public LOOKUP_FIELD = 'field';
    public LOOKUP_GUI_LABELS = 'label';
    public LOOKUP_ENUM = 'enum';
    private NO_MATCH = "#NO_MATCH"; // Force it not to internalise the String so == only works for this case

    GUILabels: MessageSelector[];
    Enums: MessageSelector[];
    Messages: MessageSelector[];
    FieldNames: MessageSelector[];
    NextMessageSourceConfig: MessageSourceConfig;

    constructor(private utilsService: UtilsService) {

    }

    findMatch(lookUpType: string, className: string, fieldName: string, subType: string, matchBlankSubType: boolean, defaultValue: string, modes: string[]): string {
        
        var table: MessageSelector[] = [];

        if (lookUpType == this.LOOKUP_MESSAGE) {
            table = this.Messages;
        }
        else if (lookUpType == this.LOOKUP_FIELD) {
            table = this.FieldNames;
        }
        else if (lookUpType == this.LOOKUP_ENUM) {
            table = this.Enums;
        }
        else if (lookUpType == this.LOOKUP_GUI_LABELS) {
            table = this.GUILabels;
        }
        else {
            throw new Error("Invalid lookup type:" + lookUpType);
        }

        for (let selector of table) {
            if (this.matches(selector, className, fieldName, subType, matchBlankSubType, modes)) {
                return selector.Value;
            }
        }
        return defaultValue;
    }

    matches(selector: MessageSelector, clazzName: string, element: string, subType: string, matchBlankSubType: boolean, modes: string[]): boolean {

        if (!clazzName) {
            clazzName = 'Object';
        }

        if (selector.ClassName != null && selector.ClassName !== clazzName) {
            return false;
        }

        if (selector.FieldName != null && selector.FieldName !== element) {
            return false;
        }

        if (selector.Subtype != null && selector.Subtype !== subType) {
            return false;
        }
        else if (selector.Subtype == null && subType != null && !matchBlankSubType) {
            return false;
        }

        if (selector.Mode != null && !modes.includes(selector.Mode)) {
            return false;
        }
        return true;
    }

    //This is combined version of getFieldDescriptor(), getGUIElementLabel() and getGUIElementLabelWithDefault()
    translateLabel(fieldName: string, subType: string, defaultValue: string, modes: string[]): string {

        var obj: string             =   this.getObjectName(fieldName);
        var actualFieldName: string =   this.getActualFieldName(fieldName);
        var lookUpType: string      =   obj ? this.LOOKUP_FIELD : this.LOOKUP_GUI_LABELS;

        var result = this.findMatch(lookUpType, obj, actualFieldName, subType, (obj ? false : true), this.NO_MATCH, modes);

        return ((result == this.NO_MATCH) ? this.NextMessageSourceConfig.translateLabel(fieldName, subType, defaultValue, modes) : result);
    }

    //Below functions are exact copy of FileMessageSource.java, not sure if those will ever get used
    getFieldDescriptor(fieldName: string, subType: string, modes: string[]): string {

        var obj: string             =   this.getObjectName(fieldName);
        var actualFieldName: string =   this.getActualFieldName(fieldName);
        var result  =   this.findMatch(this.LOOKUP_FIELD, obj, actualFieldName, subType, false, this.NO_MATCH, modes);

        return ((result == this.NO_MATCH) ? this.NextMessageSourceConfig.getFieldDescriptor(fieldName, subType, modes) : result);
    }

    getGUIElementLabel(baseName: string, modes: string[]): string {
        var result = this.findMatch(this.LOOKUP_GUI_LABELS, 'Object', baseName, null, true, this.NO_MATCH, modes);

        return ((result == this.NO_MATCH) ? this.NextMessageSourceConfig.getGUIElementLabel(baseName, modes) : result);
    }

    getGUIElementLabelWithDefault(baseName: string, defaultValue: string, modes: string[]): string {
        var result = this.findMatch(this.LOOKUP_GUI_LABELS, 'Object', baseName, null, true, this.NO_MATCH, modes);

        return ((result == this.NO_MATCH) ? this.NextMessageSourceConfig.getGUIElementLabelWithDefault(baseName, defaultValue, modes) : result);
    }

    getGUIElement(theClass: string, primaryItemName: string, secondaryItemName: string, guiType: string, defaultValue: string, modes: string[]): string {
        var itemName = (primaryItemName + (secondaryItemName == null ? "" : "_" + secondaryItemName));

        itemName = itemName ? itemName.replace(' ', "_") : '';

        var result = this.findMatch(this.LOOKUP_GUI_LABELS, theClass, itemName, guiType, true, this.NO_MATCH, modes);

        return ((result == this.NO_MATCH) ? this.NextMessageSourceConfig.getGUIElement(theClass, primaryItemName, secondaryItemName, guiType, defaultValue, modes) : result);
    }

    getFieldErrorMsg(fieldName: string, tokenReplacements, modes: string[]): string {
        return this.getFieldErrorMsgWithType(fieldName, null, tokenReplacements, modes);
    }

    getFieldErrorMsgWithType(fieldName: string, errorType: string, tokenReplacements, modes: string[]): string {
        var obj: string             =   this.getObjectName(fieldName);
        var actualFieldName: string =   this.getActualFieldName(fieldName);
        var msg                     =   this.findMatch(this.LOOKUP_MESSAGE, obj, actualFieldName, errorType, true, this.NO_MATCH, modes);

        if (msg == this.NO_MATCH) {
            return this.NextMessageSourceConfig.getFieldErrorMsgWithType(fieldName, errorType, tokenReplacements, modes);
        }

        if (msg == null) {
            throw new Error("No mesages in the database");
        }

        let params: Map<string, string> = new Map<string, string>();

        params.set("name", this.getFieldDescriptor(fieldName, null, modes));

        if (tokenReplacements && Object.keys(tokenReplacements)) {
            Object.keys(tokenReplacements).forEach(key => {
                params.set(key, tokenReplacements[key]);
            });
        }
        return this.utilsService.replaceParams(msg, params);
    }

    //getDescriptionForEnum Not yet implemented

    getObjectName(fieldName: string) : string {
        return (fieldName && fieldName.indexOf('.') != -1) ? fieldName.substr(0, fieldName.indexOf('.')) : null;
    }

    getActualFieldName(fieldName: string) : string {
        return (fieldName && fieldName.indexOf('.') != -1) ? fieldName.substr(fieldName.indexOf('.') + 1) : fieldName;
    }
}

export class DefaultMessageSourceConfig extends MessageSourceConfig {

    translateLabel(fieldName: string, subType: string, defaultValue: string, modes: string[]): string {
        if (defaultValue) {
            return defaultValue;
        }
        return this.getActualFieldName(fieldName);
    }

    getFieldDescriptor(fieldName: string, subType: string, modes: string[]): string {
        return this.getActualFieldName(fieldName);
    }

    getGUIElementLabel(baseName: string, modes: string[]): string {
        return baseName;
    }

    getGUIElementLabelWithDefault(baseName: string, defaultValue: string, modes: string[]): string {
        return defaultValue;
    }

    getGUIElement(theClass: string, primaryItemName: string, secondaryItemName: string, guiType: string, defaultValue: string, modes: string[]): string {
        return defaultValue;
    }

    getFieldErrorMsg(fieldName: string, tokenReplacements, modes: string[]): string {
        return null;
    }

    getFieldErrorMsgWithType(fieldName: string, errorType: string, tokenReplacements, modes: string[]): string {
        return null;
    }
}

export class MessageSelector {
    ClassName: string;
    FieldName: string;
    Mode: string;
    Subtype: string;
    Value: string;
}