/**
 * An element which displays a form for manually starting a dockerfile build.
 */
angular.module('quay').directive('dockerfileBuildForm', function () {
  var directiveDefinitionObject = {
    priority: 0,
    templateUrl: '/static/directives/dockerfile-build-form.html',
    replace: false,
    transclude: false,
    restrict: 'C',
    scope: {
      'repository': '=repository',
      'startNow': '=startNow',
      'isReady': '=isReady',
      'uploadFailed': '&uploadFailed',
      'uploadStarted': '&uploadStarted',
      'buildStarted': '&buildStarted',
      'buildFailed': '&buildFailed',
      'missingFile': '&missingFile',
      'uploading': '=uploading',
      'building': '=building'
    },
    controller: function($scope, $element, ApiService, DockerfileService, Config) {
      var MEGABYTE = 1000000;
      var MAX_FILE_SIZE = 100 * MEGABYTE;

      var resetState = function() {
        $scope.hasDockerFile = false;
        $scope.pullEntity = null;
        $scope.dockerfileState = 'none';
        $scope.privateBaseRepository = null;
        $scope.isReady = false;
      };

      resetState();

      $scope.handleFilesChanged = function(files) {
        $scope.dockerfileError = '';
        $scope.privateBaseRepository = null;
        $scope.pullEntity = null;

        $scope.dockerfileState = 'loading';
        $scope.hasDockerFile = files.length > 0;

        var checkPrivateImage = function(baseImage) {
          var params = {
            'repository': baseImage
          };

          ApiService.getRepo(null, params).then(function(repository) {
            $scope.privateBaseRepository = repository.is_public ? null : baseImage;
            $scope.dockerfileState = 'ready';
          }, function() {
            $scope.privateBaseRepository = baseImage;
            $scope.dockerfileState = 'ready';
          });
        };

        var loadError = function(msg) {
          $scope.$apply(function() {
            $scope.dockerfileError = msg || 'Could not read uploaded Dockerfile';
            $scope.dockerfileState = 'error';
          });
        };

        var gotDockerfile = function(df) {
          $scope.$apply(function() {
            var baseImage = df.getRegistryBaseImage();
            if (baseImage) {
              checkPrivateImage(baseImage);
            } else {
              $scope.dockerfileState = 'ready';
            }
          });
        };

        if (files.length > 0) {
          if (files[0].size < MAX_FILE_SIZE) {
            DockerfileService.getDockerfile(files[0], gotDockerfile, loadError);
          } else {
            $scope.dockerfileState = 'ready';
          }
        } else {
          $scope.dockerfileState = 'none';
        }
      };

      var handleBuildFailed = function(message) {
        message = message || 'Dockerfile build failed to start';

        var result = false;
        if ($scope.buildFailed) {
          result = $scope.buildFailed({'message': message});
        }

        if (!result) {
          bootbox.dialog({
            "message": message,
            "title": "Cannot start Dockerfile build",
            "buttons": {
              "close": {
                "label": "Close",
                "className": "btn-primary"
              }
            }
          });
        }
      };

      var handleUploadFailed = function(message) {
        message = message || 'Error with file upload';

        var result = false;
        if ($scope.uploadFailed) {
          result = $scope.uploadFailed({'message': message});
        }

        if (!result) {
          bootbox.dialog({
            "message": message,
            "title": "Cannot upload file for Dockerfile build",
            "buttons": {
              "close": {
                "label": "Close",
                "className": "btn-primary"
              }
            }
          });
        }
      };

      var handleMissingFile = function() {
        var result = false;
        if ($scope.missingFile) {
          result = $scope.missingFile({});
        }

         if (!result) {
          bootbox.dialog({
            "message": 'A Dockerfile or an archive containing a Dockerfile is required',
            "title": "Missing Dockerfile",
            "buttons": {
              "close": {
                "label": "Close",
                "className": "btn-primary"
              }
            }
          });
        }
      };

      var startBuild = function(fileId) {
        $scope.building = true;

        var repo = $scope.repository;
        var data = {
          'file_id': fileId
        };

        if ($scope.pullEntity) {
          data['pull_robot'] = $scope.pullEntity['name'];
        }

        var params = {
          'repository': repo.namespace + '/' + repo.name,
        };

        ApiService.requestRepoBuild(data, params).then(function(resp) {
          $scope.building = false;
          $scope.uploading = false;

          if ($scope.buildStarted) {
            $scope.buildStarted({'build': resp});
          }
        }, function(resp) {
          $scope.building = false;
          $scope.uploading = false;

          handleBuildFailed(resp.message);
        });
      };

      var conductUpload = function(file, url, fileId, mimeType) {
        if ($scope.uploadStarted) {
          $scope.uploadStarted({});
        }

        var request = new XMLHttpRequest();
        request.open('PUT', url, true);
        request.setRequestHeader('Content-Type', mimeType);
        request.onprogress = function(e) {
          $scope.$apply(function() {
            var percentLoaded;
            if (e.lengthComputable) {
              $scope.upload_progress = (e.loaded / e.total) * 100;
            }
          });
        };

        request.onerror = function() {
          $scope.$apply(function() {
            handleUploadFailed();
          });
        };

        request.onreadystatechange = function() {
          var state = request.readyState;
          var status = request.status;

          if (state == 4) {
            if (Math.floor(status / 100) == 2) {
              $scope.$apply(function() {
                startBuild(fileId);
                $scope.uploading = false;
              });
            } else {
              var message = request.statusText;
              if (status == 413) {
                message = 'Selected file too large to upload';
              }

              $scope.$apply(function() {
                handleUploadFailed(message);
              });
            }
          }
        };

        request.send(file);
      };

      var startFileUpload = function(repo) {
        $scope.uploading = true;
        $scope.uploading_progress = 0;

        var uploader = $element.find('#file-drop')[0];
        if (uploader.files.length == 0) {
          handleMissingFile();
          $scope.uploading = false;
          return;
        }

        var file = uploader.files[0];
        $scope.upload_file = file.name;

        var mimeType = file.type || 'application/octet-stream';
        var data = {
          'mimeType': mimeType
        };

        var getUploadUrl = ApiService.getFiledropUrl(data).then(function(resp) {
          conductUpload(file, resp.url, resp.file_id, mimeType);
        }, function() {
          handleUploadFailed('Could not retrieve upload URL');
        });
      };

      var checkReady = function() {
        $scope.isReady = ($scope.dockerfileState == 'ready' &&
                          (!$scope.privateBaseRepository || $scope.pullEntity));
      };

      var checkEntity = function() {
        $scope.currentRobotHasPermission = null;
        if (!$scope.pullEntity) { return; }

        var permParams = {
          'repository': $scope.privateBaseRepository,
          'username': $scope.pullEntity.name
        };

        ApiService.getUserTransitivePermission(null, permParams).then(function(resp) {
          $scope.currentRobotHasPermission = resp['permissions'].length > 0;
        });
      };

      $scope.$watch('pullEntity', checkEntity);
      $scope.$watch('pullEntity', checkReady);
      $scope.$watch('dockerfileState', checkReady);

      $scope.$watch('repository', resetState);

      $scope.$watch('startNow', function() {
        if ($scope.startNow && $scope.repository && !$scope.uploading && !$scope.building) {
          startFileUpload();
        }
      });
    }
  };
  return directiveDefinitionObject;
});