/**
 * Easy to use Wizard library for AngularJS
 * @version v0.4.0 - 2014-04-25 * @link https://github.com/mgonto/angular-wizard
 * @author Martin Gontovnikas <martin@gon.to>
 * @license MIT License, http://www.opensource.org/licenses/MIT
 */
angular.module('templates-angularwizard', ['step.html', 'wizard.html']);

angular.module("step.html", []).run(["$templateCache", function($templateCache) {
    $templateCache.put("step.html",
            "<div ng-show=\"selected\" ng-class=\"{current: selected, done: completed}\" class=\"step\" ng-transclude>\n" +
            "</div>");
}]);


angular.module("wizard.html", []).run(["$templateCache", function($templateCache) {
    $templateCache.put("wizard.html",
            "<div class = \"wiz-container\">\n" +
            "    <div class = \"row \">\n" +
            "    <div class = \"col-md-12 \">\n" +
            // "    <div class = \"helper-set-block\">\n" +
            // "    <div class = \"arrow-wrapper steps-indicator\">\n" +
           // "    <div class=\"steps-indicator\" ng-if=\"!hideIndicators\">\n" +
           // "      <div ng-repeat=\"step in steps\" ng-class=\"{'active':step.title == selectedStep.title, default: !step.completed && !step.selected, 'current': step.selected && !step.completed, 'done': step.completed && !step.selected, 'editing': step.selected && step.completed}\">\n" +
                "<h3 ng-hide='getEnabledSteps().length > 1'>{{steps[0].title || steps[0].wzTitle}}</h3>"+
                " <ul class=\"progress-indicator\" ng-show='getEnabledSteps().length > 1'>"+
            "        <li ng-repeat=\"step in getEnabledSteps()\" ng-click=\"goTo(step, true)\" ng-class=\"{'progress-current':step.selected,'progress-todo': " +
                "!step.completed && !step.selected, " +
                "'progress-done': step.completed && !step.selected , 'progress-danger': step.danger && !step.selected}\">" +
                "<span class='bubble'></span><i class='fa fa-check' ng-show='step.completed && !step.selected'></i>"+
                "<i class='fa fa-repeat' ng-show='step.selected'></i><i class='fa fa-times' ng-show='step.danger && !step.selected'></i>{{step.title || step.wzTitle}}"+
            "        </li>" +
                "</ul>" +
           // "      </div>\n" +
            //"    </div>\n" +
            // "    </div>\n" +
            // "    </div>\n" +
            "    </div>\n" +
            "    </div>\n" +
            "    <div class = \"row \">\n" +
            "    <div class=\"steps col-md-12\" ng-transclude></div>\n" +
            "    </div>\n" +
            "</div>\n" +
            "");
}]);
angular.module('mgo-angular-wizard', ['templates-angularwizard']);

angular.module('mgo-angular-wizard').directive('wzStep', function() {
    return {
        restrict: 'EA',
        replace: true,
        transclude: true,
        scope: {
            wzTitle: '@',
            wzHeadingTitle: '@',
            canenter : '=',
            canexit : '=',
            disabled: '@?wzDisabled',
            description: '@',
            wzData: '=',
            wzOrder: '@?'
        },
        require: '^wizard',
        templateUrl: function(element, attributes) {
            return attributes.template || "step.html";
        },
        link: function ($scope, $element, $attrs, wizard) {
            $attrs.$observe('wzTitle', function (value) {
                $scope.title = $scope.wzTitle;
            });
            $scope.title = $scope.wzTitle;
            if($element.find('form')[0].attributes['ng-submit']) {
                $scope.formController = $element.find('form').controller("form");
            }
            wizard.addStep($scope);
            $scope.$on('$destroy', function(){
                wizard.removeStep($scope);
            });
        }
    };
});

//wizard directive
angular.module('mgo-angular-wizard').directive('wizard', function() {
    return {
        restrict: 'EA',
        replace: true,
        transclude: true,
        scope: {
            currentStep: '=',
            onCancel: '&',
            onFinish: '&',
            hideIndicators: '=',
            editMode: '=',
            name: '@',
            indicatorsPosition: '@?'
        },
        templateUrl: function(element, attributes) {
            return attributes.template || "wizard.html";
        },

        //controller for wizard directive, treat this just like an angular controller
        controller: ['$scope', '$element', '$log', 'WizardHandler', '$q', '$timeout', function ($scope, $element, $log, WizardHandler, $q, $timeout) {
            //setting default step position if none declared.
            if ($scope.indicatorsPosition == undefined) {
                $scope.indicatorsPosition = 'bottom';
            }
            //this variable allows directive to load without having to pass any step validation
            var firstRun = true;
            //creating instance of wizard, passing this as second argument allows access to functions attached to this via Service
            WizardHandler.addWizard($scope.name || WizardHandler.defaultName, this);

            $scope.$on('$destroy', function() {
                WizardHandler.removeWizard($scope.name || WizardHandler.defaultName);
            });

            //steps array where all the scopes of each step are added
            $scope.steps = [];

            var stepIdx = function(step) {
                var idx = 0;
                var res = -1;
                angular.forEach($scope.getEnabledSteps(), function(currStep) {
                    if (currStep === step) {
                        res = idx;
                    }
                    idx++;
                });
                return res;
            };

            var stepByTitle = function(titleToFind) {
                var foundStep = null;
                angular.forEach($scope.getEnabledSteps(), function(step) {
                    if (step.wzTitle === titleToFind) {
                        foundStep = step;
                    }
                });
                return foundStep;
            };


            //update completed state for each step based on the editMode and current step number
            var handleEditModeChange = function() {
                var editMode = $scope.editMode;
                if (angular.isUndefined(editMode) || (editMode === null)) return;

                //Set completed for all steps to the value of editMode
                angular.forEach($scope.steps, function (step) {
                    step.completed = editMode;
                });

                //If editMode is false, set ONLY ENABLED steps with index lower then completedIndex to completed
                if (!editMode) {
                    var completedStepsIndex = $scope.currentStepNumber() - 1;
                    angular.forEach($scope.getEnabledSteps(), function(step, stepIndex) {
                        if(stepIndex < completedStepsIndex) {
                            step.completed = true;
                        }
                    });
                }
            };

            //access to context object for step validation
            $scope.context = {};

            //watching changes to currentStep
            $scope.$watch('currentStep', function(step) {
                //checking to make sure currentStep is truthy value
                if (!step) return;
                //setting stepTitle equal to current step title or default title
                var stepTitle = $scope.selectedStep.wzTitle;
                if ($scope.selectedStep && stepTitle !== $scope.currentStep) {
                    //invoking goTo() with step title as argument
                    $scope.goTo(stepByTitle($scope.currentStep));
                }
            });

            //watching steps array length and editMode value, if edit module is undefined or null the nothing is done
            //if edit mode is truthy, then all steps are marked as completed
            $scope.$watch('[editMode, steps.length]', function() {
                handleEditModeChange();
            }, true);

            //called each time step directive is loaded
            this.addStep = function(step) {
                var wzOrder = (step.wzOrder >= 0 && !$scope.steps[step.wzOrder]) ? step.wzOrder : $scope.steps.length;
                //adding the scope of directive onto step array
                $scope.steps[wzOrder] = step;
                //if this step is the new first then goTo it
                if ($scope.getEnabledSteps()[0] === step) {
                    //goTo first step
                    $scope.goTo($scope.getEnabledSteps()[0]);
                }
            };

            //called each time step directive is destroyed
            this.removeStep = function (step) {
                var index = $scope.steps.indexOf(step);
                if (index > 0) {
                    $scope.steps.splice(index, 1);
                }
            };

            this.removeSteps = function (index, length) {
                $scope.steps.splice(index, length);
            };

            this.context = $scope.context;

            $scope.getStepNumber = function(step) {
                return stepIdx(step) + 1;
            };

            $scope.goTo = function(step, onClick) {
                //if this is the first time the wizard is loading it bi-passes step validation
                if(firstRun){
                    //deselect all steps so you can set fresh below
                    unselectAll();
                    $scope.selectedStep = step;
                    //making sure current step is not undefined
                    if (!angular.isUndefined($scope.currentStep)) {
                        $scope.currentStep = step.wzTitle;
                    }
                    //setting selected step to argument passed into goTo()
                    step.selected = true;
                    //emit event upwards with data on goTo() invoktion
                    $scope.$emit('wizard:stepChanged', {step: step, index: stepIdx(step)});
                    //setting variable to false so all other step changes must pass validation
                    firstRun = false;
                } else {
                    //createing variables to capture current state that goTo() was invoked from and allow booleans
                    var thisStep;
                    //getting data for step you are transitioning out of
                    if($scope.currentStepNumber() > 0){
                        thisStep = $scope.currentStepNumber() - 1;
                    } else if ($scope.currentStepNumber() === 0){
                        thisStep = 0;
                    }
                    var formController = $scope.getEnabledSteps()[thisStep].formController;
                    if(onClick && formController) {
                        WizardHandler.wizard().checkValid(formController, onClick);
                    }

                    //$log.log('steps[thisStep] Data: ', $scope.getEnabledSteps()[thisStep].canexit);
                    $q.all([canExitStep($scope.getEnabledSteps()[thisStep], step), canEnterStep(step)]).then(function(data) {
                        if(data[0] && data[1]){
                            //deselect all steps so you can set fresh below
                            unselectAll();

                            //$log.log('value for canExit argument: ', $scope.currentStep.canexit);
                            $scope.selectedStep = step;
                            //making sure current step is not undefined
                            if(!angular.isUndefined($scope.currentStep)){
                                $scope.currentStep = step.wzTitle;
                            }
                            //setting selected step to argument passed into goTo()
                            step.selected = true;
                            //emit event upwards with data on goTo() invoktion
                            $scope.$emit('wizard:stepChanged', {step: step, index: stepIdx(step)});
                            //$log.log('current step number: ', $scope.currentStepNumber());
                        } else {
                            $scope.$emit('wizard:stepChangeFailed', {step: step, index: _.indexOf($scope.getEnabledSteps(), step)});
                        }
                    });
                }
            };

            function canEnterStep(step) {
                var defer;
                var canEnter;
                //If no validation function is provided, allow the user to enter the step
                if(step.canenter === undefined){
                    return true;
                }
                //If canenter is a boolean value instead of a function, return the value
                if(typeof step.canenter === 'boolean'){
                    return step.canenter;
                }
                //If canenter is a string instead of a function, evaluate the function
                if(typeof step.canenter === 'string'){
                    var splitFunction = step.canenter.split('(');
                    canEnter = eval('$scope.$parent.' + splitFunction[0] + '($scope.context' + splitFunction[1])
                } else {
                    canEnter = step.canenter($scope.context);
                }
                //Check to see if the canenter function is a promise which needs to be returned
                if(angular.isFunction(canEnter.then)){
                    defer = $q.defer();
                    canEnter.then(function(response){
                        defer.resolve(response);
                    });
                    return defer.promise;
                } else {
                    return canEnter === true;
                }
            }

            function canExitStep(step, stepTo) {
                var defer;
                var canExit;
                //Exiting the step should be allowed if no validation function was provided or if the user is moving backwards
                if(typeof(step.canexit) === 'undefined' || $scope.getStepNumber(stepTo) < $scope.currentStepNumber()){
                    return true;
                }
                //If canexit is a boolean value instead of a function, return the value
                if(typeof step.canexit === 'boolean'){
                    return step.canexit;
                }
                //If canenter is a string instead of a function, evaluate the function
                if(typeof step.canexit === 'string'){
                    var splitFunction = step.canexit.split('(');
                    canExit = eval('$scope.$parent.' + splitFunction[0] + '($scope.context' + splitFunction[1])
                } else {
                    canExit = step.canexit($scope.context);
                }
                //Check to see if the canexit function is a promise which needs to be returned
                if(angular.isFunction(canExit.then)){
                    defer = $q.defer();
                    canExit.then(function(response){
                        defer.resolve(response);
                    });
                    return defer.promise;
                } else {
                    return canExit === true;
                }
            }

            $scope.currentStepNumber = function() {
                //retreive current step number
                return stepIdx($scope.selectedStep) + 1;
            };

            $scope.getEnabledSteps = function() {
                return $scope.steps.filter(function(step){
                    return step && step.disabled !== 'true';
                });
            };

            //unSelect All Steps
            function unselectAll() {
                //traverse steps array and set each "selected" property to false
                angular.forEach($scope.getEnabledSteps(), function (step) {
                    step.selected = false;
                });
                //set selectedStep variable to null
                $scope.selectedStep = null;
            }

            //ALL METHODS ATTACHED TO this ARE ACCESSIBLE VIA WizardHandler.wizard().methodName()

            this.currentStepTitle = function(){
                return $scope.selectedStep.wzTitle;
            };

            this.currentStepDescription = function(){
                return $scope.selectedStep.description;
            };

            this.currentStep = function(){
                return $scope.selectedStep;
            };

            this.totalStepCount = function() {
                return $scope.getEnabledSteps().length;
            };

            //Access to enabled steps from outside
            this.getEnabledSteps = function(){
                return $scope.getEnabledSteps();
            };

            //Access to current step number from outside
            this.currentStepNumber = function(){
                return $scope.currentStepNumber();
            };

            this.checkValid = function(form, onClick){
                if(form.$valid){
                    $scope.selectedStep.danger = false;
                    this.next(undefined, onClick);
                }
                else{
                    
                    this.next(function(){
                        return $scope.selectedStep.danger = true;
                    }, onClick);
                }
            }
            //method used for next button within step
            this.next = function(callback, onClick) {
                var enabledSteps = $scope.getEnabledSteps();
                //setting variable equal to step  you were on when next() was invoked
                var index = stepIdx($scope.selectedStep);
                //checking to see if callback is a function
                if(angular.isFunction(callback)){
                    if(callback()){
                        if (index === enabledSteps.length - 1) {
                            this.finish();
                        } else {
                            //invoking goTo() with step number next in line
                            if(!onClick) {
                                $scope.goTo(enabledSteps[index + 1]);
                            }
                        }
                    } else {
                        return;
                    }
                }
                if (!callback) {
                    //completed property set on scope which is used to add class/remove class from progress bar
                    console.log("No callback");
                    $scope.selectedStep.completed = true;
                }
                else{
                    $scope.selectedStep.completed = false;
                }

                console.log($scope.selectedStep.completed);
                //checking to see if this is the last step.  If it is next behaves the same as finish()
                if (index === enabledSteps.length - 1) {
                    this.finish();
                } else {
                    //invoking goTo() with step number next in line
                    if(!onClick) {
                        $scope.goTo(enabledSteps[index + 1]);
                    }
                }

            };

            //used to traverse to any step, step number placed as argument
            this.goTo = function(step) {
                //wrapped inside $timeout so newly enabled steps are included.
                $timeout(function() {
                    var enabledSteps = $scope.getEnabledSteps();
                    var stepTo;
                    //checking that step is a Number
                    if (angular.isNumber(step)) {
                        stepTo = enabledSteps[step];
                    } else {
                        //finding the step associated with the title entered as goTo argument
                        stepTo = stepByTitle(step);
                    }
                    //going to step
                    $scope.goTo(stepTo);
                });
            };

            //calls finish() which calls onFinish() which is declared on an attribute and linked to controller via wizard directive.
            this.finish = function() {
                if ($scope.onFinish) {
                    $scope.onFinish();
                }
            };

            this.previous = function() {
                //getting index of current step
                var index = stepIdx($scope.selectedStep);
                //ensuring you aren't trying to go back from the first step
                if (index === 0) {
                    throw new Error("Can't go back. It's already in step 0");
                } else {
                    //go back one step from current step
                    $scope.goTo($scope.getEnabledSteps()[index - 1]);
                }
            };

            //cancel is alias for previous.
            this.cancel = function() {
                if ($scope.onCancel) {
                    //onCancel is linked to controller via wizard directive:
                    $scope.onCancel();
                } else {
                    //getting index of current step
                    var index = stepIdx($scope.selectedStep);
                    //ensuring you aren't trying to go back from the first step
                    if (index === 0) {
                        throw new Error("Can't go back. It's already in step 0");
                    } else {
                        //go back one step from current step
                        $scope.goTo($scope.getEnabledSteps()[0]);
                    }
                }
            };

            //reset
            this.reset = function(){
                //traverse steps array and set each "completed" property to false
                angular.forEach($scope.getEnabledSteps(), function (step) {
                    step.completed = false;
                });
                //go to first step
                this.goTo(0);
            };

            //change edit mode
            this.setEditMode = function(mode) {
                $scope.editMode = mode;
                handleEditModeChange();
            };
        }]
    };
});

function wizardButtonDirective(action) {
    angular.module('mgo-angular-wizard')
        .directive(action, function() {
            return {
                restrict: 'A',
                replace: false,
                require: '^wizard',
                link: function($scope, $element, $attrs, wizard) {

                    $element.on("click", function(e) {
                        e.preventDefault();
                        $scope.$apply(function() {
                            $scope.$eval($attrs[action]);
                            wizard[action.replace("wz", "").toLowerCase()]();
                        });
                    });
                }
            };
        });
}

wizardButtonDirective('wzNext');
wizardButtonDirective('wzPrevious');
wizardButtonDirective('wzFinish');
wizardButtonDirective('wzCancel');
wizardButtonDirective('wzReset');

angular.module('mgo-angular-wizard').factory('WizardHandler', function() {
    var service = {};

    var wizards = {};

    service.defaultName = "defaultWizard";

    service.addWizard = function(name, wizard) {
        wizards[name] = wizard;
    };

    service.removeWizard = function(name) {
        delete wizards[name];
    };

    service.wizard = function(name) {
        var nameToUse = name;
        if (!name) {
            nameToUse = service.defaultName;
        }

        return wizards[nameToUse];
    };

    return service;
});