(function () {
  'use strict';

  /*
  LemonLDAP::NG Viewer client

  This is the main app file. Other are:
   - struct.json and js/confTree.js that contains the full tree
   - translate.json that contains the keywords translation

  This file contains:
   - the AngularJS controller
  */
  var llapp;
  llapp = angular.module('llngViewer', ['ui.tree', 'ui.bootstrap', 'llApp', 'ngCookies']);

  /*
  Main AngularJS controller
  */
  llapp.controller('TreeCtrl', ['$scope', '$http', '$location', '$q', '$uibModal', '$translator', '$cookies', '$htmlParams', '$timeout', function ($scope, $http, $location, $q, $uibModal, $translator, $cookies, $htmlParams, $timeout) {
    var _download, _stoggle, c, id, pathEvent, readError, setHelp;
    $scope.links = window.links;
    $scope.menu = $htmlParams.menu;
    $scope.menulinks = window.menulinks;
    $scope.staticPrefix = window.staticPrefix;
    $scope.formPrefix = window.formPrefix;
    $scope.availableLanguages = window.availableLanguages;
    $scope.waiting = true;
    $scope.showM = false;
    $scope.showT = false;
    $scope.form = 'homeViewer';
    $scope.currentCfg = {};
    $scope.clipboardAvailable = Boolean(navigator.clipboard);
    $scope.viewPrefix = window.viewPrefix;
    $scope.allowDiff = window.allowDiff;
    $scope.message = {};
    $scope.result = '';
    // Import translations functions
    $scope.translateTitle = function (node) {
      return $translator.translateField(node, 'title');
    };
    $scope.translateP = $translator.translateP;
    $scope.translate = $translator.translate;
    // HELP DISPLAY
    $scope.helpUrl = 'start.html#configuration';
    $scope.setShowHelp = function (val) {
      var d;
      if (val == null) {
        val = !$scope.showH;
      }
      $scope.showH = val;
      d = new Date(Date.now());
      d.setFullYear(d.getFullYear() + 1);
      return $cookies.put('showhelp', val ? 'true' : 'false', {
        "expires": d
      });
    };
    $scope.showH = $cookies.get('showhelp') === 'false' ? false : true;
    if ($scope.showH == null) {
      $scope.setShowHelp(true);
    }
    // INTERCEPT AJAX ERRORS
    readError = function (response) {
      var e, j;
      e = response.status;
      j = response.statusLine;
      $scope.waiting = false;
      if (e === 403) {
        $scope.message = {
          title: 'forbidden',
          message: '',
          items: []
        };
      } else if (e === 401) {
        console.debug('Authentication needed');
        $scope.message = {
          title: 'authenticationNeeded',
          message: '__waitOrF5__',
          items: []
        };
      } else if (e === 400) {
        $scope.message = {
          title: 'badRequest',
          message: j,
          items: []
        };
      } else if (e > 0) {
        $scope.message = {
          title: 'badRequest',
          message: j,
          items: []
        };
      } else {
        $scope.message = {
          title: 'networkProblem',
          message: '',
          items: []
        };
      }
      return $scope.showModal('message.html');
    };
    // Modal launcher
    $scope.showModal = function (tpl, init) {
      var d, modalInstance;
      modalInstance = $uibModal.open({
        templateUrl: tpl,
        controller: 'ModalInstanceCtrl',
        size: 'lg',
        resolve: {
          elem: function () {
            return function (s) {
              return $scope[s];
            };
          },
          set: function () {
            return function (f, s) {
              return $scope[f] = s;
            };
          },
          init: function () {
            return init;
          }
        }
      });
      d = $q.defer();
      modalInstance.result.then(function (msgok) {
        $scope.message = {
          title: '',
          message: '',
          items: []
        };
        return d.resolve(msgok);
      }, function (msgnok) {
        $scope.message = {
          title: '',
          message: '',
          items: []
        };
        return d.reject(msgnok);
      });
      return d.promise;
    };
    // FORM DISPLAY FUNCTIONS

    // Function called when a menu item is selected. It launch function stored in
    // "action" or "title"
    $scope.menuClick = function (button) {
      if (button.popup) {
        window.open(button.popup);
      } else {
        if (!button.action) {
          button.action = button.title;
        }
        switch (typeof button.action) {
          case 'function':
            button.action($scope.currentNode, $scope);
            break;
          case 'string':
            $scope[button.action]();
            break;
          default:
            console.warn('Unknown action type', typeof button.action);
        }
      }
      return $scope.showM = false;
    };
    // Display main form
    $scope.home = function () {
      $scope.form = 'homeViewer';
      return $scope.showM = false;
    };
    // Download raw conf
    $scope.downloadConf = function () {
      return window.open($scope.viewPrefix + $scope.currentCfg.cfgNum + '?full=1');
    };
    // NODES MANAGEMENT
    id = 1;
    $scope._findContainer = function () {
      return $scope._findScopeContainer().$modelValue;
    };
    $scope._findScopeContainer = function () {
      var cs;
      cs = $scope.currentScope;
      while (!cs.$modelValue.type.match(/Container$/)) {
        cs = cs.$parentNodeScope;
      }
      return cs;
    };
    $scope._findScopeByKey = function (k) {
      var cs;
      cs = $scope.currentScope;
      while (!(cs.$modelValue.title === k)) {
        cs = cs.$parentNodeScope;
      }
      return cs;
    };
    $scope.down = function () {
      var i, ind, l, len, n, p, ref, tmp;
      id = $scope.currentNode.id;
      p = $scope.currentScope.$parentNodeScope.$modelValue;
      ind = p.nodes.length;
      ref = p.nodes;
      for (i = l = 0, len = ref.length; l < len; i = ++l) {
        n = ref[i];
        if (n.id === id) {
          ind = i;
        }
      }
      if (ind < p.nodes.length - 1) {
        tmp = p.nodes[ind];
        p.nodes[ind] = p.nodes[ind + 1];
        p.nodes[ind + 1] = tmp;
      }
      return ind;
    };
    $scope.up = function () {
      var i, ind, l, len, n, p, ref, tmp;
      id = $scope.currentNode.id;
      p = $scope.currentScope.$parentNodeScope.$modelValue;
      ind = -1;
      ref = p.nodes;
      for (i = l = 0, len = ref.length; l < len; i = ++l) {
        n = ref[i];
        if (n.id === id) {
          ind = i;
        }
      }
      if (ind > 0) {
        tmp = p.nodes[ind];
        p.nodes[ind] = p.nodes[ind - 1];
        p.nodes[ind - 1] = tmp;
      }
      return ind;
    };
    // test if value is in select
    $scope.inSelect = function (value) {
      var l, len, n, ref;
      ref = $scope.currentNode.select;
      for (l = 0, len = ref.length; l < len; l++) {
        n = ref[l];
        if (n.k === value) {
          return true;
        }
      }
      return false;
    };
    // This is for rule form: title = comment if defined, else title = re
    $scope.changeRuleTitle = function (node) {
      return node.title = node.comment.length > 0 ? node.comment : node.re;
    };
    // Node opening

    // authParams mechanism: show used auth modules only (launched by stoggle)
    $scope.filters = {};
    $scope.execFilters = function (scope) {
      var filter, func, ref;
      scope = scope ? scope : $scope;
      ref = $scope.filters;
      for (filter in ref) {
        func = ref[filter];
        if ($scope.filters.hasOwnProperty(filter)) {
          return window.filterFunctions[filter](scope, $q, func);
        }
      }
      return false;
    };
    // To avoid binding all the tree, nodes are pushed in DOM only when opened
    $scope.stoggle = function (scope) {
      var node;
      node = scope.$modelValue;
      _stoggle(node);
      return scope.toggle();
    };
    _stoggle = function (node) {
      var a, l, len, len1, len2, m, n, o, ref, ref1, ref2;
      ref = ['nodes', 'nodes_cond'];
      for (l = 0, len = ref.length; l < len; l++) {
        n = ref[l];
        if (node[`_${n}`]) {
          node[n] = [];
          ref1 = node[`_${n}`];
          for (m = 0, len1 = ref1.length; m < len1; m++) {
            a = ref1[m];
            node[n].push(a);
          }
          delete node[`_${n}`];
        }
      }
      // Call execFilter for authParams
      if (node._nodes_filter) {
        if (node.nodes) {
          ref2 = node.nodes;
          for (o = 0, len2 = ref2.length; o < len2; o++) {
            n = ref2[o];
            n.onChange = $scope.execFilters;
          }
        }
        $scope.filters[node._nodes_filter] = node;
        return $scope.execFilters();
      }
    };
    // Simple toggle management
    $scope.toggle = function (scope) {
      return scope.toggle();
    };
    // cnodes management: hash keys/values are loaded when parent node is opened
    $scope.download = function (scope) {
      var node;
      node = scope.$modelValue;
      return _download(node);
    };
    _download = function (node) {
      var d, uri;
      d = $q.defer();
      d.notify('Trying to get datas');
      $scope.waiting = true;
      console.debug(`Trying to get key ${node.cnodes}`);
      uri = encodeURI(node.cnodes);
      $http.get(`${window.viewPrefix}${$scope.currentCfg.cfgNum}/${uri}`).then(function (response) {
        var a, data, l, len;
        data = response.data;
        // Manage datas errors
        if (!data) {
          d.reject('Empty response from server');
        } else if (data.error) {
          if (data.error.match(/setDefault$/)) {
            if (node['default']) {
              node.nodes = node['default'].slice(0);
            } else {
              node.nodes = [];
            }
            delete node.cnodes;
            d.resolve('Set data to default value');
          } else {
            d.reject(`Server return an error: ${data.error}`);
          }
        } else {
          // Store datas
          delete node.cnodes;
          if (!node.type) {
            node.type = 'keyTextContainer';
          }
          node.nodes = [];
          // TODO: try/catch
          for (l = 0, len = data.length; l < len; l++) {
            a = data[l];
            if (a.template) {
              a._nodes = templates(a.template, a.title);
            }
            node.nodes.push(a);
          }
          d.resolve('OK');
        }
        return $scope.waiting = false;
      }, function (response) {
        readError(response);
        return d.reject('');
      });
      return d.promise;
    };
    $scope.openCnode = function (scope) {
      return $scope.download(scope).then(function () {
        return scope.toggle();
      });
    };
    $scope.copyPath = function () {
      var text = $scope.breadCrumb.join(" » ");
      navigator.clipboard.writeText(text).then(function () {
        $scope.$apply(function () {
          $scope.copySuccess = true;
          $timeout(function () {
            $scope.copySuccess = false;
          }, 400);
        });
      });
    };
    setHelp = function (scope) {
      while (!scope.$modelValue.help && scope.$parentNodeScope) {
        scope = scope.$parentNodeScope;
      }
      return $scope.helpUrl = scope.$modelValue.help || 'start.html#configuration';
    };
    // Form management

    // `currentNode` contains the last select node

    // method `diplayForm()`:
    //	- set the `form` property to the name of the form to download
    //		(`text` by default or `home` for node without `type` property)
    //	- launch getKeys to set `node.data`
    //	- hide tree when in XS size

    $scope.getTrPath = function (scope) {
      var path = [];
      var trpath = [];
      var current = scope;
      var safetycount = 0;
      while (current && current.$modelValue && safetycount < 100) {
        safetycount = safetycount + 1;
        if (current.$modelValue.title && current.$modelValue.title != path[0]) {
          trpath.unshift(scope.translate(current.$modelValue.title));
          path.unshift(current.$modelValue.title);
        }
        current = current.$parent;
      }
      return trpath;
    };
    $scope.displayForm = function (scope) {
      var f, l, len, n, node, ref;
      node = scope.$modelValue;
      if (node.cnodes) {
        $scope.download(scope);
      }
      if (node._nodes) {
        $scope.stoggle(scope);
      }
      $scope.currentNode = node;
      $scope.currentScope = scope;
      f = node.type ? node.type : 'text';
      if (node.nodes || node._nodes || node.cnodes) {
        $scope.form = f !== 'text' ? f : 'mini';
      } else {
        $scope.form = f;
        // Get datas
        $scope.getKey(node);
      }
      if (node.type && node.type === 'simpleInputContainer') {
        ref = node.nodes;
        for (l = 0, len = ref.length; l < len; l++) {
          n = ref[l];
          $scope.getKey(n);
        }
      }
      $scope.showT = false;
      $scope.breadCrumb = $scope.getTrPath(scope);
      return setHelp(scope);
    };
    $scope.keyTranslatable = function (scope) {
      var node;
      node = scope.$modelValue;
      // Regexp::Assemble->new->add(qw(
      //  authChoice
      //  casAppMetaDataNode
      //  casSrvMetaDataNode
      //  cmbModule
      //  keyNode
      //  keyText
      //  menuApp
      //  menuCat
      //  rule
      //  oidcAttribute
      //  oidcOPMetaDataNode
      //  oidcRPMetaDataNode
      //  samlAttribute
      //  samlIDPMetaDataNode
      //  samlSPMetaDataNode
      //  sfExtra
      //  virtualHost
      // ))->anchor_line->as_string
      if (node.type && node.type.match(/^(?:(?:c(?:as(?:App|Srv)MetaDataNod|mbModul)|oidc(?:[OR]PMetaDataNod|Attribut)|authChoic|rul)e|s(?:aml(?:(?:ID|S)PMetaDataNod|Attribut)e|fExtra)|key(?:Node|Text)|menu(?:App|Cat)|virtualHost)$/)) {
        return true;
      } else {
        return false;
      }
    };
    // method `getKey()`:
    // - return a promise with the data:
    // 	- from node when set
    // 	- after downloading else

    $scope.getKey = function (node) {
      var d, i, l, len, n, ref, tmp, uri;
      d = $q.defer();
      if (!node.data) {
        $scope.waiting = true;
        if (node.get && typeof node.get === 'object') {
          node.data = [];
          tmp = [];
          ref = node.get;
          for (i = l = 0, len = ref.length; l < len; i = ++l) {
            n = ref[i];
            node.data[i] = {
              title: n.split("/").pop(),
              get: n,
              id: n
            };
            tmp.push($scope.getKey(node.data[i]));
          }
          $q.all(tmp).then(function () {
            return d.resolve(node.data);
          }, function (response) {
            d.reject(response.statusLine);
            return $scope.waiting = false;
          });
        } else {
          uri = '';
          if (node.get) {
            console.debug(`Trying to get key ${node.get}`);
            uri = encodeURI(node.get);
          } else {
            console.debug(`Trying to get title ${node.title}`);
          }
          $http.get(`${window.viewPrefix}${$scope.currentCfg.cfgNum}/${node.get ? uri : node.title}`).then(function (response) {
            var data;
            // Set default value if response is null or if asked by server
            data = response.data;
            if ((data.value === null || data.error && data.error.match(/setDefault$/)) && node['default'] !== null) {
              node.data = node['default'];
            } else {
              node.data = data.value;
            }
            if (node.data && node.data.toString().match(/_Hidden_$/)) {
              node.type = 'text';
              node.data = '######';
            }
            // Cast int as int (remember that booleans are int for Perl)
            if (node.type && node.type.match(/^(bool|trool|boolOrExpr)$/)) {
              if (typeof node.data === 'string' && node.data.match(/^(?:-1|0|1)$/)) {
                node.data = parseInt(node.data, 10);
              }
            }
            if (node.type && node.type.match(/^int$/)) {
              node.data = parseInt(node.data, 10);
              // Split SAML types
            } else if (node.type && node.type.match(/^(saml(Service|Assertion)|blackWhiteList)$/) && !(typeof node.data === 'object')) {
              node.data = node.data.split(';');
            }
            $scope.waiting = false;
            return d.resolve(node.data);
          }, function (response) {
            readError(response);
            return d.reject(response.status);
          });
        }
      } else {
        if (node.data.toString().match(/_Hidden_$/)) {
          node.type = 'text';
          node.data = '######';
        }
        d.resolve(node.data);
      }
      return d.promise;
    };
    // function `pathEvent(event, next; current)`:
    // Called when $location.path() change, launch getCfg() with the new
    // configuration number
    pathEvent = function (event, next, current) {
      var n;
      n = next.match(new RegExp('#!?/view/(latest|[0-9]+)'));
      if (n === null) {
        return $location.path('/view/latest');
      } else {
        console.debug(`Trying to get cfg number ${n[1]}`);
        return $scope.getCfg(n[1]);
      }
    };
    $scope.$on('$locationChangeSuccess', pathEvent);
    // function `getCfg(n)`:
    // Download configuration metadatas
    $scope.getCfg = function (n) {
      if ($scope.currentCfg.cfgNum !== n) {
        return $http.get(`${window.viewPrefix}${n}`).then(function (response) {
          var d;
          $scope.currentCfg = response.data;
          d = new Date($scope.currentCfg.cfgDate * 1000);
          $scope.currentCfg.date = d.toLocaleString();
          console.debug(`Metadatas of cfg ${n} loaded`);
          $location.path(`/view/${n}`);
          return $scope.init();
        }, function (response) {
          return readError(response).then(function () {
            $scope.currentCfg.cfgNum = 0;
            return $scope.init();
          });
        });
      } else {
        return $scope.waiting = false;
      }
    };
    // method `getLanguage(lang)`
    // Launch init() after setting current language
    $scope.getLanguage = function (lang) {
      $scope.lang = lang;
      // Force reload home
      $scope.form = 'white';
      $scope.init();
      return $scope.showM = false;
    };
    // Initialization

    // Load JSON files:
    //	- struct.json: the main tree
    //	- languages/<lang>.json: the chosen language datas
    $scope.init = function () {
      var tmp;
      tmp = null;
      $scope.waiting = true;
      $scope.breadCrumb = null;
      $scope.data = [];
      $scope.confirmNeeded = false;
      $scope.forceSave = false;
      $q.all([$translator.init($scope.lang), $http.get(`${window.staticPrefix}struct.json`).then(function (response) {
        tmp = response.data;
        console.debug("Structure loaded");
      })]).then(function () {
        console.debug("Starting structure binding");
        $scope.data = tmp;
        tmp = null;
        if ($scope.currentCfg.cfgNum !== 0) {
          setScopeVars($scope);
        } else {
          $scope.message = {
            title: 'emptyConf',
            message: '__zeroConfExplanations__'
          };
          $scope.showModal('message.html');
        }
        $scope.form = 'homeViewer';
        return $scope.waiting = false;
      }, readError);
      // Colorized link
      $scope.activeModule = "viewer";
      return $scope.myStyle = {
        color: '#ffb84d'
      };
    };
    c = $location.path().match(new RegExp('^/view/(latest|[0-9]+)'));
    if (!c) {
      console.debug("Redirecting to /view/latest");
      return $location.path('/view/latest');
    }
  }]);

})();
