function UserService(bip) {
    this.bip = bip;
};

UserService.prototype.all = function () {
    return this.bip.get('/api/Users');
};

UserService.prototype.get = function (id) {
    return this.bip.get('/api/Users/' + id);
};

UserService.prototype.create = function (user) {
    return this.bip.post('/api/Users', user);
};

UserService.prototype.update = function (id, user) {
    return this.bip.put('/api/Users/' + id, user);
};

UserService.prototype.renewLastReview = function (id) {
    return this.bip.put('/api/Users/RenewLastReview/' +id);
};

UserService.prototype.verify = function (details) {
    return this.bip.put('/api/Users/Verify', details);
};

UserService.prototype.authenticate = function (details) {
    return this.bip.post('/api/Tokens', details);
};

UserService.prototype.loginAsOtherUser = function (userId) {
    return this.bip.get('/api/Tokens/' + userId);
};

UserService.prototype.getCurrent = function () {
    return this.bip.get('/api/Users/Current');
};

UserService.prototype.updatePassword = function (credentials) {
    return this.bip.put('/api/Users/Password', credentials);
};

UserService.prototype.resetPassword = function (userName) {
    return this.bip.put('/api/Users/ResetPassword?userName=' + encodeURIComponent(userName));
};

UserService.prototype.resetAccount = function (email) {
    return this.bip.put('/api/Users/' + email + '/ResetAccount');
};

UserService.prototype.linkToM5 = function (credentials) {
    return this.bip.put('/api/Users/M5', credentials);
};

UserService.prototype.getTheme = function () {
    return this.bip.get('/api/Users/Theme');
};

UserService.prototype.setCurrentTheme = function (themeId) {
    return this.bip.put('/api/Users/Theme/' + themeId);
};

UserService.prototype.setTheme = function (userId, themeId) {
    return this.bip.put('/api/Users/' + userId + '/Theme/' + themeId);
};

UserService.prototype.tags = function () {
    return this.bip.get('/api/Users/tags');
};

UserService.prototype.reactivateAccount = function (id) {
    return this.bip.put('/api/Users/' + id + '/Reactivate');
};

UserService.prototype.archive = function (id) {
    return this.bip.put('/api/Users/' + id + '/Archive');
};

UserService.prototype.allActive = function () {
    return this.bip.get('/api/Users/Active');
};
(function (exports) {
    "use strict";

    exports.isTouch = isTouch;
    exports.isIE = isIE;
    exports.rotateVector = rotateVector;
    exports.getRotatedSize = getRotatedSize;
    exports.hexToRgbA = hexToRgbA;
    exports.arraysEqual = arraysEqual;
    exports.distinct = distinct;
    exports.haversineDistance = haversineDistance;
    exports.deg2rad = deg2rad;
    exports.EventSource = EventSource;
    exports.Token = Token;
    exports.URI = URI;
    exports.roundToDp = roundToDp;
    exports.adjustBrightness = adjustBrightness;

    function isTouch() {
        try { document.createEvent("TouchEvent"); return true; }
        catch (e) { return false; }
    }

    function isIE() {
        return navigator.appName === 'Microsoft Internet Explorer' || !!(navigator.userAgent.match(/Trident/) || navigator.userAgent.match(/rv:11/)) || (typeof $.browser !== "undefined" && $.browser.msie === 1);
    }

    /// <summary>
    /// Returns a rotated vector based on the vector and the angle
    /// </summary>
    /// <param name="vec">Vector to rotate</param>
    /// <param name="ang">Angle in degrees</param>
    /// <returns>Returns the rotated vector</returns>
    function rotateVector(vec, ang) {
        ang = -ang * (Math.PI / 180);
        var cos = Math.cos(ang);
        var sin = Math.sin(ang);
        return new Array(Math.round(10000 * (vec[0] * cos - vec[1] * sin)) / 10000, Math.round(10000 * (vec[0] * sin + vec[1] * cos)) / 10000);
    }

    function getRotatedSize(width, height, rotation) {
        var alpha = rotation;

        while (alpha < 0) alpha += 360;

        var gamma = 90;
        var beta = 180 - rotation - gamma;
        
        var a1 = height * Math.sin(alpha * Math.PI / 180) / Math.sin(gamma * Math.PI / 180);
        var b1 = height * Math.sin(beta * Math.PI / 180) / Math.sin(gamma * Math.PI / 180);
        
        var a2 = width * Math.sin(alpha * Math.PI / 180) / Math.sin(gamma * Math.PI / 180);
        var b2 = width * Math.sin(beta * Math.PI / 180) / Math.sin(gamma * Math.PI / 180);

        var width = Math.round(Math.max(Math.abs(b2 + a1), Math.abs(b2 - a1)));
        var height = Math.round(Math.max(Math.abs(b1 + a2), Math.abs(b1 - a2)));

        return { width: width, height: height };
    }

    function hexToRgbA(hex, alpha) {
        var c;
        if (/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)) {
            c = hex.substring(1).split('');
            if (c.length === 3) {
                c = [c[0], c[0], c[1], c[1], c[2], c[2]];
            }
            c = '0x' + c.join('');
            return 'rgba(' + [(c >> 16) & 255, (c >> 8) & 255, c & 255].join(',') + ',' + alpha + ')';
        }
        throw new Error('Bad Hex');
    }

    function arraysEqual(a1, a2) {
        /* WARNING: arrays must not contain {objects} or behavior may be undefined */
        return JSON.stringify(a1.sort()) === JSON.stringify(a2.sort());
    }

    function distinct(value, index, array) { return array.indexOf(value) === index; }

    function haversineDistance(lat1, lon1, lat2, lon2) {
        var R = 6371; // Radius of the earth in km
        var dLat = deg2rad(lat2 - lat1);  // deg2rad below
        var dLon = deg2rad(lon2 - lon1);
        var a =
            Math.sin(dLat / 2) * Math.sin(dLat / 2) +
            Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
            Math.sin(dLon / 2) * Math.sin(dLon / 2)
            ;
        var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        var d = R * c; // Distance in km
        return d;
    };

    function deg2rad(deg) {
        return deg * (Math.PI / 180)
    }

    function EventSource() {
        this.events = [];
    }

    EventSource.prototype.on = function (event, callback, thisArg) {
        if (this.events[event] == undefined) {
            this.events[event] = [];
        }
        var handler = { callback: callback, thisArg: thisArg };
        this.events[event].push(handler);
    };

    EventSource.prototype.off = function (event, callback) {
        if (this.events[event] !== undefined) {
            this.events[event].remove(function (handler) {
                return handler.callback === callback;
            });
        }
    };

    EventSource.prototype.emit = function (event, args) {
        var handlers = this.events[event];
        if (handlers !== undefined) {
            for (var i = 0; i < handlers.length; i++) {
                var handler = handlers[i];
                try {
                    handler.callback.apply(handler.thisArg, args);
                } catch (error) {
                    console.error(error);
                }
            }
        }
    }

    function Token(header, payload, signature) {
        this.header = header;
        this.payload = payload;
        this.signature = signature;
    }

    Token.prototype.duration = function () {
        return (this.payload.exp * 1000) - new Date().getTime();
    };

    Token.prototype.hasRole = function (role) {
        try {
            if (!this.payload.role) return false;
            if (this.payload.role === role) return true;
            if (this.payload.role.includes(role)) return true;
        } catch (e) {
            console.error(e);
            return false;
        }
    };

    Token.decode = function (jwt) {
        function decode(encoded) {
            var b64 = encoded.replace('-', '+').replace('_', '/');
            var json = atob(b64);
            return JSON.parse(json);
        }

        var segments = jwt.split('.');
        var header = decode(segments[0]);
        var payload = decode(segments[1]);
        var signature = segments[2];

        return new Token(header, payload, signature);
    };

    function roundToDp(value, places) {
        return Math.round(value * Math.pow(10, places)) / Math.pow(10, places);
    }

    function adjustBrightness(col, amt) {
        var usePound = false;

        if (col[0] == "#") {
            col = col.slice(1);
            usePound = true;
        }

        var R = parseInt(col.substring(0, 2), 16);
        var G = parseInt(col.substring(2, 4), 16);
        var B = parseInt(col.substring(4, 6), 16);

        // to make the colour less bright than the input
        // change the following three "+" symbols to "-"
        R = R + amt;
        G = G + amt;
        B = B + amt;

        if (R > 255) R = 255;
        else if (R < 0) R = 0;

        if (G > 255) G = 255;
        else if (G < 0) G = 0;

        if (B > 255) B = 255;
        else if (B < 0) B = 0;

        var RR = ((R.toString(16).length == 1) ? "0" + R.toString(16) : R.toString(16));
        var GG = ((G.toString(16).length == 1) ? "0" + G.toString(16) : G.toString(16));
        var BB = ((B.toString(16).length == 1) ? "0" + B.toString(16) : B.toString(16));

        return (usePound ? "#" : "") + RR + GG + BB;
    }

    function URI() {
        this.scheme = undefined;
        this.authentication = undefined;
        this.host = undefined;
        this.port = undefined;
        this.path = undefined;
        this.query = Object.create(null);
        this.fragment = undefined;
    }

    URI.parse = function (uri) {
        var parser = document.createElement('a');
        parser.href = uri;

        var uri = new URI();
        uri.setScheme(parser.protocol);
        uri.setHost(parser.hostname);
        uri.setPort(parser.port);
        uri.setPath(parser.pathname);
        uri.setFragment(parser.hash);

        if (parser.search) {
            var queries = parser.search.substring(1).split('&');
            for (var i = 0; i < queries.length; i++) {
                var query = queries[i].split('=', 2);
                var name = decodeURIComponent(query[0]);
                var value = decodeURIComponent(query[1]);
                uri.addQuery(name, value);
            }
        }

        return uri;
    };

    URI.prototype.setScheme = function (scheme) {
        this.scheme = scheme;
        return this;
    };

    URI.prototype.setAuthentication = function (user, password) {
        if (user) {
            this.authentication = {
                user: user,
                password: password
            };
        } else {
            this.authentication = undefined;
        }
        return this;
    };

    URI.prototype.setHost = function (host) {
        this.host = host;
        return this;
    };

    URI.prototype.setPort = function (port) {
        this.port = port;
        return this;
    };

    URI.prototype.setPath = function (path) {
        this.path = path;
        return this;
    };

    URI.prototype.addQuery = function (name, value) {
        this.query[name] = value;
        return this;
    };

    URI.prototype.setFragment = function (fragment) {
        this.fragment = fragment;
        return this;
    };

    URI.prototype.toString = function () {
        var uri = '';
        if (this.scheme) {
            uri += encodeURIComponent(this.scheme) + '://';
        }
        if (this.authentication) {
            uri += encodeURIComponent(this.authentication.user) + ':' + encodeURIComponent(this.authentication.password) + '@';
        }
        if (this.host) {
            uri += encodeURIComponent(this.host);
            if (this.port) {
                uri += ':' + encodeURIComponent(this.port);
            }
        }
        if (this.host && this.path && !this.path.startsWith('/')) {
            uri += '/';
        }
        if (this.path) {
            uri += this.path;
        }
        if (this.query) {
            uri += '?';
            var parameters = [];
            for (var key in this.query) {
                parameters.push(encodeURIComponent(key) + '=' + encodeURIComponent(this.query[key]));
            }
            uri += parameters.join('&');
        }
        if (this.fragment) {
            uri += '#' + this.fragment;
        }
        return uri;
    };
})(window);
