﻿var Cabots = Cabots || {};

Cabots.MyProjects = (function ($) {
    // Constants
    var constants = {
        smallZoomFactor: 6,
        tinyZoomFactor: 8,
        localstorekey: 'cabots.projects',
        tabViewScrollAmount: 600,
        tabViewScrollAnimateDuration: 700
    };

    // Service endpoint url
    var serviceurls = {
        checkloginstatus: '/base/mem/status',
        loadprojects: '/base/mem/loadprojects',
        saveprojects: '/services/saveprojects.ashx'
    };

    // User log-in status
    var loggedin = false;

    // Models
    var models = {
        projects: [],
        stores: []
    };

    // Event listeners
    var listeners = {
        productDragStarted: null,
        productDragStopped: null,
        tabSwitch: null,
        modelReady: [],
        modelChanged: []
    };

    // Types
    var types = {
        store: function () {
            this.line = '';
            this.distance = '';
            this.lat = '';
            this.long = '';
            this.name = '';
            this.phone = '';
            this.postcode = '';
            this.state = '';
            this.suburb = '';
            this.type = 'Cabots.MyProject.types.store';
        }
    };

    // Views
    var views = {
        container: '#toolbar',
        toggler: '#toolbar header h2 a',
        content: '#toolbar .content',
        tabsContainer: '#toolbar .tabs',
        tabsScrollView: '#toolbar .scroll-view',
        tabsContainment: '#toolbar .tab-bar .inner',
        tabItems: '#toolbar .tabs li.project',
        tabs: '#toolbar .tabs a.tab-label',
        activetab: '#toolbar .tabs a.current',
        addProjectButton: '#toolbar a.add-project',
        projectDeleteButtons: '#toolbar .tabs li a.delete',
        productContainer: '#toolbar .projects',
        productGroup: '#toolbar .projects ul',
        productItems: '#toolbar .projects ul li',
        productDeleteButtons: '#toolbar .projects ul li a.delete',
        projectTmpl: '#projectTmpl',
        productsTmpl: '#productsTmpl',
        productDetailTmpl: '#productDetailTmpl',
        productDetailSmallTmpl: '#productDetailSmallTmpl',
        productDetailTinyTmpl: '#productDetailTinyTmpl',
        productDraggables: 'img.cut',
        colourOptionDraggables: '.colour-option',
        productDropTarget: '#toolbar .product-droptarget',
        spinner: '#toolbar .inner header .loader',
        scrollLeftButton: 'a.tab-arrow.back',
        scrollRightButton: 'a.tab-arrow.next'
    };

    // Controllers
    var controllers = {
        // Initialise the module
        init: function () {
            // Check user log-in status
            controllers.checkloginstatus();
        },
        // Initialise global handlers
        inithandlers: function () {
            // Bind event handlers
            $(views.toggler).click(controllers.toggle);
            $(views.addProjectButton).click(controllers.addprojectclick);
            $(views.scrollLeftButton).click(controllers.scrollleft);
            $(views.scrollRightButton).click(controllers.scrollright);

            // Make product images draggable
            $(views.productDraggables).draggable({
                revert: 'invalid',
                //helper: 'clone',
                helper: function (e, ui) {
                    var clone = $(e.currentTarget).clone().appendTo('body');
                    return $(clone);
                },
                cursor: 'move',
                tolerance: 'cursor',
                start: controllers.productdragstart,
                //drag: controllers.productdragging, // Support tab switching on drag over
                stop: controllers.productdragstop,
                zIndex: '30000'
            });

            $(views.productDropTarget).droppable({
                drop: controllers.productdropped,
                tolerance: 'pointer'
            });
        },
        // Bind draggable behaviours for colour options,
        // to be invoked by colour option lightbox initialisation script
        bindcolouroptions: function () {
            $(views.colourOptionDraggables).draggable({
                revert: 'invalid',
                helper: 'clone',
                cursor: 'move',
                start: controllers.colourdragstart,
                stop: controllers.colourdragstop,
                appendTo: 'body',
                zIndex: 5010
            });
        },
        // Toggle the my project content
        toggle: function (e) {
            e.preventDefault();
            if (controllers.isOpen()) {
                controllers.close();
            }
            else {
                controllers.open();
            }
        },
        // Open my project content
        open: function () {
            var container = $(views.container);
            var content = $(views.content);
            var height = 205; //content.height('auto').outerHeight();
            content.height(0);
            container.height('auto');
            content.animate({
                height: height
            }, function () {
                container.addClass('open')
            });
        },
        // Close my project content
        close: function () {
            var container = $(views.container);
            var content = $(views.content);
            content.animate({
                height: 0
            }, function () {
                container.removeClass('open').height(0);
            });
        },
        // Check whether my project content is open
        isOpen: function () {
            return $(views.container).hasClass('open');
        },
        // Check user log-in status
        checkloginstatus: function (callback) {
            $.ajax({
                url: serviceurls.checkloginstatus,
                dataType: 'json',
                success: function (result) {
                    loggedin = result.Data == 'true' ? true : false;
                    controllers.fetch();
                },
                error: function (error) {
                    alert('There was an error while trying to get user log-in status');
                    controllers.fetch();
                }
            });
        },
        // Fetch project data to models
        fetch: function () {
            controllers.spinstart();

            var fetchcomplete = function () {
                controllers.fixmodels();
                controllers.render();
                controllers.inithandlers();
                controllers.notifyModelReady();
                controllers.spinstop();
            };

            if (loggedin) {
                controllers.fetchfromserver(fetchcomplete);
            }
            else {
                controllers.fetchfromlocal(fetchcomplete);
            }
        },
        // Fetch models data from local (i.e. cookies)
        fetchfromlocal: function (fetchcomplete) {
            Cabots.Utils.log('fetch data from local');
            var data = $.cookie(constants.localstorekey);

            if (data) {
                Cabots.Utils.log('found local data');
                models = JSON.parse(data);
            }
            else {
                Cabots.Utils.log('data from local not found');
            }

            fetchcomplete();
        },
        // Fetch models data from server
        fetchfromserver: function (fetchcomplete) {
            Cabots.Utils.log('fetch data from server');
            $.ajax({
                url: serviceurls.loadprojects,
                dataType: 'json',
                success: function (result) {
                    if (result.Errors.length == 0) {
                        try {
                            var data = JSON.parse(result.Data);
                            models = data;
                            Cabots.Utils.log('data fetched from server successfully');
                        }
                        catch (e) { Cabots.Utils.log('error while parsing data from server'); }
                    }
                    else {
                        Cabots.Utils.log('error while trying to fetch data from server');
                    }
                    fetchcomplete();
                },
                error: function (error) {
                    Cabots.Utils.log('error while trying to fetch data from server');
                    fetchcomplete();
                }
            });
        },
        // Persist model to an appropriate persistent medium
        persist: function () {
            controllers.spinstart();

            controllers.notifyModelChanged();

            var persistcomplete = function () {
                controllers.spinstop();
            };

            if (loggedin) {
                controllers.persisttoserver(persistcomplete);
            }
            else {
                controllers.persisttolocal(persistcomplete);
            }
        },
        // Persist to local
        persisttolocal: function (complete) {
            Cabots.Utils.log('persist data to local');
            $.cookie(constants.localstorekey, JSON.stringify(models), { path: "/" });
            complete();
        },
        // Persist to server
        persisttoserver: function (complete) {
            Cabots.Utils.log('persisting data to server...');
            $.ajax({
                url: serviceurls.saveprojects,
                data: { settings: JSON.stringify(models) },
                type: 'POST',
                success: function (result) {
                    if (result.Errors.length == 0) {
                        Cabots.Utils.log('data persisted to server successfully');
                    }
                    else {
                        Cabots.Utils.log('failed to persist data to server');
                    }
                    complete();
                },
                error: function (error) {
                    Cabots.Utils.log('failed to persist data to server');
                    complete();
                }
            });
        },
        // Render content views
        render: function () {
            controllers.renderprojects();
            controllers.renderproducts();
        },
        // Render project tabs
        renderprojects: function () {
            // Render project tabs
            $(views.tabItems).remove();
            $(views.projectTmpl).tmpl(models).prependTo(views.tabsContainer);

            // Handle project tabs events
            $(views.tabs).unbind();
            $(views.tabs).click(controllers.tabclick);

            // Handle project delete button click
            $(views.projectDeleteButtons).click(controllers.deleteprojectclick);

            // Calculate and set actual tab container width for scrolling
            var containerWidth = 150;
            $(views.tabItems).each(function () {
                containerWidth += $(this).outerWidth(true);
            });
            $(views.tabsContainer).width(containerWidth);
        },
        // Render all products for each project tab
        renderproducts: function () {
            // Render products
            $(views.productGroup).remove();
            $(views.productsTmpl).tmpl(models, {
                draggableCount: $(views.productDraggables).length
            }).appendTo(views.productContainer);

            // Make products sortable
            //$(views.productGroup).sortable({
            //    containment: views.container
            //});

            // Delete product buttons handler
            $(views.productDeleteButtons).click(controllers.deleteproductclick);
        },
        // Handle delete product button click
        deleteproductclick: function (e) {
            var productid = $(this).parent().attr('productid');
            var projectid = controllers.selectedproject();
            var confirmed = confirm('Are you sure to remove this product?');
            if (confirmed) {
                controllers.deleteproduct(projectid, productid);
            }
            return false;
        },
        // Delete a product
        deleteproduct: function (projectid, productid) {
            var project = models.projects[projectid];
            var productindex = -1;

            for (var i in project.products) {
                var product = project.products[i];
                if (product.id == productid) {
                    productindex = i;
                    break;
                }
            }

            if (productindex >= 0) {
                project.products.splice(productindex, 1);
            }

            controllers.persist();
            controllers.renderproducts();
            controllers.tabswitch(projectid);
        },
        // Animate an existing product
        animateproduct: function (project, productid) {
            try {
                var productItem = views.productItems + '[productid=' + productid + ']';
                $(productItem).stop().animate({ borderColor: "#f00 !important" }, 250)
                    .animate({ borderColor: "#EEEEEE !important" }, 250)
                    .animate({ borderColor: "#f00 !important" }, 250)
                    .animate({ borderColor: "#EEEEEE !important" }, 250)
                    .animate({ borderColor: "#f00 !important" }, 250)
                    .animate({ borderColor: "#EEEEEE !important" }, 250, function () {
                        $(productItem).attr('style', '');
                    });
            }
            catch (e) {
                alert('Item has already been added');
            }
        },
        // Handle tab click event
        tabclick: function (e) {
            var index = $(this).attr('project');
            controllers.tabswitch(index);
            return false;
        },
        // Switch focus and content view to a tab
        tabswitch: function (index) {
            $(views.tabs).removeClass('current');
            $(views.tabs + '[project=' + index + ']').addClass('current');
            $(views.productGroup).hide();
            $(views.productGroup + '[project=' + index + ']').show();
            controllers.notifyTabSwitch(index);
        },
        // Handles project delete button click
        deleteprojectclick: function (e) {
            var projectid = $(this).attr('data-projectid');
            var projectname = $(this).attr('data-projectname');
            var confirmed = confirm('Are you sure to remove "' + projectname + '" project?');

            if (confirmed) {
                controllers.deleteProject(projectid);
            }

            return false;
        },
        // Delete project after confirmed
        deleteProject: function (projectid) {
            var projectindex = -1;
            for (var i in models.projects) {
                if (i == projectid) {
                    projectindex = i;
                    break;
                }
            }

            if (projectindex >= 0) {
                models.projects.splice(projectindex, 1);
            }

            controllers.persist();
            controllers.render();
        },
        // Draggable product started dragging
        productdragstart: function (e, ui) {
            controllers.verifyproject();

            if (!controllers.isOpen()) {
                controllers.open();
            }

            $(ui.helper).addClass('drag');
            $(views.productDropTarget).show();

            if (listeners.productDragStarted) {
                listeners.productDragStarted();
            }
        },
        // Draggable product is being dragged
        productdragging: function (e, ui) {
            var x = e.pageX;
            var y = e.pageY;

            $(views.tabs).each(function () {
                var left = $(this).offset().left;
                var top = $(this).offset().top;
                var width = $(this).width();
                var height = $(this).height();

                if (((x > left) && (x < (left + width)))
                    && ((y > top) && (y < (top + height)))) {
                    var index = $(this).attr('project');
                    Cabots.Utils.log('tabswitch(' + index + ')');
                    controllers.tabswitch(index);
                }
            });
        },
        // Draggable product stopped dragging
        productdragstop: function (e, ui) {
            $(views.productDropTarget).hide();

            // Send message to product drag stopped listener
            if (listeners.productDragStopped) {
                listeners.productDragStopped();
            }
        },
        // Draggable product dropped
        productdropped: function (e, ui) {
            var id = $(ui.draggable).attr('dnd-p-id');
            var name = $(ui.draggable).attr('dnd-p-name');
            var image = $(ui.draggable).attr('dnd-p-image');
            var projectid = controllers.selectedproject();

            var product = {
                id: id,
                name: name,
                imageurl: image,
                litres: '0',
                colours: [],
                calculator: {
                    width: '0',
                    length: '0',
                    coats: '0'
                }
            };

            if (!controllers.productexist(projectid, product.id)) {
                controllers.addproduct(projectid, product);
            }
            else {
                controllers.animateproduct(projectid, product.id);
            }
        },
        // Handles add project button click event
        addprojectclick: function (e) {
            controllers.addproject();
        },
        // Add new project
        addproject: function () {
            Cabots.Utils.log('add project');
            models.projects.push({
                name: 'New Project',
                products: []
            });
            controllers.persist();
            controllers.render();
        },
        // Verify if no project exist then create a default project
        verifyproject: function () {
            if (models.projects.length == 0) {
                models.projects.push({
                    name: 'My Project',
                    products: []
                });
                controllers.persist();
                controllers.render();
            }
        },
        // Add new product to a project
        addproduct: function (projectid, product) {
            models.projects[projectid].products.push(product);
            controllers.persist();
            controllers.renderproducts();
            controllers.tabswitch(projectid);
        },
        // Add a colour option to a product
        addcolour: function (projectid, productid, colour) {
            var product = controllers.findproduct(projectid, productid);
            product.colours.push(colour);
            controllers.persist();
            controllers.renderproducts();
            controllers.tabswitch(projectid);
        },
        // Check if a colour name existed in a product
        colourexist: function (projectid, productid, colourname) {
            var product = controllers.findproduct(projectid, productid);
            for (var i in product.colours) {
                var colour = product.colours[i];
                if (colour.name == colourname) {
                    return true;
                }
            }
            return false;
        },
        // A colour option started dragging
        colourdragstart: function (e, ui) {
            // Open my project bucket if closed
            if (!controllers.isOpen()) {
                controllers.open();
            }

            controllers.verifyproject();

            // Add .drag class to ui hepler element
            $(ui.helper).addClass('drag');

            // Add new product to current project if not added
            var projectid = controllers.selectedproject();
            var productid = $(ui.helper).attr('dnd-p-id');

            if (!controllers.productexist(projectid, productid)) {
                // Acquire product information from drag helper element
                var productname = $(ui.helper).attr('dnd-p-name');
                var productimage = $(ui.helper).attr('dnd-p-image');
                // Create product object
                var product = {
                    id: productid,
                    name: productname,
                    imageurl: productimage,
                    litres: '0',
                    colours: [],
                    calculator: {
                        width: '0',
                        length: '0',
                        coats: '0'
                    }
                };
                // Add product to model
                controllers.addproduct(projectid, product);
            }

            // Make products drop targets for colour options
            $(views.productItems).droppable('option', 'disabled', true);

            var activeProductItem = views.productItems + '[productid=' + productid + ']';
            $(activeProductItem).droppable({
                disabled: false,
                drop: controllers.colourdropped,
                tolerance: 'pointer'
            });
            $(activeProductItem).addClass('hover');
        },
        // A colour option stopped dragging
        colourdragstop: function (e, ui) {
            var productid = $(ui.helper).attr('dnd-p-id');
            var activeProductItem = views.productItems + '[productid=' + productid + ']';
            $(activeProductItem).removeClass('hover');
        },
        // A colour option dropped
        colourdropped: function (e, ui) {
            var projectid = controllers.selectedproject();
            var productid = $(ui.helper).attr('dnd-p-id');
            var colour = {
                name: $(ui.helper).attr('dnd-c-name'),
                image: $(ui.helper).attr('dnd-c-image')
            };
            if (!controllers.colourexist(projectid, productid, colour.name)) {
                controllers.addcolour(projectid, productid, colour);
            }
            else {
                controllers.animateproduct(projectid, productid);
            }
        },
        // Get selected project (index)
        selectedproject: function () {
            return $(views.activetab).attr('project');
        },
        // Check if a product is existed in a project
        productexist: function (projectid, productid) {
            var project = models.projects[projectid];
            for (var i in project.products) {
                var product = project.products[i];
                if (product.id == productid) {
                    return true;
                }
            }
            return false;
        },
        // Find a product by projectid and productid
        findproduct: function (projectid, productid) {
            var project = models.projects[projectid];
            for (var i in project.products) {
                var product = project.products[i];
                if (product.id == productid) {
                    return product;
                }
            }
            return 'undefined';
        },
        // Save product calculator result to a product
        savecalculation: function (product) {
            controllers.verifyproject();

            if (!controllers.isOpen()) {
                controllers.open();
            }

            var projectid = controllers.selectedproject();
            var existingProduct = controllers.findproduct(projectid, product.id);

            if (existingProduct != 'undefined') {
                existingProduct.litres = product.litres;
                existingProduct.calculator = product.calculator;
            }
            else {
                controllers.addproduct(projectid, product);
            }

            controllers.persist();
            controllers.renderproducts();
            controllers.tabswitch(projectid);
        },
        // Save store location
        savestore: function (store) {
            if (!controllers.storeexist(store.lat, store.long)) {
                models.stores.push(store);
                controllers.persist();
            }
        },
        // Delete store location
        deletestore: function (store) {
            var storeindex = -1;
            for (var i in models.stores) {
                var currentstore = models.stores[i];
                if (currentstore.lat == store.lat && currentstore.long == store.long) {
                    storeindex = i;
                    break;
                }
            }

            if (storeindex >= 0) {
                models.stores.splice(storeindex, 1);
            }

            controllers.persist();
            controllers.render();
        },
        // Check if a store exist with given longitude and latitude
        storeexist: function (lat, long) {
            for (var i in models.stores) {
                var store = models.stores[i];
                if (store.lat == lat && store.long == long) {
                    return true;
                }
            }
            return false;
        },
        // Callback to handle product start dragging event
        onProductDragStarted: function (listener) {
            listeners.productDragStarted = listener;
        },
        // Callback to handle product stop dragging event
        onProductDragStopped: function (listener) {
            listeners.productDragStopped = listener;
        },
        // Get my project model for debugging
        getmodel: function () {
            return models;
        },
        // Register a callback method for model change event
        // listener: function (models)
        onModelChanged: function (listener) {
            listeners.modelChanged.push(listener);
        },
        // Register a callback method for model ready event
        // listener: function (models)
        onModelReady: function (listener) {
            listeners.modelReady.push(listener);
        },
        // Notify listeners of model change event
        notifyModelChanged: function () {
            Cabots.Utils.log('model changed');
            controllers.notifyModelListeners(listeners.modelChanged);
        },
        // Notify listeners of model ready event
        notifyModelReady: function () {
            Cabots.Utils.log('model ready');
            controllers.notifyModelListeners(listeners.modelReady);
        },
        // Notify an array of listeners on model event
        notifyModelListeners: function (listeners) {
            for (var i in listeners) {
                listeners[i](models);
            }
        },
        // Start the spinner
        spinstart: function () {
            $(views.spinner).show();
        },
        // Stop the spinner
        spinstop: function () {
            $(views.spinner).hide();
        },
        // Save project name
        saveprojectname: function (projectId, projectName) {
            if (models.projects[projectId].name != projectName) {
                models.projects[projectId].name = projectName;
                controllers.persist();
                controllers.renderprojects();
            }
        },
        // Scroll the project tab view to the left
        scrollleft: function () {
            $(views.tabsScrollView).animate({
                scrollLeft: '-=' + constants.tabViewScrollAmount
            }, constants.tabViewScrollAnimateDuration);
            return false;
        },
        // Scroll the project tab view to the right
        scrollright: function () {
            $(views.tabsScrollView).animate({
                scrollLeft: '+=' + constants.tabViewScrollAmount
            }, constants.tabViewScrollAnimateDuration);
            return false;
        },
        // Fix my project models
        fixmodels: function () {
            for (var i in models.projects) {
                var project = models.projects[i];
                if (!project.lastupdated) {
                    project.lastupdated = 'N/A';
                }
                for (var j in project.products) {
                    var product = project.products[j];
                    if (!product.calculator) {
                        product.calculator = {
                            width: '0',
                            length: '0',
                            coats: '0'
                        };
                    }
                }
            }
        },
        // Remove a selected colour from a product
        deletecolour: function (projectid, productid, colourname) {
            var product = controllers.findproduct(projectid, productid);
            var colourindex = -1;

            for (var i in product.colours) {
                var colour = product.colours[i];
                if (colour.name == colourname) {
                    colourindex = i;
                    break;
                }
            }

            if (colourindex >= 0) {
                product.colours.splice(colourindex, 1);
            }

            controllers.persist();
            controllers.render();
        },
        onTabSwitch: function (listener) {
            listeners.tabSwitch = listener
        },
        notifyTabSwitch: function (index) {
            if (listeners.tabSwitch) {
                listeners.tabSwitch(index);
            }
        }
    };

    // Expose public interface
    return {
        init: controllers.init,
        types: types,
        bindColours: controllers.bindcolouroptions,
        saveCalculation: controllers.savecalculation,
        saveStore: controllers.savestore,
        onProductDragStarted: controllers.onProductDragStarted,
        onProductDragStopped: controllers.onProductDragStopped,
        storeExist: controllers.storeexist,
        deleteStore: controllers.deletestore,
        getmodel: controllers.getmodel,
        onModelChanged: controllers.onModelChanged,
        onModelReady: controllers.onModelReady,
        deleteProject: controllers.deleteProject,
        saveProjectName: controllers.saveprojectname,
        deleteProduct: controllers.deleteproduct,
        addProject: controllers.addproject,
        focusProject: controllers.tabswitch,
        deleteColour: controllers.deletecolour,
        onTabSwitch: controllers.onTabSwitch
    };
} (jQuery));
