// The MIT License (MIT)
//
// Copyright (c) 2014-2019 Eric J Nesser
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//     The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
//     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//

/********************************************************************************
 vCards-js, Eric J Nesser, November 2014
 ********************************************************************************/
/**
 * Represents a contact that can be imported into Outlook, iOS, Mac OS, Android devices, and more
 */

var __vCard = (function () {
    'use strict';

    var vCardFormatter = (function () {
        var majorVersion = '3';

        /**
         * Encode string
         * @param  {String}     value to encode
         * @return {String}     encoded string
         */
        function e(value) {
            if (value) {
                if (typeof(value) !== 'string') {
                    value = '' + value;
                }
                return value.replace(/\n/g, '\\n').replace(/,/g, '\\,').replace(/;/g, '\\;');
            }
            return '';
        }

        /**
         * Return new line characters
         * @return {String} new line characters
         */
        function nl() {
            return '\r\n';
        }

        /**
         * Get formatted photo
         * @param  {String} photoType       Photo type (PHOTO, LOGO)
         * @param  {String} url             URL to attach photo from
         * @param  {String} mediaType       Media-type of photo (JPEG, PNG, GIF)
         * @return {String}                 Formatted photo
         */
        function getFormattedPhoto(photoType, url, mediaType, base64) {

            var params;

            if (majorVersion >= 4) {
                params = base64 ? ';ENCODING=b;MEDIATYPE=image/' : ';MEDIATYPE=image/';
            } else if (majorVersion === 3) {
                params = base64 ? ';ENCODING=b;TYPE=' : ';TYPE=';
            } else {
                params = base64 ? ';ENCODING=BASE64;' : ';';
            }

            var formattedPhoto = photoType + params + mediaType + ':' + e(url) + nl();
            return formattedPhoto;
        }

        /**
         * Get formatted address
         * @param  {object}         address
         * @param  {object}         encoding prefix
         * @return {String}         Formatted address
         */
        function getFormattedAddress(encodingPrefix, address) {

            var formattedAddress = '';

            if (address.details.label ||
                address.details.street ||
                address.details.city ||
                address.details.stateProvince ||
                address.details.postalCode ||
                address.details.countryRegion) {

                if (majorVersion >= 4) {
                    formattedAddress = 'ADR' + encodingPrefix + ';TYPE=' + address.type +
                        (address.details.label ? ';LABEL="' + e(address.details.label) + '"' : '') + ':;;' +
                        e(address.details.street) + ';' +
                        e(address.details.city) + ';' +
                        e(address.details.stateProvince) + ';' +
                        e(address.details.postalCode) + ';' +
                        e(address.details.countryRegion) + nl();
                } else {
                    if (address.details.label) {
                        formattedAddress = 'LABEL' + encodingPrefix + ';TYPE=' + address.type + ':' + e(address.details.label) + nl();
                    }
                    formattedAddress += 'ADR' + encodingPrefix + ';TYPE=' + address.type + ':;;' +
                        e(address.details.street) + ';' +
                        e(address.details.city) + ';' +
                        e(address.details.stateProvince) + ';' +
                        e(address.details.postalCode) + ';' +
                        e(address.details.countryRegion) + nl();

                }
            }

            return formattedAddress;
        }

        /**
         * Convert date to YYYYMMDD format
         * @param  {Date}       date to encode
         * @return {String}     encoded date
         */
        function YYYYMMDD(date) {
            return date.getFullYear() + ('0' + (date.getMonth()+1)).slice(-2) + ('0' + date.getDate()).slice(-2);
        }

        return {

            /**
             * Get formatted vCard in VCF format
             * @param  {object}     vCard object
             * @return {String}     Formatted vCard in VCF format
             */
            getFormattedString: function(vCard) {

                majorVersion = vCard.getMajorVersion();

                var formattedVCardString = '';
                formattedVCardString += 'BEGIN:VCARD' + nl();
                formattedVCardString += 'VERSION:' + vCard.version + nl();

                var encodingPrefix = majorVersion >= 4 ? '' : ';CHARSET=UTF-8';
                var formattedName = vCard.formattedName;

                if (!formattedName) {
                    formattedName = '';

                    [vCard.firstName, vCard.middleName, vCard.lastName]
                    .forEach(function(name) {
                        if (name) {
                            if (formattedName) {
                                formattedName += ' ';
                            }
                        }
                        formattedName += name;
                    });
                }

                formattedVCardString += 'FN' + encodingPrefix + ':' + e(formattedName) + nl();
                formattedVCardString += 'N' + encodingPrefix + ':' +
                    e(vCard.lastName) + ';' +
                    e(vCard.firstName) + ';' +
                    e(vCard.middleName) + ';' +
                    e(vCard.namePrefix) + ';' +
                    e(vCard.nameSuffix) + nl();

                if (vCard.nickname && majorVersion >= 3) {
                    formattedVCardString += 'NICKNAME' + encodingPrefix + ':' + e(vCard.nickname) + nl();
                }

                if (vCard.gender) {
                    formattedVCardString += 'GENDER:' + e(vCard.gender) + nl();
                }

                if (vCard.uid) {
                    formattedVCardString += 'UID' + encodingPrefix + ':' + e(vCard.uid) + nl();
                }

                if (vCard.birthday) {
                    formattedVCardString += 'BDAY:' + YYYYMMDD(vCard.birthday) + nl();
                }

                if (vCard.anniversary) {
                    formattedVCardString += 'ANNIVERSARY:' + YYYYMMDD(vCard.anniversary) + nl();
                }

                if (vCard.email) {
                    if(!Array.isArray(vCard.email)){
                        vCard.email = [vCard.email];
                    }
                    vCard.email.forEach(
                        function(address) {
                            if (majorVersion >= 4) {
                                formattedVCardString += 'EMAIL' + encodingPrefix + ';type=HOME:' + e(address) + nl();
                            } else if (majorVersion >= 3 && majorVersion < 4) {
                                formattedVCardString += 'EMAIL' + encodingPrefix + ';type=HOME,INTERNET:' + e(address) + nl();
                            } else {
                                formattedVCardString += 'EMAIL' + encodingPrefix + ';HOME;INTERNET:' + e(address) + nl();
                            }
                        }
                    );
                }

                if (vCard.workEmail) {
                    if(!Array.isArray(vCard.workEmail)){
                        vCard.workEmail = [vCard.workEmail];
                    }
                    vCard.workEmail.forEach(
                        function(address) {
                            if (majorVersion >= 4) {
                                formattedVCardString += 'EMAIL' + encodingPrefix + ';type=WORK:' + e(address) + nl();
                            } else if (majorVersion >= 3 && majorVersion < 4) {
                                formattedVCardString += 'EMAIL' + encodingPrefix + ';type=WORK,INTERNET:' + e(address) + nl();
                            } else {
                                formattedVCardString += 'EMAIL' + encodingPrefix + ';WORK;INTERNET:' + e(address) + nl();
                            }
                        }
                    );
                }

                if (vCard.otherEmail) {
                    if(!Array.isArray(vCard.otherEmail)){
                        vCard.otherEmail = [vCard.otherEmail];
                    }
                    vCard.otherEmail.forEach(
                        function(address) {
                            if (majorVersion >= 4) {
                                formattedVCardString += 'EMAIL' + encodingPrefix + ';type=OTHER:' + e(address) + nl();
                            } else if (majorVersion >= 3 && majorVersion < 4) {
                                formattedVCardString += 'EMAIL' + encodingPrefix + ';type=OTHER,INTERNET:' + e(address) + nl();
                            } else {
                                formattedVCardString += 'EMAIL' + encodingPrefix + ';OTHER;INTERNET:' + e(address) + nl();
                            }
                        }
                    );
                }

                if (vCard.logo.url) {
                    formattedVCardString += getFormattedPhoto('LOGO', vCard.logo.url, vCard.logo.mediaType, vCard.logo.base64);
                }

                if (vCard.photo.url) {
                    formattedVCardString += getFormattedPhoto('PHOTO', vCard.photo.url, vCard.photo.mediaType, vCard.photo.base64);
                }

                if (vCard.cellPhone) {
                    if(!Array.isArray(vCard.cellPhone)){
                        vCard.cellPhone = [vCard.cellPhone];
                    }
                    vCard.cellPhone.forEach(
                        function(number){
                            if (majorVersion >= 4) {
                                formattedVCardString += 'TEL;VALUE=uri;TYPE="voice,cell":tel:' + e(number) + nl();
                            } else {
                                formattedVCardString += 'TEL;TYPE=CELL:' + e(number) + nl();
                            }
                        }
                    );
                }

                if (vCard.pagerPhone) {
                    if(!Array.isArray(vCard.pagerPhone)){
                        vCard.pagerPhone = [vCard.pagerPhone];
                    }
                    vCard.pagerPhone.forEach(
                        function(number) {
                            if (majorVersion >= 4) {
                                formattedVCardString += 'TEL;VALUE=uri;TYPE="pager,cell":tel:' + e(number) + nl();
                            } else {
                                formattedVCardString += 'TEL;TYPE=PAGER:' + e(number) + nl();
                            }
                        }
                    );
                }

                if (vCard.homePhone) {
                    if(!Array.isArray(vCard.homePhone)){
                        vCard.homePhone = [vCard.homePhone];
                    }
                    vCard.homePhone.forEach(
                        function(number) {
                            if (majorVersion >= 4) {
                                formattedVCardString += 'TEL;VALUE=uri;TYPE="voice,home":tel:' + e(number) + nl();
                            } else {
                                formattedVCardString += 'TEL;TYPE=HOME,VOICE:' + e(number) + nl();
                            }
                        }
                    );
                }

                if (vCard.workPhone) {
                    if(!Array.isArray(vCard.workPhone)){
                        vCard.workPhone = [vCard.workPhone];
                    }
                    vCard.workPhone.forEach(
                        function(number) {
                            if (majorVersion >= 4) {
                                formattedVCardString += 'TEL;VALUE=uri;TYPE="voice,work":tel:' + e(number) + nl();

                            } else {
                                formattedVCardString += 'TEL;TYPE=WORK,VOICE:' + e(number) + nl();
                            }
                        }
                    );
                }

                if (vCard.homeFax) {
                    if(!Array.isArray(vCard.homeFax)){
                        vCard.homeFax = [vCard.homeFax];
                    }
                    vCard.homeFax.forEach(
                        function(number) {
                            if (majorVersion >= 4) {
                                formattedVCardString += 'TEL;VALUE=uri;TYPE="fax,home":tel:' + e(number) + nl();

                            } else {
                                formattedVCardString += 'TEL;TYPE=HOME,FAX:' + e(number) + nl();
                            }
                        }
                    );
                }

                if (vCard.workFax) {
                    if(!Array.isArray(vCard.workFax)){
                        vCard.workFax = [vCard.workFax];
                    }
                    vCard.workFax.forEach(
                        function(number) {
                            if (majorVersion >= 4) {
                                formattedVCardString += 'TEL;VALUE=uri;TYPE="fax,work":tel:' + e(number) + nl();

                            } else {
                                formattedVCardString += 'TEL;TYPE=WORK,FAX:' + e(number) + nl();
                            }
                        }
                    );
                }

                if (vCard.otherPhone) {
                    if(!Array.isArray(vCard.otherPhone)){
                        vCard.otherPhone = [vCard.otherPhone];
                    }
                    vCard.otherPhone.forEach(
                        function(number) {
                            if (majorVersion >= 4) {
                                formattedVCardString += 'TEL;VALUE=uri;TYPE="voice,other":tel:' + e(number) + nl();

                            } else {
                                formattedVCardString += 'TEL;TYPE=OTHER:' + e(number) + nl();
                            }
                        }
                    );
                }

                [{
                    details: vCard.homeAddress,
                    type: 'HOME'
                }, {
                    details: vCard.workAddress,
                    type: 'WORK'
                }].forEach(
                    function(address) {
                        formattedVCardString += getFormattedAddress(encodingPrefix, address);
                    }
                );

                if (vCard.title) {
                    formattedVCardString += 'TITLE' + encodingPrefix + ':' + e(vCard.title) + nl();
                }

                if (vCard.role) {
                    formattedVCardString += 'ROLE' + encodingPrefix + ':' + e(vCard.role) + nl();
                }

                if (vCard.organization) {
                    formattedVCardString += 'ORG' + encodingPrefix + ':' + e(vCard.organization) + nl();
                }

                if (vCard.url) {
                    formattedVCardString += 'URL' + encodingPrefix + ':' + e(vCard.url) + nl();
                }

                if (vCard.workUrl) {
                    formattedVCardString += 'URL;type=WORK' + encodingPrefix + ':' + e(vCard.workUrl) + nl();
                }

                if (vCard.note) {
                    formattedVCardString += 'NOTE' + encodingPrefix + ':' + e(vCard.note) + nl();
                }

                if (vCard.socialUrls) {
                    for (var key in vCard.socialUrls) {
                        if (vCard.socialUrls.hasOwnProperty(key) &&
                            vCard.socialUrls[key]) {
                            formattedVCardString += 'X-SOCIALPROFILE;TYPE=' + key + ':' + e(vCard.socialUrls[key]) + nl();
                        }
                    }
                }

                if (vCard.source) {
                    formattedVCardString += 'SOURCE' + encodingPrefix + ':' + e(vCard.source) + nl();
                }

                formattedVCardString += 'REV:' + (new Date()).toISOString() + nl();

                if (vCard.isOrganization) {
                    formattedVCardString += 'X-ABShowAs:COMPANY' + nl();
                }

                formattedVCardString += 'END:VCARD' + nl();
                return formattedVCardString;
            }
        };
    })();

    /**
     * Get photo object for storing photos in vCards
     */
    function getPhoto() {
        return {
            url: '',
            mediaType: '',
            base64: false,

            /**
             * Attach a photo from a URL
             * @param  {string} url       URL where photo can be found
             * @param  {string} mediaType Media type of photo (JPEG, PNG, GIF)
             */
            attachFromUrl: function(url, mediaType) {
                this.url = url;
                this.mediaType = mediaType;
                this.base64 = false;
            },

            /**
             * Embed a photo from a file using base-64 encoding (not implemented yet)
             * @param  {string} filename
             */
            embedFromFile: function(fileLocation) {
                var fs   = require('fs');
                var path = require('path');
                this.mediaType = path.extname(fileLocation).toUpperCase().replace(/\./g, "");
                var imgData = fs.readFileSync(fileLocation);
                this.url = imgData.toString('base64');
                this.base64 = true;
            },

            /**
             * Embed a photo from a base-64 string
             * @param  {string} base64String
             */
            embedFromString: function(base64String, mediaType) {
                this.mediaType = mediaType;
                this.url = base64String;
                this.base64 = true;
            }
        };
    }

    /**
     * Get a mailing address to attach to a vCard.
     */
    function getMailingAddress() {
        return {
            /**
             * Represents the actual text that should be put on the mailing label when delivering a physical package
             * @type {String}
             */
            label: '',

            /**
             * Street address
             * @type {String}
             */
            street: '',

            /**
             * City
             * @type {String}
             */
            city: '',

            /**
             * State or province
             * @type {String}
             */
            stateProvince: '',

            /**
             * Postal code
             * @type {String}
             */
            postalCode: '',

            /**
             * Country or region
             * @type {String}
             */
            countryRegion: ''
        };
    }

    /**
     * Get social media URLs
     * @return {object} Social media URL hash group
     */
    function getSocialUrls() {
        return {
            'facebook': '',
            'linkedIn': '',
            'twitter': '',
            'flickr': ''
        };
    }

    /********************************************************************************
     * Public interface for vCard
     ********************************************************************************/
    return {

        /**
         * Specifies a value that represents a persistent, globally unique identifier associated with the vCard
         * @type {String}
         */
        uid: '',

        /**
         * Date of birth
         * @type {Datetime}
         */
        birthday: '',

        /**
         * Cell phone number
         * @type {String}
         */
        cellPhone: '',

        /**
         * Other cell phone number or pager
         * @type {String}
         */
        pagerPhone: '',

        /**
         * The address for private electronic mail communication
         * @type {String}
         */
        email: '',

        /**
         * The address for work-related electronic mail communication
         * @type {String}
         */
        workEmail: '',

        /**
         * First name
         * @type {String}
         */
        firstName: '',

        /**
         * Formatted name string associated with the vCard object (will automatically populate if not set)
         * @type {String}
         */
        formattedName: '',

        /**
         * Gender.
         * @type {String} Must be M or F for Male or Female
         */
        gender: '',

        /**
         * Home mailing address
         * @type {object}
         */
        homeAddress: getMailingAddress(),

        /**
         * Home phone
         * @type {String}
         */
        homePhone: '',

        /**
         * Home facsimile
         * @type {String}
         */
        homeFax: '',

        /**
         * Last name
         * @type {String}
         */
        lastName: '',

        /**
         * Logo
         * @type {object}
         */
        logo: getPhoto(),

        /**
         * Middle name
         * @type {String}
         */
        middleName: '',

        /**
         * Prefix for individual's name
         * @type {String}
         */
        namePrefix: '',

        /**
         * Suffix for individual's name
         * @type {String}
         */
        nameSuffix: '',

        /**
         * Nickname of individual
         * @type {String}
         */
        nickname: '',

        /**
         * Specifies supplemental information or a comment that is associated with the vCard
         * @type {String}
         */
        note: '',

        /**
         * The name and optionally the unit(s) of the organization associated with the vCard object
         * @type {String}
         */
        organization: '',

        /**
         * Individual's photo
         * @type {object}
         */
        photo: getPhoto(),

        /**
         * The role, occupation, or business category of the vCard object within an organization
         * @type {String}
         */
        role: '',

        /**
         * Social URLs attached to the vCard object (ex: Facebook, Twitter, LinkedIn)
         * @type {String}
         */
        socialUrls: getSocialUrls(),

        /**
         * A URL that can be used to get the latest version of this vCard
         * @type {String}
         */
        source: '',

        /**
         * Specifies the job title, functional position or function of the individual within an organization
         * @type {String}
         */
        title: '',

        /**
         * URL pointing to a website that represents the person in some way
         * @type {String}
         */
        url: '',

        /**
         * URL pointing to a website that represents the person's work in some way
         * @type {String}
         */
        workUrl: '',

        /**
         * Work mailing address
         * @type {object}
         */
        workAddress: getMailingAddress(),

        /**
         * Work phone
         * @type {String}
         */
        workPhone: '',

        /**
         * Work facsimile
         * @type {String}
         */
        workFax: '',

        /**
         * vCard version
         * @type {String}
         */
        version: '3.0',

        /**
         * Get major version of the vCard format
         * @return {integer}
         */
        getMajorVersion: function() {
            var majorVersionString = this.version ? this.version.split('.')[0] : '4';
            if (!isNaN(majorVersionString)) {
                return parseInt(majorVersionString);
            }
            return 4;
        },

        /**
         * Get formatted vCard
         * @return {String} Formatted vCard in VCF format
         */
        getFormattedString: function() {
            //var vCardFormatter = require('./lib/vCardFormatter');
            return vCardFormatter.getFormattedString(this);
        },

        /**
         * Save formatted vCard to file
         * @param  {String} filename
         */
        saveToFile: function(filename) {
            //var vCardFormatter = require('./lib/vCardFormatter');
            var contents = vCardFormatter.getFormattedString(this);

            var fs = require('fs');
            fs.writeFileSync(filename, contents, { encoding: 'utf8' });
        }
    };
});

