angular.module("core-ui", [])
  .factory('CoreDialog', [function() {
    var service = {};
    service['fatal'] = function(title, message) {
      bootbox.dialog({
        "title": title,
        "message": "<div class='alert-icon-container-container'><div class='alert-icon-container'><div class='alert-icon'></div></div></div>" + message,
        "buttons": {},
        "className": "co-dialog fatal-error",
        "closeButton": false
      });
    };

    return service;
  }])

  .directive('corLogBox', function() {
    var directiveDefinitionObject = {
      priority: 1,
      templateUrl: '/static/directives/cor-log-box.html',
      replace: true,
      transclude: true,
      restrict: 'C',
      scope: {
        'logs': '=logs'
      },
      controller: function($rootScope, $scope, $element, $timeout) {
        $scope.hasNewLogs = false;

        var scrollHandlerBound = false;
        var isAnimatedScrolling = false;
        var isScrollBottom = true;

        var scrollHandler = function() {
          if (isAnimatedScrolling) { return; }
          var element = $element.find("#co-log-viewer")[0];
          isScrollBottom = element.scrollHeight - element.scrollTop === element.clientHeight;
          if (isScrollBottom) {
            $scope.hasNewLogs = false;
          }
        };

        var animateComplete = function() {
          isAnimatedScrolling = false;
        };

        $scope.moveToBottom = function() {
          $scope.hasNewLogs = false;
          isAnimatedScrolling = true;
          isScrollBottom = true;

          $element.find("#co-log-viewer").animate(
            { scrollTop: $element.find("#co-log-content").height() }, "slow", null, animateComplete);
        };

        $scope.$watch('logs', function(value, oldValue) {
          if (!value) { return; }

          $timeout(function() {
            if (!scrollHandlerBound) {
              $element.find("#co-log-viewer").on('scroll', scrollHandler);
              scrollHandlerBound = true;
            }

            if (!isScrollBottom) {
              $scope.hasNewLogs = true;
              return;
            }

            $scope.moveToBottom();
          }, 500);
        });
      }
    };
    return directiveDefinitionObject;
  })

  .directive('corOptionsMenu', function() {
    var directiveDefinitionObject = {
      priority: 1,
      templateUrl: '/static/directives/cor-options-menu.html',
      replace: true,
      transclude: true,
      restrict: 'C',
      scope: {},
      controller: function($rootScope, $scope, $element) {
      }
    };
    return directiveDefinitionObject;
  })

  .directive('corOption', function() {
    var directiveDefinitionObject = {
      priority: 1,
      templateUrl: '/static/directives/cor-option.html',
      replace: true,
      transclude: true,
      restrict: 'C',
      scope: {
        'optionClick': '&optionClick'
      },
      controller: function($rootScope, $scope, $element) {
      }
    };
    return directiveDefinitionObject;
  })


  .directive('corTitle', function() {
    var directiveDefinitionObject = {
      priority: 1,
      templateUrl: '/static/directives/cor-title.html',
      replace: true,
      transclude: true,
      restrict: 'C',
      scope: {},
      controller: function($rootScope, $scope, $element) {
      }
    };
    return directiveDefinitionObject;
  })

  .directive('corTitleAction', function() {
    var directiveDefinitionObject = {
      priority: 1,
      templateUrl: '/static/directives/cor-title-action.html',
      replace: true,
      transclude: true,
      restrict: 'C',
      scope: {},
      controller: function($rootScope, $scope, $element) {
      }
    };
    return directiveDefinitionObject;
  })

  .directive('corTitleContent', function() {
    var directiveDefinitionObject = {
      priority: 1,
      templateUrl: '/static/directives/cor-title-content.html',
      replace: true,
      transclude: true,
      restrict: 'C',
      scope: {},
      controller: function($rootScope, $scope, $element) {
      }
    };
    return directiveDefinitionObject;
  })

  .directive('corTitleLink', function() {
    var directiveDefinitionObject = {
      priority: 1,
      templateUrl: '/static/directives/cor-title-link.html',
      replace: true,
      transclude: true,
      restrict: 'C',
      scope: {},
      controller: function($rootScope, $scope, $element) {
      }
    };
    return directiveDefinitionObject;
  })

  .directive('corConfirmDialog', function() {
    var directiveDefinitionObject = {
      priority: 1,
      templateUrl: '/static/directives/cor-confirm-dialog.html',
      replace: false,
      transclude: true,
      restrict: 'C',
      scope: {
        'dialogTitle': '@dialogTitle',
        'dialogActionTitle': '@dialogActionTitle',

        'dialogContext': '=dialogContext',
        'dialogAction': '&dialogAction'
      },
      controller: function($rootScope, $scope, $element) {
        $scope.working = false;

        $scope.$watch('dialogContext', function(dc) {
          if (!dc) { return; }
          $scope.show();
        });

        $scope.performAction = function() {
          $scope.working = true;
          $scope.dialogAction({
            'info': $scope.dialogContext,
            'callback': function(result) {
              $scope.hide();
            }
          });
        };

        $scope.show = function() {
          $scope.working = false;
          $element.find('.modal').modal({});
        };

        $scope.hide = function() {
          $element.find('.modal').modal('hide');
        };
      }
    };
    return directiveDefinitionObject;
  })

  .directive('corTabPanel', function() {
    var directiveDefinitionObject = {
      priority: 1,
      templateUrl: '/static/directives/cor-tab-panel.html',
      replace: true,
      transclude: true,
      restrict: 'C',
      scope: {},
      controller: function($rootScope, $scope, $element) {
      }
    };
    return directiveDefinitionObject;
  })

  .directive('corTabContent', function() {
    var directiveDefinitionObject = {
      priority: 2,
      templateUrl: '/static/directives/cor-tab-content.html',
      replace: true,
      transclude: true,
      restrict: 'C',
      scope: {},
      controller: function($rootScope, $scope, $element) {
      }
    };
    return directiveDefinitionObject;
  })

 .directive('corTabs', function() {
    var directiveDefinitionObject = {
      priority: 3,
      templateUrl: '/static/directives/cor-tabs.html',
      replace: true,
      transclude: true,
      restrict: 'C',
      scope: {},
      controller: function($rootScope, $scope, $element, $timeout, $location, UIService) {
        $scope.isClosed = true;

        $scope.toggleClosed = function(e) {
          $scope.isClosed = !$scope.isClosed;
          e.stopPropagation();
          e.preventDefault();
        };

        UIService.initializeTabs($scope, $element, function() {
          $scope.isClosed = true;
        });
      }
    };
    return directiveDefinitionObject;
  })

 .directive('corFloatingBottomBar', function() {
    var directiveDefinitionObject = {
      priority: 3,
      templateUrl: '/static/directives/cor-floating-bottom-bar.html',
      replace: true,
      transclude: true,
      restrict: 'C',
      scope: {},
      controller: function($rootScope, $scope, $element, $timeout, $interval) {
        var handler = function() {
          $element.removeClass('floating');
          $element.css('width', $element[0].parentNode.clientWidth + 'px');

          var windowHeight = $(window).height();
          var rect = $element[0].getBoundingClientRect();
          if (rect.bottom > windowHeight) {
            $element.addClass('floating');
          }
        };

        $(window).on("scroll", handler);
        $(window).on("resize", handler);

        var previousHeight = $element[0].parentNode.clientHeight;
        var stop = $interval(function() {
          var currentHeight = $element[0].parentNode.clientWidth;
          if (previousHeight != currentHeight) {
            currentHeight = previousHeight;
            handler();
          }
        }, 100);

        $scope.$on('$destroy', function() {
          $(window).off("resize", handler);
          $(window).off("scroll", handler);
          $interval.cancel(stop);
        });
      }
    };
    return directiveDefinitionObject;

 })

  .directive('corLoaderInline', function() {
      var directiveDefinitionObject = {
        templateUrl: '/static/directives/cor-loader-inline.html',
        replace: true,
        restrict: 'C',
        scope: {
        },
        controller: function($rootScope, $scope, $element) {
        }
      };
      return directiveDefinitionObject;
  })

  .directive('corLoader', function() {
      var directiveDefinitionObject = {
        templateUrl: '/static/directives/cor-loader.html',
        replace: true,
        restrict: 'C',
        scope: {
        },
        controller: function($rootScope, $scope, $element) {
        }
      };
      return directiveDefinitionObject;
  })

 .directive('corTab', function() {
    var directiveDefinitionObject = {
      priority: 4,
      templateUrl: '/static/directives/cor-tab.html',
      replace: true,
      transclude: true,
      restrict: 'C',
      scope: {
        'tabActive': '@tabActive',
        'tabTitle': '@tabTitle',
        'tabTarget': '@tabTarget',
        'tabInit': '&tabInit',
        'tabShown': '&tabShown',
        'tabHidden': '&tabHidden'
      },
      controller: function($rootScope, $scope, $element) {
        $element.find('a[data-toggle="tab"]').on('hidden.bs.tab', function (e) {
          $scope.tabHidden({});
        });

        $element.find('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
          $scope.tabShown({});
        });
      }
    };
    return directiveDefinitionObject;
  })

 .directive('corStep', function() {
    var directiveDefinitionObject = {
      priority: 4,
      templateUrl: '/static/directives/cor-step.html',
      replace: true,
      transclude: false,
      requires: '^corStepBar',
      restrict: 'C',
      scope: {
        'icon': '@icon',
        'title': '@title',
        'text': '@text'
      },
      controller: function($rootScope, $scope, $element) {
      }
    };
    return directiveDefinitionObject;
  })

  .directive('realtimeAreaChart', function () {
    var directiveDefinitionObject = {
      priority: 0,
      templateUrl: '/static/directives/realtime-area-chart.html',
      replace: false,
      transclude: false,
      restrict: 'C',
      scope: {
        'data': '=data',
        'labels': '=labels',
        'colors': '=colors',
        'counter': '=counter'
      },
      controller: function($scope, $element) {
        var graph = null;
        var series = [];
        var palette = new Rickshaw.Color.Palette( { scheme: 'spectrum14' } );
        var colors = $scope.colors || [];

        var setupGraph = function() {
          for (var i = 0; i < $scope.labels.length; ++i) {
            series.push({
              name: $scope.labels[i],
              color: i >= colors.length ? palette.color(): $scope.colors[i],
              stroke: 'rgba(0,0,0,0.15)',
              data: []
            });
          }

          var options = {
            element: $element.find('.chart')[0],
            renderer: 'area',
            stroke: true,
            series: series,
            min: 0,
            padding: {
              'top': 0.3,
              'left': 0,
              'right': 0,
              'bottom': 0.3
            }
          };

          if ($scope.minimum != null) {
            options['min'] = $scope.minimum == 'auto' ? 'auto' : $scope.minimum * 1;
          } else {
            options['min'] = 0;
          }

          if ($scope.maximum != null) {
            options['max'] = $scope.maximum == 'auto' ? 'auto' : $scope.maximum * 1;
          }

          graph = new Rickshaw.Graph(options);

          xaxes = new Rickshaw.Graph.Axis.Time({
            graph: graph,
            timeFixture: new Rickshaw.Fixtures.Time.Local()
          });

          yaxes = new Rickshaw.Graph.Axis.Y({
            graph: graph,
            tickFormat: Rickshaw.Fixtures.Number.formatKMBT
          });

          hoverDetail = new Rickshaw.Graph.HoverDetail({
            graph: graph,
            xFormatter: function(x) {
              return new Date(x * 1000).toString();
            }
          });
        };

        var refresh = function(data) {
          if (!data || $scope.counter < 0) { return; }
          if (!graph) {
            setupGraph();
          }

          var timecode = new Date().getTime() / 1000;
          for (var i = 0; i < $scope.data.length; ++i) {
            var arr = series[i].data;
            arr.push(
              {'x': timecode, 'y': $scope.data[i] }
            );

            if (arr.length > 10) {
              series[i].data = arr.slice(arr.length - 10, arr.length);
            }
          }

          graph.renderer.unstack = true;
          graph.update();
        };

        $scope.$watch('counter', function() {
          refresh($scope.data_raw);
        });

        $scope.$watch('data', function(data) {
          $scope.data_raw = data;
          refresh($scope.data_raw);
        });
      }
    };
    return directiveDefinitionObject;
  })


  .directive('realtimeLineChart', function () {
    var directiveDefinitionObject = {
      priority: 0,
      templateUrl: '/static/directives/realtime-line-chart.html',
      replace: false,
      transclude: false,
      restrict: 'C',
      scope: {
        'data': '=data',
        'labels': '=labels',
        'counter': '=counter',
        'labelTemplate': '@labelTemplate',
        'minimum': '@minimum',
        'maximum': '@maximum'
      },
      controller: function($scope, $element) {
        var graph = null;
        var xaxes = null;
        var yaxes = null;
        var hoverDetail = null;
        var series = [];
        var counter = 0;
        var palette = new Rickshaw.Color.Palette( { scheme: 'spectrum14' } );

        var setupGraph = function() {
          var options = {
            element: $element.find('.chart')[0],
            renderer: 'line',
            series: series,
            padding: {
              'top': 0.3,
              'left': 0,
              'right': 0,
              'bottom': 0.3
            }
          };

          if ($scope.minimum != null) {
            options['min'] = $scope.minimum == 'auto' ? 'auto' : $scope.minimum * 1;
          } else {
            options['min'] = 0;
          }

          if ($scope.maximum != null) {
            options['max'] = $scope.maximum == 'auto' ? 'auto' : $scope.maximum * 1;
          }

          graph = new Rickshaw.Graph(options);
          xaxes = new Rickshaw.Graph.Axis.Time({
            graph: graph,
            timeFixture: new Rickshaw.Fixtures.Time.Local()
          });

          yaxes = new Rickshaw.Graph.Axis.Y({
            graph: graph,
            tickFormat: Rickshaw.Fixtures.Number.formatKMBT
          });

          hoverDetail = new Rickshaw.Graph.HoverDetail({
            graph: graph,
            xFormatter: function(x) {
              return new Date(x * 1000).toString();
            }
          });
        };

        var refresh = function(data) {
          if (data == null) { return; }
          if (!graph) {
            setupGraph();
          }

          if (typeof data == 'number') {
            data = [data];
          }

          if ($scope.labels) {
            data = data.slice(0, $scope.labels.length);
          }

          if (series.length == 0){
            for (var i = 0; i < data.length; ++i) {
              var title = $scope.labels ? $scope.labels[i] : $scope.labelTemplate.replace('{x}', i + 1);
              series.push({
                'color': palette.color(),
                'data': [],
                'name': title
              })
            }
          }

          counter++;
          var timecode = new Date().getTime() / 1000;

          for (var i = 0; i < data.length; ++i) {
            var arr = series[i].data;
            arr.push({
              'x': timecode,
              'y': data[i]
            })

            if (arr.length > 10) {
              series[i].data = arr.slice(arr.length - 10, arr.length);
            }
          }

          graph.update();
        };

        $scope.$watch('counter', function(counter) {
          refresh($scope.data_raw);
        });

        $scope.$watch('data', function(data) {
          $scope.data_raw = data;
        });
      }
    };
    return directiveDefinitionObject;
  })

 .directive('corStepBar', function() {
    var directiveDefinitionObject = {
      priority: 4,
      templateUrl: '/static/directives/cor-step-bar.html',
      replace: true,
      transclude: true,
      restrict: 'C',
      scope: {
        'progress': '=progress'
      },
      controller: function($rootScope, $scope, $element) {
        $scope.$watch('progress', function(progress) {
          if (!progress) { return; }

          var index = 0;
          for (var i = 0; i < progress.length; ++i) {
            if (progress[i]) {
              index = i;
            }
          }

          $element.find('.transclude').children('.co-step-element').each(function(i, elem) {
            $(elem).removeClass('active');
            if (i <= index) {
              $(elem).addClass('active');
            }
          });
        });
      }
    };
    return directiveDefinitionObject;
  })

 .directive('corCheckableMenu', function() {
    var directiveDefinitionObject = {
      priority: 1,
      templateUrl: '/static/directives/cor-checkable-menu.html',
      replace: false,
      transclude: true,
      restrict: 'C',
      scope: {
        'controller': '=controller'
      },
      controller: function($rootScope, $scope, $element) {
        $scope.getClass = function(items, checked) {
          if (checked.length == 0) {
            return 'none';
          }

          if (checked.length == items.length) {
            return 'all';
          }

          return 'some';
        };

        $scope.toggleItems = function($event) {
          $event.stopPropagation();
          $scope.controller.toggleItems();
        };

        this.checkByFilter = function(filter) {
          $scope.controller.checkByFilter(function(tag) {
            return filter({'tag': tag});
          });
        };
      }
    };
    return directiveDefinitionObject;
  })

 .directive('corCheckableMenuItem', function() {
    var directiveDefinitionObject = {
      priority: 1,
      templateUrl: '/static/directives/cor-checkable-menu-item.html',
      replace: true,
      transclude: true,
      restrict: 'C',
      require: '^corCheckableMenu',
      scope: {
        'itemFilter': '&itemFilter'
      },
      link: function($scope, $element, $attr, parent) {
        $scope.parent = parent;
      },

      controller: function($rootScope, $scope, $element) {
        $scope.selected = function() {
          $scope.parent.checkByFilter(this.itemFilter);
        };
      }
    };
    return directiveDefinitionObject;
  })

 .directive('corCheckableItem', function() {
    var directiveDefinitionObject = {
      priority: 1,
      templateUrl: '/static/directives/cor-checkable-item.html',
      replace: false,
      transclude: false,
      restrict: 'C',
      scope: {
        'item': '=item',
        'controller': '=controller'
      },
      controller: function($rootScope, $scope, $element) {
        $scope.toggleItem = function() {
          $scope.controller.toggleItem($scope.item);
        };
      }
    };
    return directiveDefinitionObject;
  })

 .directive('corTable', function() {
  var directiveDefinitionObject = {
    priority: 1,
    replace: false,
    transclude: false,
    restrict: 'C',
    scope: {},
    compile: function(tElement, tAttrs, transclude) {
      if (!window.matchMedia('(max-width: 767px)').matches) {
        return;
      }

      var cloneWithAttr = function(e, kind, opt_fullclone) {
        var clone = $(document.createElement(kind));
        var attributes = $(e).prop("attributes");
        $.each(attributes, function() {
          clone.attr(this.name, this.value);
        });

        if (opt_fullclone) {
          for (var i = 0; i < e.childNodes.length; ++i) {
            clone.append($(e.childNodes[i]).clone(true));
          }
        }

        return clone;
      };

      var appendRepeater = function(div, tr, headers, includeRepeat) {
        // Find all the tds directly under the tr and convert into a header + value span.
        tr.children('td').each(function(idx, td) {
          var displayer = cloneWithAttr(tr, 'div');

          if (!includeRepeat) {
            displayer.removeAttr('ng-repeat');
          }

          if (idx < headers.length) {
            displayer.append(headers[idx].clone(true).addClass('mobile-col-header'));
          }

          displayer.append(cloneWithAttr(td, 'div', true).addClass('mobile-col-value'));
          div.append(displayer);
        });
      };

      // Find the thead's tds and turn them into header elements.
      var headers = [];
      tElement.find('thead td').each(function(idx, td) {
        headers.push(cloneWithAttr(td, 'div', true));
      });

      // Find the element with the 'ng-repeat'.
      var repeater = tElement.find('[ng-repeat]')[0];

      // Convert the repeater into a <div> repeater.
      var divRepeater = cloneWithAttr(repeater, 'div').addClass('mobile-row');

      // If the repeater is a tbody, then we append each child tr. Otherwise, the repeater
      // itself should be a tr.
      if (repeater.nodeName.toLowerCase() == 'tbody') {
        $(repeater).children().each(function(idx, tr) {
          appendRepeater(divRepeater, $(tr), headers, true);
        });
      } else {
        appendRepeater(divRepeater, $(repeater), headers, false);
      }

      var repeaterBody = $(document.createElement('tbody'));
      var repeaterTr = $(document.createElement('tr'))
      var repeaterTd = $(document.createElement('td'))

      repeaterTd.append(divRepeater);
      repeaterTr.append(repeaterTd);
      repeaterBody.append(repeaterTr);

      // Remove the tbody and thead.
      tElement.find('thead').remove();
      tElement.find('tbody').remove();
      tElement.append(repeaterBody);
    },

    controller: function($rootScope, $scope, $element) {
      $element.addClass('co-table');
    }
  };
  return directiveDefinitionObject;
});
