/*
 * Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may obtain
 * a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

/**
 * A module of resources that talk with the ironic API.
 */
angular.module('ironic.api', ['openstack', 'ngResource'])
  .config(function($$configurationProvider, $windowProvider) {
    'use strict';

    // Build a default ironic location from the $window provider
    var location = $windowProvider.$get().location;
    var apiRoot = location.protocol + '//' + location.hostname + ':6385';

    // This line registers ironic's default API root, as detected via the current hostname, with
    // the $$configurationProvider's default API detection mechanism.
    $$configurationProvider.$registerDefault('ironic', apiRoot);
  });

/*
 * Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may obtain
 * a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

/**
 * IronicChassis is an ngResource abstraction, which switches which Ironic it talks to based on the
 * selected cloud configuration provided by the $$configuration service. It may be used as if it
 * was an ngResource instance, and supports the methods `query`, `create`, `read`, `update`, and
 * `remove`.
 */
angular.module('ironic.api').factory('IronicChassis',
  function($log, $$selectedConfiguration, $$resourceCache, $resource, $$dummyResource) {

    /**
     * This method extracts the current active API root URI from $$configuration, ensures that
     * the appropriate Ironic resources exists in the $$resourceCache, and returns it.
     */
    function getResource () {
      // Pull the current configuration.
      var currentConfig = $$selectedConfiguration.get();

      // This should resolve the API root, except in cases where the selected configuration is
      // invalid and/or has not yet been resolved.
      var ironicConfig = currentConfig.ironic || {};
      var ironicApiRoot = ironicConfig.apiRoot || null;

      if (!ironicApiRoot) {
        $log.warn('Ironic API Root for Config [' + currentConfig.id + '] not found.');
        return $$dummyResource;
      }

      // Build and/or retrieve a cached instance of the requested service.
      var chassisUrl = ironicApiRoot + '/chassis/:uuid';

      if (!$$resourceCache.has(chassisUrl)) {
        $log.debug("Creating new IronicChassis at: " + chassisUrl);
        var resource = $resource(chassisUrl, {'uuid': '@uuid'}, {
          'query': {
            'method': 'GET',
            'isArray': true,
            'transformResponse': function(data) {
              var parsed = angular.fromJson(data);
              return parsed.chassis;
            }
          },
          'create': {
            'method': 'POST'
          },
          'read': {
            'method': 'GET'
          },
          'update': {
            'method': 'PUT'
          },
          'remove': {
            'method': 'DELETE'
          }
        });

        $$resourceCache.set(chassisUrl, resource);
      }

      return $$resourceCache.get(chassisUrl);
    }

    return {
      'query': function() {
        var r = getResource();
        return r.query.apply(r, arguments);
      },
      'create': function() {
        var r = getResource();
        return r.create.apply(r, arguments);
      },
      'read': function() {
        var r = getResource();
        return r.read.apply(r, arguments);
      },
      'update': function() {
        var r = getResource();
        return r.update.apply(r, arguments);
      },
      'remove': function() {
        var r = getResource();
        return r.remove.apply(r, arguments);
      }
    };
  });


/*
 * Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may obtain
 * a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

/**
 * IronicDriver is an ngResource abstraction, which switches which Ironic it talks to based on the
 * selected cloud configuration provided by the $$configuration service. It may be used as if it
 * was an ngResource instance, and supports the methods `query`, `create`, `read`, `update`, and
 * `remove`.
 */
angular.module('ironic.api').factory('IronicDriver',
  function($log, $$selectedConfiguration, $$resourceCache, $resource, $$dummyResource) {

    /**
     * This method extracts the current active API root URI from $$configuration, ensures that
     * the appropriate Ironic resources exists in the $$resourceCache, and returns it.
     */
    function getResource () {
      // Pull the current configuration.
      var currentConfig = $$selectedConfiguration.get();

      // This should resolve the API root, except in cases where the selected configuration is
      // invalid and/or has not yet been resolved.
      var ironicConfig = currentConfig.ironic || {};
      var ironicApiRoot = ironicConfig.apiRoot || null;

      if (!ironicApiRoot) {
        $log.warn('Ironic API Root for Config [' + currentConfig.id + '] not found.');
        return $$dummyResource;
      }

      // Build and/or retrieve a cached instance of the requested service.
      var driverUrl = ironicApiRoot + '/drivers/:uuid';

      if (!$$resourceCache.has(driverUrl)) {
        $log.debug("Creating new IronicDriver at: " + driverUrl);
        var resource = $resource(driverUrl, {'uuid': '@uuid'}, {
          'query': {
            'method': 'GET',
            'isArray': true,
            'transformResponse': function(data) {
              var parsed = angular.fromJson(data);
              return parsed.drivers;
            }
          },
          'create': {
            'method': 'POST'
          },
          'read': {
            'method': 'GET'
          },
          'update': {
            'method': 'PUT'
          },
          'remove': {
            'method': 'DELETE'
          }
        });

        $$resourceCache.set(driverUrl, resource);
      }

      return $$resourceCache.get(driverUrl);
    }

    return {
      'query': function() {
        var r = getResource();
        return r.query.apply(r, arguments);
      },
      'create': function() {
        var r = getResource();
        return r.create.apply(r, arguments);
      },
      'read': function() {
        var r = getResource();
        return r.read.apply(r, arguments);
      },
      'update': function() {
        var r = getResource();
        return r.update.apply(r, arguments);
      },
      'remove': function() {
        var r = getResource();
        return r.remove.apply(r, arguments);
      }
    };
  });

/*
 * Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may obtain
 * a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

/**
 * IronicNode is an ngResource abstraction, which switches which Ironic it talks to based on the
 * selected cloud configuration provided by the $$configuration service. It may be used as if it
 * was an ngResource instance, and supports the methods `query`, `create`, `read`, `update`, and
 * `remove`.
 */
angular.module('ironic.api').factory('IronicNode',
  function($log, $$selectedConfiguration, $$resourceCache, $resource, $$dummyResource) {

    /**
     * This method extracts the current active API root URI from $$configuration, ensures that
     * the appropriate Ironic resources exists in the $$resourceCache, and returns it.
     */
    function getResource () {
      // Pull the current configuration.
      var currentConfig = $$selectedConfiguration.get();

      // This should resolve the API root, except in cases where the selected configuration is
      // invalid and/or has not yet been resolved.
      var ironicConfig = currentConfig.ironic || {};
      var ironicApiRoot = ironicConfig.apiRoot || null;

      if (!ironicApiRoot) {
        $log.warn('Ironic API Root for Config [' + currentConfig.id + '] not found.');
        return $$dummyResource;
      }

      // Build and/or retrieve a cached instance of the requested service.
      var nodeUrl = ironicApiRoot + '/nodes/:uuid';

      if (!$$resourceCache.has(nodeUrl)) {
        $log.debug("Creating new IronicNode at: " + nodeUrl);
        var resource = $resource(nodeUrl, {'uuid': '@uuid'}, {
          'query': {
            'method': 'GET',
            'isArray': true,
            'transformResponse': function(data) {
              var parsed = angular.fromJson(data);
              return parsed.nodes;
            }
          },
          'create': {
            'method': 'POST'
          },
          'read': {
            'method': 'GET'
          },
          'update': {
            'method': 'PUT'
          },
          'remove': {
            'method': 'DELETE'
          }
        });

        $$resourceCache.set(nodeUrl, resource);
      }

      return $$resourceCache.get(nodeUrl);
    }

    return {
      'query': function() {
        var r = getResource();
        return r.query.apply(r, arguments);
      },
      'create': function() {
        var r = getResource();
        return r.create.apply(r, arguments);
      },
      'read': function() {
        var r = getResource();
        return r.read.apply(r, arguments);
      },
      'update': function() {
        var r = getResource();
        return r.update.apply(r, arguments);
      },
      'remove': function() {
        var r = getResource();
        return r.remove.apply(r, arguments);
      }
    };
  });

/*
 * Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may obtain
 * a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

/**
 * IronicPort is an ngResource abstraction, which switches which Ironic it talks to based on the
 * selected cloud configuration provided by the $$configuration service. It may be used as if it
 * was an ngResource instance, and supports the methods `query`, `create`, `read`, `update`, and
 * `remove`.
 */
angular.module('ironic.api').factory('IronicPort',
  function($log, $$selectedConfiguration, $$resourceCache, $resource, $$dummyResource) {

    /**
     * This method extracts the current active API root URI from $$configuration, ensures that
     * the appropriate Ironic resources exists in the $$resourceCache, and returns it.
     */
    function getResource () {
      // Pull the current configuration.
      var currentConfig = $$selectedConfiguration.get();

      // This should resolve the API root, except in cases where the selected configuration is
      // invalid and/or has not yet been resolved.
      var ironicConfig = currentConfig.ironic || {};
      var ironicApiRoot = ironicConfig.apiRoot || null;

      if (!ironicApiRoot) {
        $log.warn('Ironic API Root for Config [' + currentConfig.id + '] not found.');
        return $$dummyResource;
      }

      // Build and/or retrieve a cached instance of the requested service.
      var portUrl = ironicApiRoot + '/ports/:uuid';

      if (!$$resourceCache.has(portUrl)) {
        $log.debug("Creating new IronicPort at: " + portUrl);
        var resource = $resource(portUrl, {'uuid': '@uuid'}, {
          'query': {
            'method': 'GET',
            'isArray': true,
            'transformResponse': function(data) {
              var parsed = angular.fromJson(data);
              return parsed.ports;
            }
          },
          'create': {
            'method': 'POST'
          },
          'read': {
            'method': 'GET'
          },
          'update': {
            'method': 'PUT'
          },
          'remove': {
            'method': 'DELETE'
          }
        });

        $$resourceCache.set(portUrl, resource);
      }

      return $$resourceCache.get(portUrl);
    }

    return {
      'query': function() {
        var r = getResource();
        return r.query.apply(r, arguments);
      },
      'create': function() {
        var r = getResource();
        return r.create.apply(r, arguments);
      },
      'read': function() {
        var r = getResource();
        return r.read.apply(r, arguments);
      },
      'update': function() {
        var r = getResource();
        return r.update.apply(r, arguments);
      },
      'remove': function() {
        var r = getResource();
        return r.remove.apply(r, arguments);
      }
    };
  });

/*
 * Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
 *
 * Licensed under the Apache License, Version 2.0 (the 'License'); you may
 * not use this file except in compliance with the License. You may obtain
 * a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

/**
 * Form validator that will perform an asynchronous check to see if Ironic is
 * available at the provided url. It will expose a variable on the control to
 * which it is applied, with an array of detected API versions.
 */
/*eslint-disable angular/ng_no_services */
angular.module('ironic.api').directive('ironicApiUrl',
  function ($q, $http) {
/*eslint-enable angular/ng_no_services */
    'use strict';

    return {
      'require': 'ngModel',
      'link': function (scope, elm, attrs, ctrl) {

        ctrl.$ironicVersions = [];

        ctrl.$asyncValidators.ironicApiUrl =
          function (modelValue) {
            var def = $q.defer();

            $http.get(modelValue).then(function (result) {
              var name = result.data.name;

              if (name !== 'OpenStack Ironic API') {
                def.reject();
              }

              var versions = result.data.versions || [];
              for (var i = 0; i < versions.length; i++) {
                versions[i] = versions[i].id;
              }
              ctrl.$ironicVersions = versions;
              def.resolve();
            }, function () {
              def.reject();
            });

            return def.promise;
          };
      }
    };
  });
