// @@OLD_SERVICE_CONTROLLER: this service should be rewritten to use services.
// See app/components/templates/templatesController.js as a reference.
angular.module('createContainer', [])
.controller('CreateContainerController', ['$q', '$scope', '$state', '$stateParams', '$filter', 'Container', 'ContainerHelper', 'Image', 'ImageHelper', 'Volume', 'NetworkService', 'ResourceControlService', 'Authentication', 'Notifications', 'ContainerService', 'ImageService', 'FormValidator', 'ModalService', 'RegistryService',
function ($q, $scope, $state, $stateParams, $filter, Container, ContainerHelper, Image, ImageHelper, Volume, NetworkService, ResourceControlService, Authentication, Notifications, ContainerService, ImageService, FormValidator, ModalService, RegistryService) {

  $scope.formValues = {
    alwaysPull: true,
    Console: 'none',
    Volumes: [],
    NetworkContainer: '',
    Labels: [],
    ExtraHosts: [],
    IPv4: '',
    IPv6: '',
    AccessControlData: new AccessControlFormData()
  };

  $scope.state = {
    formValidationError: ''
  };

  $scope.config = {
    Image: '',
    Env: [],
    Cmd: '',
    ExposedPorts: {},
    HostConfig: {
      RestartPolicy: {
        Name: 'no'
      },
      PortBindings: [],
      PublishAllPorts: false,
      Binds: [],
      NetworkMode: 'bridge',
      Privileged: false,
      ExtraHosts: [],
      Devices:[]
    },
    NetworkingConfig: {
      EndpointsConfig: {}
    },
    Labels: {}
  };

  $scope.addVolume = function() {
    $scope.formValues.Volumes.push({ name: '', containerPath: '', readOnly: false, type: 'volume' });
  };

  $scope.removeVolume = function(index) {
    $scope.formValues.Volumes.splice(index, 1);
  };

  $scope.addEnvironmentVariable = function() {
    $scope.config.Env.push({ name: '', value: ''});
  };

  $scope.removeEnvironmentVariable = function(index) {
    $scope.config.Env.splice(index, 1);
  };

  $scope.addPortBinding = function() {
    $scope.config.HostConfig.PortBindings.push({ hostPort: '', containerPort: '', protocol: 'tcp' });
  };

  $scope.removePortBinding = function(index) {
    $scope.config.HostConfig.PortBindings.splice(index, 1);
  };

  $scope.addLabel = function() {
    $scope.formValues.Labels.push({ name: '', value: ''});
  };

  $scope.removeLabel = function(index) {
    $scope.formValues.Labels.splice(index, 1);
  };

  $scope.addExtraHost = function() {
    $scope.formValues.ExtraHosts.push({ value: '' });
  };

  $scope.removeExtraHost = function(index) {
    $scope.formValues.ExtraHosts.splice(index, 1);
  };

  $scope.addDevice = function() {
    $scope.config.HostConfig.Devices.push({ pathOnHost: '', pathInContainer: '' });
  };

  $scope.removeDevice = function(index) {
    $scope.config.HostConfig.Devices.splice(index, 1);
  };

  $scope.fromContainerMultipleNetworks = false;

  function prepareImageConfig(config) {
    var image = config.Image;
    var registry = $scope.formValues.Registry;
    var imageConfig = ImageHelper.createImageConfigForContainer(image, registry.URL);
    config.Image = imageConfig.fromImage + ':' + imageConfig.tag;
    $scope.imageConfig = imageConfig;
  }

  function preparePortBindings(config) {
    var bindings = {};
    config.HostConfig.PortBindings.forEach(function (portBinding) {
      if (portBinding.containerPort) {
        var key = portBinding.containerPort + '/' + portBinding.protocol;
        var binding = {};
        if (portBinding.hostPort && portBinding.hostPort.indexOf(':') > -1) {
          var hostAndPort = portBinding.hostPort.split(':');
          binding.HostIp = hostAndPort[0];
          binding.HostPort = hostAndPort[1];
        } else {
          binding.HostPort = portBinding.hostPort;
        }
        bindings[key] = [binding];
        config.ExposedPorts[key] = {};
      }
    });
    config.HostConfig.PortBindings = bindings;
  }

  function prepareConsole(config) {
    var value = $scope.formValues.Console;
    var openStdin = true;
    var tty = true;
    if (value === 'tty') {
      openStdin = false;
    } else if (value === 'interactive') {
      tty = false;
    } else if (value === 'none') {
      openStdin = false;
      tty = false;
    }
    config.OpenStdin = openStdin;
    config.Tty = tty;
  }

  function prepareEnvironmentVariables(config) {
    var env = [];
    config.Env.forEach(function (v) {
      if (v.name && v.value) {
        env.push(v.name + '=' + v.value);
      }
    });
    config.Env = env;
  }

  function prepareVolumes(config) {
    var binds = [];
    var volumes = {};

    $scope.formValues.Volumes.forEach(function (volume) {
      var name = volume.name;
      var containerPath = volume.containerPath;
      if (name && containerPath) {
        var bind = name + ':' + containerPath;
        volumes[containerPath] = {};
        if (volume.readOnly) {
          bind += ':ro';
        }
        binds.push(bind);
      }
    });
    config.HostConfig.Binds = binds;
    config.Volumes = volumes;
  }

  function prepareNetworkConfig(config) {
    var mode = config.HostConfig.NetworkMode;
    var container = $scope.formValues.NetworkContainer;
    var containerName = container;
    if (container && typeof container === 'object') {
      containerName = $filter('trimcontainername')(container.Names[0]);
      if ($scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM') {
        containerName = $filter('swarmcontainername')(container);
      }
    }
    var networkMode = mode;
    if (containerName) {
      networkMode += ':' + containerName;
      config.Hostname = '';
    }
    config.HostConfig.NetworkMode = networkMode;

    config.NetworkingConfig.EndpointsConfig[networkMode] = {
      IPAMConfig: {
        IPv4Address: $scope.formValues.IPv4,
        IPv6Address: $scope.formValues.IPv6
      }
    };

    $scope.formValues.ExtraHosts.forEach(function (v) {
    if (v.value) {
        config.HostConfig.ExtraHosts.push(v.value);
      }
    });
  }

  function prepareLabels(config) {
    var labels = {};
    $scope.formValues.Labels.forEach(function (label) {
      if (label.name && label.value) {
        labels[label.name] = label.value;
      }
    });
    config.Labels = labels;
  }

  function prepareDevices(config) {
    var path = [];
    config.HostConfig.Devices.forEach(function (p) {
      if (p.pathOnHost) {
        if(p.pathInContainer === '') {
          p.pathInContainer = p.pathOnHost;
        }
        path.push({PathOnHost:p.pathOnHost,PathInContainer:p.pathInContainer,CgroupPermissions:'rwm'});
      }
    });
    config.HostConfig.Devices = path;
  }

  function prepareConfiguration() {
    var config = angular.copy($scope.config);
    config.Cmd = ContainerHelper.commandStringToArray(config.Cmd);
    prepareNetworkConfig(config);
    prepareImageConfig(config);
    preparePortBindings(config);
    prepareConsole(config);
    prepareEnvironmentVariables(config);
    prepareVolumes(config);
    prepareLabels(config);
    prepareDevices(config);
    return config;
  }

  function confirmCreateContainer() {
    var deferred = $q.defer();
    Container.query({ all: 1, filters: {name: ['^/' + $scope.config.name + '$'] }}).$promise
    .then(function success(data) {
      var existingContainer = data[0];
      if (existingContainer) {
        ModalService.confirm({
          title: 'Are you sure ?',
          message: 'A container with the same name already exists. Portainer can automatically remove it and re-create one. Do you want to replace it?',
          buttons: {
            confirm: {
              label: 'Replace',
              className: 'btn-danger'
            }
          },
          callback: function onConfirm(confirmed) {
            if(!confirmed) { deferred.resolve(false); }
            else {
              // Remove old container
              ContainerService.remove(existingContainer, true)
              .then(function success(data) {
                Notifications.success('Container Removed', existingContainer.Id);
                deferred.resolve(true);
              })
              .catch(function error(err) {
                deferred.reject({ msg: 'Unable to remove container', err: err });
              });
            }
          }
        });
      } else {
        deferred.resolve(true);
      }
    })
    .catch(function error(err) {
      Notifications.error('Failure', err, 'Unable to retrieve containers');
      return undefined;
    });
    return deferred.promise;
  }

  function loadFromContainerCmd(d) {
    if ($scope.config.Cmd) {
      $scope.config.Cmd = ContainerHelper.commandArrayToString($scope.config.Cmd);
    } else {
      $scope.config.Cmd = '';
    }
  }

  function loadFromContainerPortBindings(d) {
    var bindings = [];
    for (var p in $scope.config.HostConfig.PortBindings) {
      if ({}.hasOwnProperty.call($scope.config.HostConfig.PortBindings, p)) {
	var hostPort = '';
        if ($scope.config.HostConfig.PortBindings[p][0].HostIp) {
          hostPort = $scope.config.HostConfig.PortBindings[p][0].HostIp + ':';
        }
        hostPort += $scope.config.HostConfig.PortBindings[p][0].HostPort;
        var b = {
          'hostPort': hostPort,
          'containerPort': p.split('/')[0],
          'protocol': p.split('/')[1]
        };
        bindings.push(b);
      }
    }
    $scope.config.HostConfig.PortBindings = bindings;
  }

  function loadFromContainerVolumes(d) {
    for (var v in d.Mounts) {
      if ({}.hasOwnProperty.call(d.Mounts, v)) {
        var mount = d.Mounts[v];
        var volume = {
          'type': mount.Type,
          'name': mount.Name || mount.Source,
          'containerPath': mount.Destination,
          'readOnly': mount.RW === false
        };
        $scope.formValues.Volumes.push(volume);
      }
    }
  }

  function loadFromContainerNetworkConfig(d) {
    $scope.config.NetworkingConfig = {
      EndpointsConfig: {}
    };
    var networkMode = d.HostConfig.NetworkMode;
    if (networkMode === 'default') {
      $scope.config.HostConfig.NetworkMode = 'bridge';
      if (!_.find($scope.availableNetworks, {'Name': 'bridge'})) {
        $scope.config.HostConfig.NetworkMode = 'nat';
      }
    }
    if ($scope.config.HostConfig.NetworkMode.indexOf('container:') === 0) {
      var netContainer = $scope.config.HostConfig.NetworkMode.split(/^container:/)[1];
      $scope.config.HostConfig.NetworkMode = 'container';
      for (var c in $scope.runningContainers) {
        if ($scope.runningContainers[c].Names && $scope.runningContainers[c].Names[0] === '/' + netContainer) {
          $scope.formValues.NetworkContainer = $scope.runningContainers[c];
        }
      }
    }
    $scope.fromContainerMultipleNetworks = Object.keys(d.NetworkSettings.Networks).length >= 2;
    if (d.NetworkSettings.Networks[$scope.config.HostConfig.NetworkMode]) {
      if (d.NetworkSettings.Networks[$scope.config.HostConfig.NetworkMode].IPAMConfig) {
        if (d.NetworkSettings.Networks[$scope.config.HostConfig.NetworkMode].IPAMConfig.IPv4Address) {
          $scope.formValues.IPv4 = d.NetworkSettings.Networks[$scope.config.HostConfig.NetworkMode].IPAMConfig.IPv4Address;
        }
        if (d.NetworkSettings.Networks[$scope.config.HostConfig.NetworkMode].IPAMConfig.IPv6Address) {
          $scope.formValues.IPv6 = d.NetworkSettings.Networks[$scope.config.HostConfig.NetworkMode].IPAMConfig.IPv6Address;
        }
      }
    }
    $scope.config.NetworkingConfig.EndpointsConfig[$scope.config.HostConfig.NetworkMode] = d.NetworkSettings.Networks[$scope.config.HostConfig.NetworkMode];
    // ExtraHosts
    for (var h in $scope.config.HostConfig.ExtraHosts) {
      if ({}.hasOwnProperty.call($scope.config.HostConfig.ExtraHosts, h)) {
        $scope.formValues.ExtraHosts.push({'value': $scope.config.HostConfig.ExtraHosts[h]});
        $scope.config.HostConfig.ExtraHosts = [];
      }
    }
  }

  function loadFromContainerEnvrionmentVariables(d) {
    var envArr = [];
    for (var e in $scope.config.Env) {
      if ({}.hasOwnProperty.call($scope.config.Env, e)) {
        var arr = $scope.config.Env[e].split(/\=(.+)/);
        envArr.push({'name': arr[0], 'value': arr[1]});
      }
    }
    $scope.config.Env = envArr;
  }

  function loadFromContainerLabels(d) {
    for (var l in $scope.config.Labels) {
      if ({}.hasOwnProperty.call($scope.config.Labels, l)) {
        $scope.formValues.Labels.push({ name: l, value: $scope.config.Labels[l]});
      }
    }
  }

  function loadFromContainerConsole(d) {
    if ($scope.config.OpenStdin && $scope.config.Tty) {
      $scope.formValues.Console = 'both';
    } else if (!$scope.config.OpenStdin && $scope.config.Tty) {
      $scope.formValues.Console = 'tty';
    } else if ($scope.config.OpenStdin && !$scope.config.Tty) {
      $scope.formValues.Console = 'interactive';
    } else if (!$scope.config.OpenStdin && !$scope.config.Tty) {
      $scope.formValues.Console = 'none';
    }
  }

  function loadFromContainerDevices(d) {
    var path = [];
    for (var dev in $scope.config.HostConfig.Devices) {
      if ({}.hasOwnProperty.call($scope.config.HostConfig.Devices, dev)) {
        var device = $scope.config.HostConfig.Devices[dev];
        path.push({'pathOnHost': device.PathOnHost, 'pathInContainer': device.PathInContainer});
      }
    }
    $scope.config.HostConfig.Devices = path;
  }

  function loadFromContainerImageConfig(d) {
    // If no registry found, we let default DockerHub and let full image path
    var imageInfo = ImageHelper.extractImageAndRegistryFromRepository($scope.config.Image);
    RegistryService.retrieveRegistryFromRepository($scope.config.Image)
    .then(function success(data) {
      if (data) {
        $scope.config.Image = imageInfo.image;
        $scope.formValues.Registry = data;
      }
    })
    .catch(function error(err) {
      Notifications.error('Failure', err, 'Unable to retrive registry');
    });
  }

  function loadFromContainerSpec() {
    // Get container
    Container.get({ id: $stateParams.from }).$promise
    .then(function success(d) {
      var fromContainer = new ContainerDetailsViewModel(d);
      if (!fromContainer.ResourceControl) {
        $scope.formValues.AccessControlData.AccessControlEnabled = false;
      }
      $scope.fromContainer = fromContainer;
      $scope.config = ContainerHelper.configFromContainer(fromContainer.Model);
      loadFromContainerCmd(d);
      loadFromContainerPortBindings(d);
      loadFromContainerVolumes(d);
      loadFromContainerNetworkConfig(d);
      loadFromContainerEnvrionmentVariables(d);
      loadFromContainerLabels(d);
      loadFromContainerConsole(d);
      loadFromContainerDevices(d);
      loadFromContainerImageConfig(d);
    })
    .catch(function error(err) {
      Notifications.error('Failure', err, 'Unable to retrieve container');
    });
  }

  function initView() {
    Volume.query({}, function (d) {
      $scope.availableVolumes = d.Volumes;
    }, function (e) {
      Notifications.error('Failure', e, 'Unable to retrieve volumes');
    });

    var provider = $scope.applicationState.endpoint.mode.provider;
    var apiVersion = $scope.applicationState.endpoint.apiVersion;
    NetworkService.networks(
      provider === 'DOCKER_STANDALONE' || provider === 'DOCKER_SWARM_MODE',
      false,
      provider === 'DOCKER_SWARM_MODE' && apiVersion >= 1.25,
      provider === 'DOCKER_SWARM'
    )
    .then(function success(data) {
      var networks = data;
      networks.push({ Name: 'container' });
      $scope.availableNetworks = networks;

      if (_.find(networks, {'Name': 'nat'})) {
        $scope.config.HostConfig.NetworkMode = 'nat';
      }
    })
    .catch(function error(err) {
      Notifications.error('Failure', err, 'Unable to retrieve networks');
    });

    Container.query({}, function (d) {
      var containers = d;
      $scope.runningContainers = containers;
      if ($stateParams.from !== '') {
        loadFromContainerSpec();
      } else {
        $scope.fromContainer = {};
        $scope.formValues.Registry = {};
      }
    }, function(e) {
      Notifications.error('Failure', e, 'Unable to retrieve running containers');
    });

  }

  function validateForm(accessControlData, isAdmin) {
    $scope.state.formValidationError = '';
    var error = '';
    error = FormValidator.validateAccessControl(accessControlData, isAdmin);

    if (error) {
      $scope.state.formValidationError = error;
      return false;
    }
    return true;
  }

  $scope.create = function () {
    confirmCreateContainer()
    .then(function success(confirm) {
      if (!confirm) {
        return false;
      }
      $('#createContainerSpinner').show();
      var accessControlData = $scope.formValues.AccessControlData;
      var userDetails = Authentication.getUserDetails();
      var isAdmin = userDetails.role === 1 ? true : false;

      if (!validateForm(accessControlData, isAdmin)) {
        $('#createContainerSpinner').hide();
        return;
      }

      var config = prepareConfiguration();
      createContainer(config, accessControlData);
    })
    .catch(function error(err) {
      Notifications.error('Failure', err, 'Unable to create container');
    });
  };

  function createContainer(config, accessControlData) {
    $q.when(!$scope.formValues.alwaysPull || ImageService.pullImage($scope.config.Image, $scope.formValues.Registry, true))
    .finally(function final() {
      ContainerService.createAndStartContainer(config)
      .then(function success(data) {
        var containerIdentifier = data.Id;
        var userId = Authentication.getUserDetails().ID;
        return ResourceControlService.applyResourceControl('container', containerIdentifier, userId, accessControlData, []);
      })
      .then(function success() {
        Notifications.success('Container successfully created');
        $state.go('containers', {}, {reload: true});
      })
      .catch(function error(err) {
        Notifications.error('Failure', err, 'Unable to create container');
      })
      .finally(function final() {
        $('#createContainerSpinner').hide();
      });
    });
  }

  initView();
}]);