
(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD
        define([], factory);
    } else if (typeof exports === 'object') {
        module.exports = factory();
    } else {
        // Browser globals (root is window)
        root.Ndef= factory();
    }
}(this, function () {
    /**
     * Utility functions used for working with NDEF
     *
     * @constructor
     * @throws if you attempt to intsantiate NdefUtils
     */
    var NdefUtils = function() {
        throw "Dont construct NdefUtils!";
    };

    /**
     * Check if a value has a bit flag
     *
     * @param {!integer} value
     * @param {!integer} flag
     * @return {!boolean} true if flag is set, false otherwise
     */
    NdefUtils.hasFlag = function(value,flag) {
        return (value & flag) === flag;
    };

    /**
     * Get the type from this NDEF Record
     *
     * @constructor
     * @this {NdefRecord}
     * @param {!boolean} chunked if this record has a chunked flag
     * @param {!integer} tnf the Type Name Format for this record
     * @param {!Uint8Array} type the type data for this record
     * @param {?Uint8Array} id the id for this record, if present
     * @param {!payload} payload the payload contained in this record
     */
    var NdefRecord = function (chunked,tnf,type,id,payload) {
        if(typeof chunked == "undefined" ||
                typeof tnf == "undefined" ||
                typeof type == "undefined" ||
                typeof id == "undefined" ||
                typeof payload == "undefined") {
                    throw "Parameters missing from NdefRecord constructor call";
                }

        if(chunked === null ||
                tnf === null ||
                type === null ||
                payload === null) {
                    throw "Passed null to non-nullable constructor parameter in NdefRecord";
                }


        this.chunked = chunked;
        this.tnf = tnf;
        this.type = type;
        this.id = id;
        this.payload = payload;
    };

    /**
     * These flags and masks are used for packing/unpacking
     * the record header
     */
    NdefRecord.HDR_FLAG_MESSAGE_BEGIN = 0x80;
    NdefRecord.HDR_FLAG_MESSAGE_END = 0x40;
    NdefRecord.HDR_FLAG_CHUNKED = 0x20;
    NdefRecord.HDR_FLAG_SHORT_RECORD = 0x10;
    NdefRecord.HDR_FLAG_ID_LENGTH_PRESENT = 0x08;
    NdefRecord.HDR_MASK_TNF = 0x07;

    /**
     * Type Name Format values
     */
    NdefRecord.TNF_EMPTY = 0x00;
    NdefRecord.TNF_WELL_KNOWN = 0x01;
    NdefRecord.TNF_MEDIA = 0x02;
    NdefRecord.TNF_ABSOLUTE_URI = 0x03;
    NdefRecord.TNF_EXTERNAL = 0x04;
    NdefRecord.TNF_UNKNOWN = 0x05;
    NdefRecord.TNF_UNCHANGED = 0x06;
    NdefRecord.TNF_RESERVED = 0x07;

    /**
     * NDEF URI Prefixes
     */
    NdefRecord.URI_PRE_NONE = 0x00;
    NdefRecord.URI_PRE_HTTP_WWW = 0x01;
    NdefRecord.URI_PRE_HTTPS_WWW = 0x02;
    NdefRecord.URI_PRE_HTTP = 0x03;
    NdefRecord.URI_PRE_HTTPS = 0x04;
    NdefRecord.URI_PRE_TEL = 0x05;
    NdefRecord.URI_PRE_MAILTO = 0x06;
    NdefRecord.URI_PRE_FTP_ANON = 0x07;
    NdefRecord.URI_PRE_FTP_FTP = 0x08; // ftp://ftp
    NdefRecord.URI_PRE_FTPS = 0x09;
    NdefRecord.URI_PRE_SFTP = 0x0A;
    NdefRecord.URI_PRE_SMB = 0x0B;
    NdefRecord.URI_PRE_NFS = 0x0C;
    NdefRecord.URI_PRE_FTP = 0x0D;
    NdefRecord.URI_PRE_DAV = 0x0E;
    NdefRecord.URI_PRE_NEWS = 0x0F;
    NdefRecord.URI_PRE_TELNET = 0x10;
    NdefRecord.URI_PRE_IMAP = 0x11;
    NdefRecord.URI_PRE_RTSP = 0x12;
    NdefRecord.URI_PRE_URN = 0x13;
    NdefRecord.URI_PRE_POP = 0x14;
    NdefRecord.URI_PRE_SIP = 0x15;
    NdefRecord.URI_PRE_SIPS = 0x16;
    NdefRecord.URI_PRE_TFTP = 0x17;
    NdefRecord.URI_PRE_BTSPP = 0x18;
    NdefRecord.URI_PRE_BTL2CAP = 0x19;
    NdefRecord.URI_PRE_BTGOEP = 0x1A;
    NdefRecord.URI_PRE_TCPOBEX = 0x1B;
    NdefRecord.URI_PRE_IRDAOBEX = 0x1C;
    NdefRecord.URI_PRE_FILE = 0x1D;
    NdefRecord.URI_PRE_URN_EPC_ID = 0x1E;
    NdefRecord.URI_PRE_URN_EPC_TAG = 0x1F;
    NdefRecord.URI_PRE_URN_EPC_PAT = 0x20;
    NdefRecord.URI_PRE_URN_EPC_RAW = 0x21;
    NdefRecord.URI_PRE_URN_EPC = 0x22;
    NdefRecord.URI_PRE_URN_NFC = 0x23;

    /**
     * Indicate if this record has its chunked flag set
     *
     * @return {!boolean} isChunked;
     */
    NdefRecord.prototype.isChunked = function() {
        return this.chunked;
    };

    /**
     * Get the TNF for this record
     *
     * @return {!integer} tnf;
     */
    NdefRecord.prototype.getTnf = function() {
        return this.tnf;
    };

    /**
     * Get the type from this NDEF Record
     *
     * @return {!Uint8Array} type;
     */
    NdefRecord.prototype.getType = function() {
        return this.type;
    };

    /**
     * Get the id from this NDEF Record
     *
     * @return {?Uint8Array} id;
     */
    NdefRecord.prototype.getId = function() {
        return this.id;
    };

    /**
     * Get the payload from this NDEF Record
     *
     * @return {!Uint8Array} payload;
     */
    NdefRecord.prototype.getPayload = function() {
        return this.payload;
    };

    /**
     * Convert an NDEF Record to a byte array for writing
     *
     * @param {boolean} isBegin if this record starts a message
     * @param {boolean} isEnd if this record concludes a message
     * @return {Uint8Array} byte array representation
     */
    NdefRecord.prototype.toByteArray = function(isBegin, isEnd) {
        var shortRecord = true;
        var hasId = true;

        if(this.payload.length >= 255) {
            shortRecord = false;
        }

        if(typeof this.id === "undefined" || this.id === null || this.id.length === 0) {
            hasId = false;
        }

        var messageSize = 2; // control byte, type length

        messageSize += this.type.length;
        if(shortRecord) {
            messageSize += 1; // single byte length
        }
        else {
            messageSize += 4; // four byte length
        }

        if(hasId) {
            messageSize += 1; // id length
            messageSize += this.id.length;
        }

        messageSize += this.payload.length;

        var result = new Uint8Array(messageSize);

        // this mask should be unecessary
        var firstByte = (this.tnf & NdefRecord.HDR_MASK_TNF);

        if(shortRecord) {
            firstByte |= NdefRecord.HDR_FLAG_SHORT_RECORD;
        }

        if(hasId) {
            firstByte |= NdefRecord.HDR_FLAG_ID_LENGTH_PRESENT;
        }

        if(isBegin) {
            firstByte |= NdefRecord.HDR_FLAG_MESSAGE_BEGIN;
        }

        if(isEnd) {
            firstByte |= NdefRecord.HDR_FLAG_MESSAGE_END;
        }

        if(this.isChunked()) {
            firstByte |= NdefRecord.HDR_FLAG_CHUNKED;
        }

        var count = 0;

        result[count++] = firstByte;
        result[count++] = this.type.length;

        if(shortRecord) {
            result[count++] = 0xff & this.payload.length;
        }
        else {
            result[count++] = 0xff & (this.payload.length >>> 24);
            result[count++] = 0xff & (this.payload.length >>> 16);
            result[count++] = 0xff & (this.payload.length >>> 8);
            result[count++] = 0xff & (this.payload.length >>> 0);
        }

        if(hasId) {
            result[count++] = 0xff & this.id.length;
        }

        //add type bytes
        for(var typeCntr = 0; typeCntr < this.type.length; typeCntr++) {
            result[count++] = this.type[typeCntr];
        }

        if(hasId) {
            for(var idCntr = 0; idCntr < this.id.length; idCntr++) {
                result[count++] = this.id[idCntr];
            }
        }

        for(var pldCntr = 0; pldCntr < this.payload.length; pldCntr++) {
            result[count++] = this.payload[pldCntr];
        }

        if(result.length !== count) {
            throw "NDEF Record was not successfully generated";
        }

        return result;
    };

    /**
     * Constructs an NdefMessage from an array of NdefRecord
     *
     * @constructor
     * @param {!NdefRecord[]}  ndefRecords the records this message contains
     */
    var NdefMessage = function(ndefRecords) {
        if(typeof ndefRecords === "undefined" ||
                ndefRecords === null ||
                ndefRecords.length === 0) {
                    throw "You must supply a non-zero length array of NdefRecords to construct an NdefMessage";
                }
        this.ndefRecords = ndefRecords;
    };


    /**
     * Returns the array of records this NdefMessage contains
     *
     * @return {!NdefRecord[]} the NDEF records contained
     */
    NdefMessage.prototype.getRecords = function() {
        return this.ndefRecords;
    };

    /**
     * Returns a byte array of the message ready to be written to a tag
     *
     * @return {!Uint8Array} byte array representation
     */
    NdefMessage.prototype.toByteArray = function() {
        var result = new Uint8Array(0);
        for(var i = 0; i < this.ndefRecords.length; i++) {
            // this is very inefficient, but it shouldn't really matter
            // unless very large multi-record messages are being constructed
            var recordBytes = this.ndefRecords[i].toByteArray(i===0, i===(this.ndefRecords.length - 1));
            var grownArray = new Uint8Array(result.length + recordBytes.length);
            grownArray.set(result);
            grownArray.set(recordBytes,result.length);
            result = grownArray;
        }
        return result;
    };

    /**
     * Creates an NdefMessage from a byte array
     *
     * @param {!Uint8Array} bytes
     * @return {?NdefMessage} resulting message, null if a parse error occured
     */
    NdefMessage.fromBytes = function(bytes) {
        if(typeof bytes === "undefined" ||
                bytes === null) {
                    throw "Bytes must be defined and non-null";
                }
        // fixes odd issue when normal arrays are passed 
        bytes = new Uint8Array(bytes);
        // theoretical minimum is 3 bytes
        if(bytes.length < 3) {
            throw "Byte array is too short to contain any kind of NDEF message";
        }


        var throwOnMsgTooShort = function(bytes, idx) {
            if(bytes.length < (idx+1)) {
                throw "Message ended abruptly, trying to access index "+idx+" from an array of "+bytes.length+" items.";
            }
        };

        var ndefRecords = [];
        var done = false;
        var foundStart = false;

        var currentIndex = 0;
        while(!done) {
            if(currentIndex >= bytes.length) {
                // ran out of bytes before message ended
                throw "Ran out of bytes before message started";
            }

            var recordFirstByte = bytes[currentIndex];


            // check for message beginning
            foundStart = foundStart || NdefUtils.hasFlag(recordFirstByte,NdefRecord.HDR_FLAG_MESSAGE_BEGIN);

            if(foundStart) {
                done = NdefUtils.hasFlag(recordFirstByte,NdefRecord.HDR_FLAG_MESSAGE_END);
                var isChunked = NdefUtils.hasFlag(recordFirstByte,NdefRecord.HDR_FLAG_CHUNKED);
                var isShortRecord = NdefUtils.hasFlag(recordFirstByte,NdefRecord.HDR_FLAG_SHORT_RECORD);
                var hasIdLength = NdefUtils.hasFlag(recordFirstByte,NdefRecord.HDR_FLAG_ID_LENGTH_PRESENT);

                var tnf = recordFirstByte & NdefRecord.HDR_MASK_TNF;

                var typeIdx = currentIndex + 1;
                throwOnMsgTooShort(bytes,typeIdx);
                var typeLength = bytes[typeIdx];

                var payloadLength = 0;
                var payloadLengthLength = isShortRecord ? 1 : 4;
                var payloadLengthStartIdx = typeIdx+1;

                if(isShortRecord) {
                    throwOnMsgTooShort(bytes,payloadLengthStartIdx);
                    payloadLength = bytes[payloadLengthStartIdx];
                }
                else {
                    throwOnMsgTooShort(bytes,payloadLengthStartIdx+3);
                    var valueArr = new Uint32Array(bytes.slice(payloadLengthStartIdx,payloadStartIdx+4));
                    payloadLength = valueArr[0];
                }

                var idLength = 0;
                var idLengthStartIdx = payloadLengthStartIdx+payloadLengthLength;

                throwOnMsgTooShort(bytes,idLengthStartIdx);
                if(hasIdLength) {
                    idLength = bytes[idLengthStartIdx];
                }
                
                var idLengthEndIdx = idLengthStartIdx + (hasIdLength ? 1 : 0);
                var typeEndIdx = idLengthEndIdx + typeLength;
                throwOnMsgTooShort(bytes,typeEndIdx);
                var type = new Uint8Array(bytes.buffer,idLengthEndIdx,typeLength);

                var idStartIdx = typeEndIdx;                
                throwOnMsgTooShort(bytes,idStartIdx+idLength);
                var id = new Uint8Array(bytes.buffer,idStartIdx,idLength);

                var payloadStartIdx = idStartIdx+idLength;
                throwOnMsgTooShort(bytes,payloadStartIdx+payloadLength-1);
                var payload = new Uint8Array(bytes.buffer,payloadStartIdx,payloadLength);

                currentIndex = payloadStartIdx+payloadLength;
                ndefRecords.push(new NdefRecord(isChunked,tnf,type,id,payload));
            }
            else {
                currentIndex++;
            }
        }

        return new NdefMessage(ndefRecords);
    };

    /**
     * Utility functions for Ndef operations and debugging
     *
     * @version 1.0.2
     */
    var NdefRecordUtils = function() {
        throw "Do not instantiate ndef record utils";
    };

    /**
     * Resolves a URI into the appropriate NDEF record using 
     * the standard URI prefixes
     *
     * @param {string} uri uri to write
     * @returns {NdefRecord} record with the uri prefix extracted
     */
    NdefRecordUtils.createUriRecord = function(uri) {
        var parsed = NdefRecordUtils.resolveUriToPrefix(uri);
        var prefixCode = parsed.prefixCode;
        var content = parsed.content;

        var contentArray = NdefRecordUtils.stringToUint8Array(content);

        var payload = new Uint8Array(contentArray.length+1);
        payload[0] = prefixCode;
        payload.set(contentArray,1);

        return new NdefRecord(false,NdefRecord.TNF_WELL_KNOWN,new Uint8Array([0x55]),null,payload);
    };

    /**
     * Resolves a string into the appropriate UTF8 
     * text record
     *
     * @param {string} content record contents
     * @param {?string} language language code for record
     * @returns {NdefRecord}
     */
    NdefRecordUtils.createTextRecord = function(content, language) {

        var contentArray = NdefRecordUtils.stringToUint8Array(content);
        if(typeof language === "undefined") {
            language = "en";
        }
        var languageArray = NdefRecordUtils.stringToUint8Array(language);


        var payload = new Uint8Array(contentArray.length+languageArray.length+1);
        payload[0] = languageArray.length;
        payload.set(languageArray,1);
        payload.set(contentArray,1+languageArray.length);

        return new NdefRecord(false,NdefRecord.TNF_WELL_KNOWN,new Uint8Array([0x54]),null,payload);
    };

    /**
     * Internal function for converting a string
     * into a Uint8Array for creating NdefRecords
     *
     * @param {string} string record contents
     * @return {Uint8Array} binary representation
     */
    NdefRecordUtils.stringToUint8Array = function(string) {
        var escstr = encodeURIComponent(string);
        var binstr = escstr.replace(/%([0-9A-F]{2})/g, function(match, p1) {
            return String.fromCharCode('0x' + p1);
        });
        var ua = new Uint8Array(binstr.length);
        Array.prototype.forEach.call(binstr, function (ch, i) {
            ua[i] = ch.charCodeAt(0);
        });
        return ua;
    };

    /**
     * Internal function for converting a Uint8Array 
     * into a string for parsing NdefRecords
     *
     * @param {Uint8Array} binary representation
     * @return {string} string record contents
     */
    NdefRecordUtils.uint8ArrayToString = function(arr) {
        var binstr = Array.prototype.map.call(arr, function (ch) {
            return String.fromCharCode(ch);
        }).join('');

        var escstr = binstr.replace(/(.)/g, function (m, p) {
            var code = p.charCodeAt(0).toString(16).toUpperCase();
            if (code.length < 2) {
                code = '0' + code;
            }
            return '%' + code;
        });
        return decodeURIComponent(escstr);    
    };

    /**
     * Resolves a URI into the appropriate prefix/data
     * pair for NDEF writing
     *
     * @param {string} uri string of supposed uri
     * @returns { prefixCode: int, prefix: string, content: string, fullUri: string}
     */
    NdefRecordUtils.resolveUriString = function(uri) {
        return NdefRecordUtils.resolveUriToPrefix(uri);
    };

    /**
     * Resolves a URI into the appropriate prefix/data
     * pair for NDEF writing
     *
     * @deprecated Use NdefRecordUtils.resolveUriString instead
     * @param uri string of supposed uri
     * @returns { prefixCode: int, prefix: string, content: string, fullUri: string}
     */
    NdefRecordUtils.resolveUriToPrefix = function(url) {
        var u = url;
        var stripper = function(prefix,prefixCode,fullUri) {
            return {
                "prefixCode": prefixCode,
                    "prefix": prefix,
                    "content": fullUri.slice(prefix.length),
                    "fullUri": fullUri
            };
        };

        if(u.startsWith("http")) {
            if(u.startsWith("https://www.")) {
                return stripper("https://www.",NdefRecord.URI_PRE_HTTPS_WWW,u);
            } else if(u.startsWith("https://")) {
                return stripper("https://",NdefRecord.URI_PRE_HTTPS,u);
            } else if(u.startsWith("http://www.")) {
                return stripper("http://www.",NdefRecord.URI_PRE_HTTP_WWW,u);
            } else if(u.startsWith("http://")) {
                return stripper("http://",NdefRecord.URI_PRE_HTTP,u);
            }
        }
        else if(u.startsWith("ftp")) {
            if(u.startsWith("ftp://ftp.")) {
                return stripper("ftp://ftp.",NdefRecord.URI_PRE_FTP_FTP,u);
            }
            else if (u.startsWith("ftps://")) {
                return stripper("ftps://",NdefRecord.URI_PRE_FTPS,u);
            }
            else if (u.startsWith("ftp://anonymous:anonymous@")) {
                return stripper("ftp://anonymous:anonymous@",NdefRecord.URI_PRE_FTP_ANON,u);
            }
            else if(u.startsWith("ftp://")) {
                return stripper("ftp://",NdefRecord.URI_PRE_FTP,u);
            }
        }
        else if (u.startsWith("mailto:")) {
            return stripper("mailto:",NdefRecord.URI_PRE_MAILTO,u);
        }
        else if (u.startsWith("tel:")) {
            return stripper("tel:",NdefRecord.URI_PRE_TEL,u);
        }
        else if (u.startsWith("sftp://")) {
            return stripper("sftp://",NdefRecord.URI_PRE_SFTP,u);
        }
        else if (u.startsWith("smb://")) {
            return stripper("smb://",NdefRecord.URI_PRE_SMB,u);
        }
        else if (u.startsWith("nfs://")) {
            return stripper("nfs://",NdefRecord.URI_PRE_NFS,u);
        }
        else if (u.startsWith("dav://")) {
            return stripper("dav://",NdefRecord.URI_PRE_DAV,u);
        }
        else if (u.startsWith("news:")) {
            return stripper("news:",NdefRecord.URI_PRE_NEWS,u);
        }
        else if (u.startsWith("telnet://")) {
            return stripper("telnet://",NdefRecord.URI_PRE_TELNET,u);
        }
        else if (u.startsWith("imap:")) {
            return stripper("imap:",NdefRecord.URI_PRE_IMAP,u);
        }
        else if (u.startsWith("rtsp://")) {
            return stripper("rtsp://",NdefRecord.URI_PRE_RTSP,u);
        }
        else if (u.startsWith("pop:")) {
            return stripper("pop:",NdefRecord.URI_PRE_POP,u);
        }
        else if (u.startsWith("sip:")) {
            return stripper("sip:",NdefRecord.URI_PRE_SIP,u);
        }
        else if (u.startsWith("sips:")) {
            return stripper("sips:",NdefRecord.URI_PRE_SIPS,u);
        }
        else if (u.startsWith("tftp:")) {
            return stripper("tftp:",NdefRecord.URI_PRE_TFTP,u);
        }
        else if (u.startsWith("btspp://")) {
            return stripper("btspp://",NdefRecord.URI_PRE_BTSPP,u);
        }
        else if (u.startsWith("btl2cap://")) {
            return stripper("btl2cap://",NdefRecord.URI_PRE_BTL2CAP,u);
        }
        else if (u.startsWith("btgoep://")) {
            return stripper("btgoep://",NdefRecord.URI_PRE_BTGOEP,u);
        }
        else if (u.startsWith("tcpobex://")) {
            return stripper("tcpobex://",NdefRecord.URI_PRE_TCPOBEX,u);
        }
        else if (u.startsWith("irdaobex://")) {
            return stripper("irdaobex://",NdefRecord.URI_PRE_IRDAOBEX,u);
        }
        else if (u.startsWith("file://")) {
            return stripper("file://",NdefRecord.URI_PRE_FILE,u);
        }
        else if (u.startsWith("urn:epc:id:")) {
            return stripper("urn:epc:id:",NdefRecord.URI_PRE_URN_EPC_ID,u);
        }
        else if (u.startsWith("urn:epc:tag:")) {
            return stripper("urn:epc:tag:",NdefRecord.URI_PRE_URN_EPC_TAG,u);
        }
        else if (u.startsWith("urn:epc:pat:")) {
            return stripper("urn:epc:pat:",NdefRecord.URI_PRE_URN_EPC_PAT,u);
        }
        else if (u.startsWith("urn:epc:raw:")) {
            return stripper("urn:epc:raw:",NdefRecord.URI_PRE_EPC_RAW,u);
        }
        else if (u.startsWith("urn:epc:")) {
            return stripper("urn:epc:",NdefRecord.URI_PRE_URN_EPC,u);
        }
        else if (u.startsWith("urn:nfc:")) {
            return stripper("urn:nfc:",NdefRecord.URI_PRE_URN_NFC,u);
        }
        else if (u.startsWith("urn:")) {
            return stripper("urn:",NdefRecord.URI_PRE_URN,u);
        }

        return stripper("",NdefRecord.URI_PRE_NONE,url);
    };

    /**
     * Resolves the completely from a WELL_KNOWN URI record
     * by prepending the appropriate URI prefix.
     *
     * @throws if record isn't a WELL_KNOWN URI
     * @throws if the record prefix isn't known
     * @param {NdefRecord} record to extract string
     * @return {string} resulting URI string
     */
    NdefRecordUtils.resolveUriRecordToString = function(ndefRecord) {
        return NdefRecordUtils.resolveUrlFromPrefix(ndefRecord);  
    };

    /**
     * Resolves the completely from a WELL_KNOWN URI record
     * by prepending the appropriate URI prefix.
     *
     * @deprecated use NdefRecordUtils.resolveUriToString instead
     * @throws if record isn't a WELL_KNOWN URI
     * @throws if the record prefix isn't known
     * @param {NdefRecord} record to extract string
     * @return {string} resulting URI string
     */
    NdefRecordUtils.resolveUrlFromPrefix = function(ndefRecord) {
        if(ndefRecord.getTnf() !== NdefRecord.TNF_WELL_KNOWN || (ndefRecord.getType())[0] !== 0x55) {
            throw "Not a WELL_KNOWN URI record";
        }
        else {
            var payload = ndefRecord.getPayload();
            var prefixCode = payload[0];
            var data = payload.slice(1);
            var prefix = "";

            switch(prefixCode) {
                case NdefRecord.URI_PRE_NONE:
                    break;
                case NdefRecord.URI_PRE_HTTP_WWW:
                    prefix = "http://www.";
                    break;
                case NdefRecord.URI_PRE_HTTPS_WWW:
                    prefix = "https://www.";
                    break;
                case NdefRecord.URI_PRE_HTTP:
                    prefix = "http://";
                    break;
                case NdefRecord.URI_PRE_HTTPS:
                    prefix = "https://";
                    break;
                case NdefRecord.URI_PRE_TEL:
                    prefix = "tel:";
                    break;
                case NdefRecord.URI_PRE_MAILTO:
                    prefix = "mailto:";
                    break;
                case NdefRecord.URI_PRE_FTP_ANON:
                    prefix = "ftp://anonymous:anonymous@";
                    break;
                case NdefRecord.URI_PRE_FTP_FTP:
                    prefix = "ftp://ftp.";
                    break; // ftp://ftp
                case NdefRecord.URI_PRE_FTPS:
                    prefix = "ftps://";
                    break;
                case NdefRecord.URI_PRE_SFTP:
                    prefix = "sftp://";
                    break;
                case NdefRecord.URI_PRE_SMB:
                    prefix = "smb://";
                    break;
                case NdefRecord.URI_PRE_NFS:
                    prefix = "nfs://";
                    break;
                case NdefRecord.URI_PRE_FTP:
                    prefix = "ftp://";
                    break;
                case NdefRecord.URI_PRE_DAV:
                    prefix = "dav://";
                    break;
                case NdefRecord.URI_PRE_NEWS:
                    prefix = "news:";
                    break;
                case NdefRecord.URI_PRE_TELNET:
                    prefix = "telnet://";
                    break;
                case NdefRecord.URI_PRE_IMAP:
                    prefix = "imap:";
                    break;
                case NdefRecord.URI_PRE_RTSP:
                    prefix = "rtsp://";
                    break;
                case NdefRecord.URI_PRE_URN:
                    prefix = "urn:";
                    break;
                case NdefRecord.URI_PRE_POP:
                    prefix = "pop:";
                    break;
                case NdefRecord.URI_PRE_SIP:
                    prefix = "sip:";
                    break;
                case NdefRecord.URI_PRE_SIPS:
                    prefix = "sips:";
                    break;
                case NdefRecord.URI_PRE_TFTP:
                    prefix = "tftp:";
                    break;
                case NdefRecord.URI_PRE_BTSPP:
                    prefix = "btspp://";
                    break;
                case NdefRecord.URI_PRE_BTL2CAP:
                    prefix = "btl2cap://";
                    break;
                case NdefRecord.URI_PRE_BTGOEP:
                    prefix = "btgoep://";
                    break;
                case NdefRecord.URI_PRE_TCPOBEX:
                    prefix = "tcpobex://";
                    break;
                case NdefRecord.URI_PRE_IRDAOBEX:
                    prefix = "irdaobex://";
                    break;
                case NdefRecord.URI_PRE_FILE:
                    prefix = "file://";
                    break;
                case NdefRecord.URI_PRE_URN_EPC_ID:
                    prefix = "urn:epc:id:";
                    break;
                case NdefRecord.URI_PRE_URN_EPC_TAG:
                    prefix = "urn:epc:tag:";
                    break;
                case NdefRecord.URI_PRE_URN_EPC_PAT:
                    prefix = "urn:epc:pat:";
                    break;
                case NdefRecord.URI_PRE_URN_EPC_RAW:
                    prefix = "urn:epc:raw:";
                    break;
                case NdefRecord.URI_PRE_URN_EPC:
                    prefix = "urn:epc:";
                    break;
                case NdefRecord.URI_PRE_URN_NFC:
                    prefix = "urn:nfc:";
                    break;
                default:
                    throw "Invalid URI code";
            }

            return prefix + NdefRecordUtils.uint8ArrayToString(data);
        }
    };

    /**
     * Resolves a WELL_KNOWN text record
     * extracting the language code and content
     *
     * @throws if record isn't a WELL_KNOWN TEXT 
     * @throws if the language code bytes are invalid
     * @returns {language: language code string, content: text contained} or null
     */
    NdefRecordUtils.resolveTextRecord = function(ndefRecord) {
        if(ndefRecord.getTnf() !== NdefRecord.TNF_WELL_KNOWN || (ndefRecord.getType())[0] !== 0x54) {
            throw "Not a WELL_KNOWN text record";
        }
        else {
            var payload = ndefRecord.getPayload();
            if(payload.length === 0) {
                throw "Missing error code";
            }

            //masking out the utf bit
            var languageCodeLength = payload[0] & 0x3F;
            if(payload.length < (languageCodeLength+1)) {
                throw "Payload too short to contain language code";
            }

            var language = NdefRecordUtils.uint8ArrayToString(payload.slice(1,1+languageCodeLength));
            var content = "";

            if(payload.length > 1+languageCodeLength) {
                content = NdefRecordUtils.uint8ArrayToString(payload.slice(1+languageCodeLength));
            }

            return {language: language, content: content}; 
        }
    };

    var Ndef = {};
    Ndef.Message = NdefMessage;
    Ndef.Record = NdefRecord;
    Ndef.Utils = NdefRecordUtils;
    return Ndef;
}));
