/**
 * Checklist-model
 * AngularJS directive for list of checkboxes
 * https://github.com/vitalets/checklist-model
 * License: MIT http://opensource.org/licenses/MIT
 */

angular.module('checklist-model', [])
.directive('checklistModel', ['$parse', '$compile', function($parse, $compile) {
  // contains
  function contains(arr, item, comparator) {
    if (angular.isArray(arr)) {
      for (var i = arr.length; i--;) {
        if (comparator(arr[i], item)) {
          return true;
        }
      }
    }
    return false;
  }

  function containsSingleValue(newArr, value, comparator)
  {
	  if(!newArr || !value)
		  return false;
	  
	  if(!value.value || value.value.length == 0)
		  return false;
	  
	  var values = [];
      values = newArr.split('|');
     
      for (var i = 0; i < values.length; ++i)
      {
    	 if(value.value)
    		 if(comparator(values[i],value.value))
    			return true;
      }
      
      return false;
  }
  
  function compactValues(arrayToCompact)
  {
  	var sValue = "";
  	
  	for (var sItem in arrayToCompact)
  	{
  		var item = arrayToCompact[sItem];
  		
  		if (item == null)
  			continue;
  			
  		if (typeof item === "object")
  		{
  			// The field that will be compacted is "value"
  			if(item.value)
  				//sValue = (sValue === "") ? sValue.concat(item.value) : sValue.concat("",item.value);
  				sValue = (sValue === "") ? sValue.concat(item.value) : sValue.concat("|",item.value);
  		}
  	}
  	return sValue;
  }
  
  // add
  function add(arr, item, comparator, returnArray) {
    arr = angular.isArray(arr) ? arr : [];
      if(!contains(arr, item, comparator)) {
          arr.push(item);
      }
    
    if(returnArray)
    	return arr;
    else
    {
    	// return in internal format
    	var sValue = compactValues(arr);
    	return sValue;
    }
    	
  }  

  // remove
  function remove(arr, item, comparator, returnArray) {
    if (angular.isArray(arr)) {
      for (var i = arr.length; i--;) {
        if (comparator(arr[i], item)) {
          arr.splice(i, 1);
          break;
        }
      }
    }
    
    if(returnArray)
    	return arr;
    else
    {
    	// return in internal format
    	var sValue = compactValues(arr);
    	return sValue;
    }
  }
  
  //add 2
  function add2(currentValue, valueToAdd) {
      var sValue = "";
      
      if(currentValue == null)
		  return sValue;
	  if(valueToAdd == null)
		  return currentValue;
	  
      var index = currentValue.lastIndexOf(valueToAdd);
      if(index > -1) 
    	  return currentValue;
      
      sValue = currentValue+valueToAdd;
      
      return sValue;
  }  

  // remove 2
  function remove2(currentValue, valueToRemove) {
	  var sValue = "";
	  
	  if(currentValue == null)
		  return sValue;
	  if(valueToRemove == null)
		  return currentValue;
		  
      var index = currentValue.lastIndexOf(valueToRemove);
      if(index == -1) 
    	  return currentValue;
      
      sValue = currentValue.substring(0,index)+currentValue.substring(index+valueToRemove.length);
      
      return sValue;
  }

  function containsSingleValue2(currentValue, valueToCheck)
  {
	  if(currentValue == null || valueToCheck == null)
		  return false;
	  
	  //console.log("Current value is:"+currentValue);
	  var index = currentValue.lastIndexOf(valueToCheck);
      if(index > -1) 
    	  return true;
      
      return false;
  }
  
  //IE does not have support for endsWith ... so just add it if is missing
  if (!String.prototype.endsWith) 
  {
  	  String.prototype.endsWith = function(searchString, position) {
  	      var subjectString = this.toString();
  	      if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {
  	        position = subjectString.length;
  	      }
  	      position -= searchString.length;
  	      var lastIndex = subjectString.indexOf(searchString, position);
  	      return lastIndex !== -1 && lastIndex === position;
  	  };
  }
  
  //add 3
  function add3(currentValue, valueToAdd) {
      var sValue = "";
      
      if(currentValue == null)
		  return sValue;
	  if(valueToAdd == null)
		  return currentValue;
	  
	  if(containsSingleValue3(currentValue, valueToAdd))
    	  return currentValue;
      
      sValue = currentValue+valueToAdd;
      
      return sValue;
  } 
  
  //remove 3
  function remove3(currentValue, valueToRemove) {
	  var sValue = "";
	  
	  if(currentValue == null)
		  return sValue;
	  if(valueToRemove == null)
		  return currentValue;
		  
	  if(!containsSingleValue3(currentValue, valueToRemove))
    	  return currentValue;
      
	  var values = [];
	  values = currentValue.split('$');
	  var valueToCheck = valueToRemove.endsWith("$")?valueToRemove.substring(0,valueToRemove.length-1):valueToRemove;
	  for (var i = 0; i < values.length; i++)
      {
      	if(valueToCheck && values[i] != "" && values[i] != undefined)
      		if(values[i] != valueToCheck)
      			sValue += values[i]+"$";
      }
      
      return sValue;
  }
  
  function containsSingleValue3(newArr, value)
  {
	  if(!newArr)
		  return false;
	  var values = [];
	  values = newArr.split('$');
	  	
	  var valueToCheck = value.endsWith("$")?value.substring(0,value.length-1):value;
	  	
	  if(valueToCheck == undefined || valueToCheck == "")
		  return false;
	  
      for (var i = 0; i < values.length; i++)
      {
      	if(valueToCheck)
      		if(values[i] == valueToCheck)
      			return true;
      }
      
      return false;
  }
  
  // http://stackoverflow.com/a/19228302/1458162
  function postLinkFn(scope, elem, attrs) {
     // exclude recursion, but still keep the model
	
    var checklistModel = attrs.checklistModel;
    attrs.$set("checklistModel", null);
    // compile with `ng-model` pointing to `checked`
    $compile(elem)(scope);
    attrs.$set("checklistModel", checklistModel);

    // getter / setter for original model
    var getter = $parse(checklistModel);
    var setter = getter.assign;
    var checklistChange = $parse(attrs.checklistChange);
    
    // value added to list
    var value = attrs.checklistValue ? $parse(attrs.checklistValue)(scope.$parent) : attrs.value;
	
	// NEW: Restrict output to defined user field output-properties
	if ( typeof attrs.outputProperties !== 'undefined' )
	{
		var    outputProps     = [];
		outputProps = attrs.outputProperties.split(' ');                
        
		var tempObj         = {};	
	
		angular.forEach( value, function( val, key ) 
		{                                
			if ( outputProps.indexOf( key ) > -1 )                                                                          
				tempObj[ key ] = val;                                    
                
        });
		value = tempObj;
	}

    var comparator = angular.equals;

    if (attrs.hasOwnProperty('checklistComparator')){
      if (attrs.checklistComparator[0] == '.') {
        var comparatorExpression = attrs.checklistComparator.substring(1);
        comparator = function (a, b) {
          return a[comparatorExpression] === b[comparatorExpression];
        }
        
      } else {
        comparator = $parse(attrs.checklistComparator)(scope.$parent);
      }
    }

    // watch UI checked change
    scope.$watch(attrs.ngModel, function(newValue, oldValue) {
      if (newValue === oldValue) { 
        return;
      } 
      
      var values = [];
      
      /* Old method with split delimitator
      if(getter(scope.$parent) && value && value.value)
    	  //values = getter(scope.$parent).split('|');
      */
  
      var current = "";
      current = add3(current, getter(scope.$parent));
          
      /* Old method with split delimitator
      var current = [];
      for (var i = 0; i < values.length; ++i)
      {
    	 if(values[i])
    	 {
	         var obj = { value: values[i] };
	         current = add(current, obj, comparator, true);
    	 }
      }
      */
              
      if (angular.isFunction(setter)) {
        if (newValue === true) {
          setter(scope.$parent, add3(current, value.value));
          values = getter(scope.$parent);
         } else {
          setter(scope.$parent, remove3(current, value.value));
        }
      }

      if (checklistChange) {
        checklistChange(scope);
      }
    });
    
    // declare one function to be used for both $watch functions
    function setChecked(newArr, oldArr) {
    	//console.log(newArr);
    	//console.log(oldArr);
        scope[attrs.ngModel] = containsSingleValue3(newArr, value.value);
    }

    // watch original model change
    // use the faster $watchCollection method if it's available
    if (angular.isFunction(scope.$parent.$watchCollection)) {
        scope.$parent.$watchCollection(checklistModel, setChecked);
    } else {
        scope.$parent.$watch(checklistModel, setChecked, true);
    }
  }

  return {
    restrict: 'A',
    priority: 1000,
    terminal: true,
    scope: true,
    compile: function(tElement, tAttrs) {
      if ((tElement[0].tagName !== 'INPUT' || tAttrs.type !== 'checkbox')
          && (tElement[0].tagName !== 'MD-CHECKBOX')
          && (!tAttrs.btnCheckbox)) {
        //throw 'checklist-model should be applied to `input[type="checkbox"]` or `md-checkbox`.';
      }

      if (!tAttrs.checklistValue && !tAttrs.value) {
        throw 'You should provide `value` or `checklist-value`.';
      }

      // by default ngModel is 'checked', so we set it if not specified
      if (!tAttrs.ngModel) {
        // local scope var storing individual checkbox model
        tAttrs.$set("ngModel", "checked");
      }

      return postLinkFn;
    }
  };
}]);