/*
* profile.js
* Profile Service for AkzoNobel Fusion Website
*
* Written by Tessella plc
* 26 The Quadrant, Abingdon Science Park, Abingdon, Oxfordshire, OX14 3YS
*
* Requires ConsumerProfile webservice, as defined in calls['profile-service-url']
*
* Stub implementation requires external XML files to load test data, defined in:
* calls['get-items-mydulux']
* calls['get-shareditems']
* The brand variable should also be defined.
*
* Copyright 2011 AkzoNobel
*/

var USER_ITEM_TYPES = {
    Room: "Fusion_Room",
    Color: "Fusion_Color",
    Abstract: "Fusion_Abstract",
    Product: "Fusion_Product"
};

function ProfileService(options) {

    //Processes Option flags passed into Profile service when initialised
    if (options == null || options == "") {
        options = {};
    }

    var isStub = options.isStub || false;

    var log = function(message) {
        if (isStub) {
            console.log(message);
        }
    };

    /*Stub Implementation Function */
    var genGuid = function() {
        var guid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
            var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
        });
        return guid;
    };

    if (isStub) {

        calls['profile-service-url'] = 'http://localhost/Profile/webservices/v1/ProfileWebService.svc/pox';
        calls['get-items-mydulux'] = '/_LAYOUTS/Fusion/test/get-icc-items-mydulux.xml';
        calls['get-shareditems'] = '/_LAYOUTS/Fusion/test/get-share-items-mydulux.xml';
    }
    var stubURL = calls['profile-service-url'];

    /*Runs and setups up JavaScript Profile Object*/
    var COOKIE_NAME = 'FusionProfileCookie';
    var websiteUrl = $('.filtersite_url-value').val();
    var createAnonymousProfile = function() {
        var guid = "not_set";
        if (isStub) {
            guid = genGuid();
            jQuery.cookie(COOKIE_NAME, guid, { expires: 30, path: '/' });
            return guid;
        } else {
            jQuery.ajax({
                type: "GET",
                cache: false,
                url: stubURL + "/profile/website/" + websiteUrl,
                dataType: "xml",
                success: function(profileXml) {
                    guid = $('ID', profileXml).text();
                    jQuery.cookie(COOKIE_NAME, guid, { expires: 30, path: '/' });
                },
                async: false
            });
        }
        return guid;
    };

    var profileId;
    if (jQuery.cookie(COOKIE_NAME) == null) {
        profileId = createAnonymousProfile();
    } else {
        profileId = jQuery.cookie(COOKIE_NAME);
    }

    //Private Variables
    var initialised = false;
    var stubIsLoggedIn = false;

    var savedItems;
    var sharedItems;

    var callBackFunctions = [];

    //Private Methods to return URLs for ServerSide ProfileService
    var generateDataURL = function() {
        return "/Profile/" + profileId + "/Website/" + websiteUrl;
    };

    var getSavedItemsURL = function() {
        return (isStub) ? calls['profile-service-url'] : stubURL + generateDataURL() + "/Items";
    };

    var getSharedItemsURL = function() {
        return (isStub) ? calls['profile-service-url'] : stubURL + generateDataURL() + "/SharedItems";
    };

    var getDeleteSavedItemsURL = function() {
        return stubURL + generateDataURL() + "/Items/Delete";
    };

    var getDeleteSharedItemsURL = function() {
        return stubURL + generateDataURL() + "/SharedItems/Delete";
    };

    var addSharedItemsURL = function() {
        return stubURL + generateDataURL() + "/Items/Share";
    };

    var getDeleteAllSharedItemsURL = function() {
        return stubURL + generateDataURL() + "/SharedItems/Clean";
    };

    var getDeleteAllUserItems = function() {
        return stubURL + generateDataURL() + "/Items/Clean";
    };



    //Private methods

    var ajaxErrorHandlers = [];
    var handleAjaxError = function(jqXHR, textStatus, errorThrown) {
        log(jqXHR);
        log(textStatus);
        log(errorThrown);
        while (ajaxErrorHandlers.length > 0) {
            var sub = ajaxErrorHandlers.shift();
            sub(jqXHR, textStatus, errorThrown);
        }
    };

    var getData = function(url, success) {
        jQuery.ajax({
            type: "GET",
            cache: false,
            url: url,
            dataType: "xml",
            success: success,
            error: handleAjaxError
        });
    };

    var updateService = function(data, url, success) {
        if (isStub) {
            success("200");
        } else {
            jQuery.ajax({
                type: "POST",
                data: data,
                contentType: "application/xml; charset=utf-8",
                url: url,
                dataType: "xml",
                success: success,
                error: handleAjaxError
            });
        }
    };

    var defaultPostActionSuccess = function(httpResp) {
        if (isStub) {
            return true;
        } else {
            // Optional section to handle to http response
            // Currently just returns true
            return true;
        }
    };

    var serialiseKey = function(item, key, xmlNode) {
        var elem = "";
        if (item.hasOwnProperty(key) && item[key] !== "" && item[key] !== null) {
            elem = "<" + xmlNode + ">" + item[key] + "</" + xmlNode + ">";
        }
        return elem;
    };

    var serialiseAndEncodeKey = function(item, key, xmlNode) {
        var elem = "";
        if (item.hasOwnProperty(key) && item[key] !== "" && item[key] !== null) {
            elem = "<" + xmlNode + ">" + encodeURIComponent(item[key]) + "</" + xmlNode + ">";
        }
        return elem;
    };

    var parseProduct = function(xmlProduct) {
        var productXML = $('Product', xmlProduct);

        var recommendedPackages = [];
        $('RecommendedPackage', xmlProduct).each(function() {
            recommendedPackages.push({
                amount: $(this).children('Amount').text(),
                size: $(this).children('Size').text()
            });
        });

        var product = {
            productId: $(productXML).children('Id').text(),
            name: $(productXML).children('Name').text(),
            calcAmountOfPaint: $(productXML).children('CalculatedAmountOfPaint').text(),
            recommendedPackages: recommendedPackages,
            thumbLocationUrl: $(productXML).children('ThumbnailLocationUrl').text()
        };

        if ($(productXML).children('Color').length === 1) {
            product.color = parseColor($(productXML).children('Color'));
        }

        return product;
    };

    var serialiseProduct = function(product) {
        var value = "<Product>";
        value += serialiseKey(product, "calcAmountOfPaint", "CalculatedAmountOfPaint");
        if (product.hasOwnProperty('color') && product.color !== "" && product.color !== null) {
            value += serialiseColor(product.color);
        }
        value += serialiseKey(product, "productId", "Id");
        value += serialiseKey(product, "name", "Name");
        value += serialiseKey(product, "thumbLocationUrl", "ThumbnailLocationUrl");

        if ($(product.recommendedPackages).length > 0) {
            var rpackagesXML = "<RecommendedPackages>";
            for (productItem in product.recommendedPackages) {
                var packageXML = '<RecommendedPackage>';
                packageXML += serialiseKey(product.recommendedPackages[productItem], "amount", "Amount");
                packageXML += serialiseKey(product.recommendedPackages[productItem], "size", "Size");
                packageXML += '</RecommendedPackage>';
                rpackagesXML += packageXML;
            }
            rpackagesXML += "</RecommendedPackages>";
            value += rpackagesXML;
        }
        value += "</Product>";
        return value;
    };

    var parseNirvanaImage = function(xmlAbstract) {
        var nirvImgXML = $('NirvanaImageDetails', xmlAbstract);
        var sections = [];
        $('Section', xmlAbstract).each(function() {
            var color;
            if ($(this).children('Color').length === 1) {
                color = parseColor($(this).children('Color'));
            }
            sections.push({
                nirvanaSectionId: $(this).children('NirvanaSectionId').text(),
                isLeadingColor: $(this).children('IsLeadingColor').text(),
                color: color,
                recommendedProductId: $(this).children('RecommendedProductId').text(),
                recommendedProductName: $(this).children('RecommendedProductName').text()
            });
        });

        var abstrct = {
            nirvanaImageLocalId: $(nirvImgXML).children('NirvanaImageLocalId').text(),
            name: decodeURIComponent( $(nirvImgXML).children('Name').text()),
            sections: sections
        };
        return abstrct;
    };

    var serialiseNirvanaImage = function(abstrct) {
        var value = "<NirvanaImageDetails>";
        value += serialiseKey(abstrct, 'nirvanaImageLocalId', 'NirvanaImageLocalId');
        var sections = "<Sections>";
        for (sectionItem in abstrct.sections) {
            var section = "<Section>";
            section += serialiseKey(abstrct.sections[sectionItem], 'nirvanaSectionId', 'NirvanaSectionId');
            section += serialiseKey(abstrct.sections[sectionItem], 'isLeadingColor', 'IsLeadingColor');
            if (abstrct.sections[sectionItem].hasOwnProperty('color') && abstrct.sections[sectionItem].color !== "" && abstrct.sections[sectionItem].color !== null) {
                section += serialiseColor(abstrct.sections[sectionItem].color);
            }
            section += serialiseKey(abstrct.sections[sectionItem], 'recommendedProductId', 'RecommendedProductId');
            section += serialiseKey(abstrct.sections[sectionItem], 'recommendedProductName', 'RecommendedProductName');
            section += "</Section>";
            sections += section;
        }
        sections += "</Sections>";
        value += sections;
        value += serialiseAndEncodeKey(abstrct, 'name', 'Name');
        value += "</NirvanaImageDetails>";
        return value;
    };

    var parseColor = function(xmlColor) {
        var colour = {
            colorId: $('ColorId', xmlColor).text(),
            rgb: $('RGB', xmlColor).text(),
            name: $('Name', xmlColor).text(),
            colorCode: $('ColorCode', xmlColor).text(),
            colorCodeType: $('ColorCodeType', xmlColor).text()
        };
        return colour;
    };

    var serialiseColor = function(color) {
        var value = "<Color>";

        value += serialiseKey(color, "colorCode", "ColorCode");
        value += serialiseKey(color, "colorCodeType", "ColorCodeType");
        value += serialiseKey(color, "colorId", "ColorId");
        value += serialiseKey(color, "name", "Name");
        value += serialiseKey(color, "rgb", "RGB");

        value += "</Color>";
        return value;
    };

    var parseUserSavedItems = function(data) {
        return $(data).find('UserItem').map(function() { return parseSavedItem(this); }).get();
    };

    var parseSharedItems = function(data) {
        return $(data).find('SharedItem').map(function() {
            var item = parseSavedItem(this);
            item.parentId = $('ParentID', this).text();
            return item;
        }).get();
    };

    var parseSavedItem = function(xml) {
        var item;

        if ($(xml).children('Type').text().toLowerCase() === USER_ITEM_TYPES.Room.toLowerCase()) {
            item = parseNirvanaImage(xml);
        } else if ($(xml).children('Type').text().toLowerCase() === USER_ITEM_TYPES.Color.toLowerCase()) {
            item = parseColor($(xml).children('Value').children('Color'));
        } else if ($(xml).children('Type').text().toLowerCase() === USER_ITEM_TYPES.Abstract.toLowerCase()) {
            item = parseNirvanaImage(xml);
        } else if ($(xml).children('Type').text().toLowerCase() === USER_ITEM_TYPES.Product.toLowerCase()) {
            item = parseProduct(xml);
        } else {
            log("UserItem not matched");
        }
        item.id = $(xml).children('ID').text();
        item.type = $(xml).children('Type').text();

        return item;
    };

    var serialiseSavedItem = function(item) {
        var itemXml;
        if (item.type === USER_ITEM_TYPES.Room) {
            itemXml = serialiseNirvanaImage(item);
        } else if (item.type === USER_ITEM_TYPES.Color) {
            itemXml = serialiseColor(item);
        } else if (item.type === USER_ITEM_TYPES.Abstract) {
            itemXml = serialiseNirvanaImage(item);
        } else if (item.type === USER_ITEM_TYPES.Product) {
            itemXml = serialiseProduct(item);
        } else {
            log("Unknown UserItemType");
        }
        var xml = '<UserItem>';
        if (item.hasOwnProperty("id") && item.id != null && item.id != '') {
            xml += "<ID>" + item.id + "</ID>";
        }
        xml += "<Type>" + item.type + "</Type>";
        xml += "<Value>" + itemXml + "</Value>";
        xml += "</UserItem>";
        return xml;
    };

    var serialiseSharedItem = function(item) {
        var itemXml;
        if (item.type === USER_ITEM_TYPES.Room) {
            itemXml = serialiseNirvanaImage(item);
        } else if (item.type === USER_ITEM_TYPES.Color) {
            itemXml = serialiseColor(item);
        } else if (item.type === USER_ITEM_TYPES.Abstract) {
            itemXml = serialiseNirvanaImage(item);
        } else if (item.type === USER_ITEM_TYPES.Product) {
            itemXml = serialiseProduct(item);
        } else {
            log("Unknown UserItemType");
        }
        var xml = '<SharedItem>';
        if (item.hasOwnProperty("id") && item.id != null && item.id != '') {
            xml += "<ID>" + item.id + "</ID>";
        }
        xml += "<Type>" + item.type + "</Type>";
        xml += "<Value>" + itemXml + "</Value>";
        xml += "</SharedItem>";
        return xml;
    };

    var parseProfile = function(data) {

    };

    var copyArray = function(array) {
        return jQuery.map(array, function(item) {
            return jQuery.extend(true, {}, item);
        });

    };

    //This routine works as a barrier checking that all functions that need to be initialised have called this. When 
    //the last one calls this it will initalise the rest of the profile service object
    var ifFinishedInitialising = function(initBarrier) {
        leftToInitialise = jQuery.grep(leftToInitialise, function(barriered, counter) {
            return (initBarrier !== barriered);
        });

        if (leftToInitialise.length === 0) {
            initialised = true;
            while (callBackFunctions.length > 0) {
                var sub = callBackFunctions.shift();
                sub();
            }
        }
    };

    var leftToInitialise = ["getSavedItems", "getSharedItems"];

    return {
        //public methods

        // This method is required after constructing the profile service,
        // and can be used to reload the data
        loadItems: function() {
            getData(getSavedItemsURL(), function(data) {
                savedItems = parseUserSavedItems(data);
                ifFinishedInitialising("getSavedItems");
            });
            getData(getSharedItemsURL(), function(data) {
                sharedItems = parseSharedItems(data);
                ifFinishedInitialising("getSharedItems");
            });
        },

        addInitialisedCallBackFunctions: function(callBackFunction) {
            if (initialised && callBackFunctions.length === 0) {
                callBackFunction();
            } else {
                callBackFunctions.push(callBackFunction);
            }
        },

        addErrorHandlerFunctions: function(errorHandlerFunction) {
            ajaxErrorHandlers.push(errorHandlerFunction);
        },

        //type is optional flag which will return that type of JSON object
        getUserItems: function(type) {
            if (type == null || type == "") {
                return copyArray(savedItems);
            } else {
                return copyArray(jQuery.grep(savedItems, function(item) {
                    return item.type === type;
                }));
            }
        },

        deleteUserItems: function(userItemIds) {
            savedItems = jQuery.grep(savedItems, function(item) {
                return (jQuery.inArray(item.id, userItemIds) >= 0);
            }, true);

            var data = '<ArrayOfUserItem>';
            $(userItemIds).each(function(i) {
                data += '<UserItem><ID>' + userItemIds[i] + '</ID></UserItem>';
            });
            data += '</ArrayOfUserItem>';

            updateService(data, getDeleteSavedItemsURL(), defaultPostActionSuccess);
        },

        deleteAllUserItems: function() {
            getData(getDeleteAllUserItems(), defaultPostActionSuccess);

            savedItems = [];
        },

        //Any UserItems that contain a UserItem ID will be updated, any that do not include a UserItemId will be added.
        updateUserItems: function(userItems, complete) {
            var xml = "<ArrayOfUserItem>";
            $(userItems).each(function(counter, item) {
                xml += serialiseSavedItem(item);
            });
            xml += "</ArrayOfUserItem>";

            updateService(xml, getSavedItemsURL(), function(data) {
                var itemsToUpdate = [];
                if (isStub) {
                    $(userItems).each(function(counter, item) {
                        if (!(item.hasOwnProperty('id') && item.id !== "" && item.id !== null)) {
                            item.id = genGuid();
                        }
                    });
                    itemsToUpdate = userItems;

                } else {
                    itemsToUpdate = parseUserSavedItems(data);
                }

                $(itemsToUpdate).each(function(counter, item) {

                    var itemPos = jQuery.inArray(item.id, jQuery.map(savedItems, function(sItem) { return sItem.id; }));
                    if (itemPos >= 0) {
                        savedItems[itemPos] = item;
                    } else {
                        // force to put the most recent item on the top
                        savedItems.reverse();
                        savedItems.push(item);
                        savedItems.reverse();
                    }

                });

                complete(copyArray(itemsToUpdate));
            });

            return true;
        },

        addUserItems: function(newUserItems, complete) {
            //separate method for convenience. Logic is all handled within UpdateUserItems. Any JSON user Items without
            //ids will be added to the project. Any that include an UserItem Id will be updated.
            return this.updateUserItems(newUserItems, complete);
        },

        //type is optional flag which will return that type of JSON object
        getShareItems: function(type) {
            if (type == null || type == "") {
                return copyArray(sharedItems);
            } else {
                return copyArray(jQuery.grep(sharedItems, function(item) {
                    return item.type === type;
                }));
            }
        },

        shareUserItems: function(userItemIds, complete) {
            var xml = "<ArrayOfUserItem>";
            $(userItemIds).each(function(counter, userItemId) {
                xml += "<UserItem><ID>" + userItemId + "</ID></UserItem>";
            });
            xml += "</ArrayOfUserItem>";

            updateService(xml, addSharedItemsURL(), function(data) {
                var newItems = [];
                if (isStub) {
                    var itemsPos = jQuery.map(savedItems, function(item, pos) {
                        if (jQuery.inArray(item.id, userItemIds) >= 0) {
                            return pos;
                        }
                    });
                    $(itemsPos).each(function(counter, pos) {
                        var newItem = jQuery.extend(true, {}, savedItems[pos]);
                        newItem.parentId = newItem.id;
                        newItem.id = genGuid();
                        newItems.push(newItem);
                    });
                } else {
                    newItems = parseSharedItems(data);
                }

                $(newItems).each(function(itemCount, item) {
                    sharedItems.push(item);
                });

                complete(copyArray(newItems));
            });
        },

        saveShareItems: function(shareItems, complete) {
            var xml = '<ArrayOfSharedItem>';
            $(shareItems).each(function(counter, shareItem) {
                xml += serialiseSharedItem(shareItem);
            });
            xml += '</ArrayOfSharedItem>';

            updateService(xml, getSharedItemsURL(), function(data) {

                var newItems = [];
                if (isStub) {
                    $(shareItems).each(function(counter, shareItem) {
                        shareItem.id = genGuid();
                        newItems.push(shareItem);
                    });
                } else {
                    newItems = parseSharedItems(data);
                }

                $(newItems).each(function(itemCount, item) {
                    sharedItems.push(item);
                });

                complete(copyArray(newItems));

            });

            return true;
        },

        deleteShareItems: function(userShareItemIds) {
            sharedItems = jQuery.grep(sharedItems, function(shareItem) {
                return (jQuery.inArray(shareItem.id, userShareItemIds) >= 0);
            }, true);

            var data = '<ArrayOfSharedItem>';
            $(userShareItemIds).each(function(i) {
                data += '<SharedItem><ID>' + userShareItemIds[i] + '</ID></SharedItem>';
            });
            data += '</ArrayOfSharedItem>';

            updateService(data, getDeleteSharedItemsURL(), defaultPostActionSuccess);
        },

        deleteAllShareItems: function() {
            getData(getDeleteAllSharedItemsURL(), defaultPostActionSuccess);
            sharedItems = [];
        },

        isUserLoggedIn: function() {
            // In the AuthenticationLinksControl we write a a div which can be investigated
            isLoggedIn = ( $(".box-account .display-account #SignedIn").length > 0 );
            return isLoggedIn;
        },


        //Stub Methods to be implemented by ICC
        //TODO implement by ICC

        login: function(email, password) {
            stubIsLoggedIn = true;
            return profileId;
        },

        logout: function() {
            stubIsLoggedIn = false;
        },

        registerProfile: function(formDetails) {
            return true;
        },

        updateProfile: function(formDetails) {
            return true;
        },

        getUserDetails: function() {
            return { Name: "Jan", FirstName: "Jansen" };
        },

        getProfileFormEntries: function() {
            return { stuff: "values" };
        },

        changePassword: function(username, oldPassword, newPassword) {
            return true;
        }

    };
}
