const MamiArguments = (function() {
    return {
        type: (name, type, fallback, throwOnFail) => {
            if(typeof name !== 'string')
                throw 'name must be a string';
            if(typeof type !== 'string')
                throw 'type must be a string';

            return { name: name, type: type, fallback: fallback, throwOnFail: !!throwOnFail };
        },

        check: (name, fallback, check, throwOnFail) => {
            if(typeof name !== 'string')
                throw 'name must be a string';
            if(typeof check !== 'function')
                throw 'check must be a function';

            return { name: name, fallback: fallback, check: check, throwOnFail: !!throwOnFail };
        },

        filter: (name, type, fallback, filter, throwOnFail) => {
            if(typeof name !== 'string')
                throw 'name must be a string';
            if(typeof type !== 'string')
                throw 'type must be a string';
            if(typeof filter !== 'function')
                throw 'filter must be a function';

            return { name: name, type: type, fallback: fallback, filter: filter, throwOnFail: !!throwOnFail };
        },

        verify: (args, options) => {
            if(!Array.isArray(options))
                throw 'options must be an array';
            if(typeof args !== 'object')
                args = {};

            for(const option of options) {
                if(typeof option !== 'object')
                    throw 'entries of options must be objects';
                if(!('name' in option) || typeof option.name !== 'string')
                    throw 'option.name must be a string';
                const name = option.name;
                const type = 'type' in option && typeof option.type === 'string' ? option.type : undefined;
                const fallback = 'fallback' in option ? option.fallback : undefined;
                const check = 'check' in option && typeof option.check === 'function' ? option.check : undefined;
                const filter = 'filter' in option && typeof option.filter === 'function' ? option.filter : undefined;
                const throwOnFail = 'throwOnFail' in option && option.throwOnFail;

                if(!(name in args)
                    || (type !== undefined && typeof args[name] !== type)
                    || (check !== undefined && !check(args[name]))) {
                    if(throwOnFail)
                        throw `${name} is invalid`;

                    args[name] = fallback;
                } else if(filter !== undefined)
                    args[name] = filter(args[name]);
            }

            return args;
        },
    };
})();
