text: /* Autogenerated with Kurento Idl */
  
  /*
   * (C) Copyright 2013-2015 Kurento (http://kurento.org/)
   *
   * 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.
   */
  
  var inherits = require('inherits');
  
  var kurentoClient = require('kurento-client');
  
  var disguise = kurentoClient.disguise;
  
  var checkType      = kurentoClient.checkType;
  var ChecktypeError = checkType.ChecktypeError;
  
  
  var Transaction = kurentoClient.TransactionsManager.Transaction;
  
  var promiseCallback = require('promisecallback');
  
  var EventEmitter = require('events').EventEmitter;
  
  
  function noop(error, result) {
    if (error) console.trace(error);
  
    return result
  };
  
  
  /**
   * @classdesc
   *  <p>Base interface used to manage capabilities common to all Kurento 
   *  elements. This includes both: {@link module:core/abstracts.MediaElement 
   *  MediaElement} and {@link module:core.MediaPipeline MediaPipeline}</p>
   *        <h4>Properties</h4>
   *        <ul>
   *          <li><b>id</b>: unique identifier assigned to this 
   *          <code>MediaObject</code> at instantiation time. {@link 
   *          module:core.MediaPipeline MediaPipeline} IDs are generated with a 
   *          GUID followed by suffix <code>_kurento.MediaPipeline</code>. {@link 
   *          module:core/abstracts.MediaElement MediaElement} IDs are also a GUID
   *            <blockquote>
   *            <dl>
   *              <dt><i>MediaPipeline ID example</i></dt>
   *              <dd><code>907cac3a-809a-4bbe-a93e-ae7e944c5cae_kurento.MediaPipeline</code></dd>
   *              <dt><i>MediaElement ID example</i></dt> 
   *              <dd><code>907cac3a-809a-4bbe-a93e-ae7e944c5cae_kurento.MediaPipeline/403da25a-805b-4cf1-8c55-f190588e6c9b_kurento.WebRtcEndpoint</code></dd>
   *            </dl>
   *            </blockquote>
   *          </li>
   *          <li><b>name</b>: free text intended to provide a friendly name for 
   *          this <code>MediaObject</code>. Its default value is the same as the 
   *          ID.</li>
   *          <li><b>tags</b>: key-value pairs intended for applications to 
   *          associate metadata to this <code>MediaObject</code> instance.</li>
   *        </ul>
   *        <p>
   *        <h4>Events</h4>
   *        <ul>
   *          <li>`ErrorEvent`: reports asynchronous error events. It is 
   *          recommended to always subscribe a listener to this event, as regular
   *        </ul>
   *
   * @abstract
   * @extends external:EventEmitter
   *
   * @constructor module:core/abstracts.MediaObject
   *
   * @fires {@link module:core#event:Error Error}
   */
  function MediaObject(){
    MediaObject.super_.call(this);
  
  
    var self = this;
  
  
    //
    // Define object properties
    //
  
    /**
     * Unique identifier of this object
     *
     * @public
     * @readonly
     * @member {external:Number} id
     */
    this.once('_id', function(error, id)
    {
      if(error)
        return Object.defineProperties(this,
        {
          '_createError': {value: error},
          'id': {value: null, enumerable: true}
        });
  
      Object.defineProperty(this, 'id',
      {
        configurable: true,
        enumerable: true,
        value: id
      });
    })
  
    //
    // Subscribe and unsubscribe events on the server when adding and removing
    // event listeners on this MediaObject
    //
  
    var subscriptions = {};
  
    this.on('removeListener', function(event, listener)
    {
      // Blacklisted events
      if(event[0] == '_'
      || event == 'release'
      || event == 'newListener')
        return;
  
      var count = EventEmitter.listenerCount(this, event);
      if(count) return;
  
      var token = subscriptions[event];
  
      var params =
      {
        object: this,
        subscription: token.value,
        sessionId: token.sessionId
      };
  
      this.emit('_rpc', undefined, 'unsubscribe', params, function(error)
      {
        if(error) return self.emit('error', error);
  
        delete subscriptions[event];
      });
    });
  
    this.on('newListener', function(event, listener)
    {
      // Blacklisted events
      if(event[0] == '_'
      || event == 'release')
        return;
  
      var constructor = this.constructor;
  
      if(constructor.events.indexOf(event) < 0)
        throw new SyntaxError(constructor.name+" doesn't accept events of type '"+event+"'")
  
      var count = EventEmitter.listenerCount(this, event);
      if(count) return;
  
      var params =
      {
        object: this,
        type: event
      };
  
      this.emit('_rpc', undefined, 'subscribe', params, function(error, token)
      {
        if(error) return self.emit('error', error);
  
        subscriptions[event] = token;
      });
    });
  };
  inherits(MediaObject, EventEmitter);
  
  
  //
  // Public properties
  //
  
  /**
   * children of this <code>MediaObject</code>.
   *
   * @alias module:core/abstracts.MediaObject#getChildren
   *
   * @param {module:core/abstracts.MediaObject~getChildrenCallback} [callback]
   *
   * @return {external:Promise}
   */
  MediaObject.prototype.getChildren = function(callback){
    var transaction = (arguments[0] instanceof Transaction)
                    ? Array.prototype.shift.apply(arguments)
                    : undefined;
  
    var usePromise = false;
    
    if (callback == undefined) {
      usePromise = true;
    }
    
    if(!arguments.length) callback = undefined;
  
    callback = (callback || noop).bind(this)
  
    if (usePromise) {
      var self = this;
  
      var promise = new Promise(function(resolve, reject) {
  
        function callback2(error, values) {
          resolve(values)
        }
  
       self._invoke(transaction, 'getChildren', function(error, result) {
          if (error) return callback(error);
  
          self.emit('_describe', result, callback2);
        })
      });
      return promise;
    } else {
      return disguise(this._invoke(transaction, 'getChildren', function(error, result) {
        if (error) return callback(error);
  
        this.emit('_describe', result, callback);
      }), this)
    }
  };
  /**
   * @callback module:core/abstracts.MediaObject~getChildrenCallback
   * @param {external:Error} error
   * @param {module:core/abstracts.MediaObject} result
   */
  
  /**
   * @deprecated
   *  (Use children instead) children of this <code>MediaObject</code>.
   *
   * @alias module:core/abstracts.MediaObject#getChilds
   *
   * @param {module:core/abstracts.MediaObject~getChildsCallback} [callback]
   *
   * @return {external:Promise}
   */
  MediaObject.prototype.getChilds = function(callback){
    var transaction = (arguments[0] instanceof Transaction)
                    ? Array.prototype.shift.apply(arguments)
                    : undefined;
  
    var usePromise = false;
    
    if (callback == undefined) {
      usePromise = true;
    }
    
    if(!arguments.length) callback = undefined;
  
    callback = (callback || noop).bind(this)
  
    if (usePromise) {
      var self = this;
  
      var promise = new Promise(function(resolve, reject) {
  
        function callback2(error, values) {
          resolve(values)
        }
  
       self._invoke(transaction, 'getChilds', function(error, result) {
          if (error) return callback(error);
  
          self.emit('_describe', result, callback2);
        })
      });
      return promise;
    } else {
      return disguise(this._invoke(transaction, 'getChilds', function(error, result) {
        if (error) return callback(error);
  
        this.emit('_describe', result, callback);
      }), this)
    }
  };
  /**
   * @callback module:core/abstracts.MediaObject~getChildsCallback
   * @param {external:Error} error
   * @param {module:core/abstracts.MediaObject} result
   */
  
  /**
   * <code>MediaObject</code> creation time in seconds since Epoch.
   *
   * @alias module:core/abstracts.MediaObject#getCreationTime
   *
   * @param {module:core/abstracts.MediaObject~getCreationTimeCallback} [callback]
   *
   * @return {external:Promise}
   */
  MediaObject.prototype.getCreationTime = function(callback){
    var transaction = (arguments[0] instanceof Transaction)
                    ? Array.prototype.shift.apply(arguments)
                    : undefined;
  
    var usePromise = false;
    
    if (callback == undefined) {
      usePromise = true;
    }
    
    if(!arguments.length) callback = undefined;
  
    callback = (callback || noop).bind(this)
  
    return disguise(this._invoke(transaction, 'getCreationTime', callback), this)
  };
  /**
   * @callback module:core/abstracts.MediaObject~getCreationTimeCallback
   * @param {external:Error} error
   * @param {external:Integer} result
   */
  
  /**
   * {@link module:core.MediaPipeline MediaPipeline} to which this 
   * <code>MediaObject</code> belongs. It returns itself when invoked for a 
   * pipeline object.
   *
   * @alias module:core/abstracts.MediaObject#getMediaPipeline
   *
   * @param {module:core/abstracts.MediaObject~getMediaPipelineCallback} [callback]
   *
   * @return {external:Promise}
   */
  MediaObject.prototype.getMediaPipeline = function(callback){
    var transaction = (arguments[0] instanceof Transaction)
                    ? Array.prototype.shift.apply(arguments)
                    : undefined;
  
    var usePromise = false;
    
    if (callback == undefined) {
      usePromise = true;
    }
    
    if(!arguments.length) callback = undefined;
  
    callback = (callback || noop).bind(this)
  
    if (usePromise) {
      var self = this;
  
      var promise = new Promise(function(resolve, reject) {
  
        function callback2(error, values) {
          resolve(values)
        }
  
       self._invoke(transaction, 'getMediaPipeline', function(error, result) {
          if (error) return callback(error);
  
          self.emit('_describe', result, callback2);
        })
      });
      return promise;
    } else {
      return disguise(this._invoke(transaction, 'getMediaPipeline', function(error, result) {
        if (error) return callback(error);
  
        this.emit('_describe', result, callback);
      }), this)
    }
  };
  /**
   * @callback module:core/abstracts.MediaObject~getMediaPipelineCallback
   * @param {external:Error} error
   * @param {module:core.MediaPipeline} result
   */
  
  /**
   * this <code>MediaObject</code>'s name. This is just a comodity to simplify 
   * developers' life debugging, it is not used internally for indexing nor 
   * idenfiying the objects. By default, it's the object's ID.
   *
   * @alias module:core/abstracts.MediaObject#getName
   *
   * @param {module:core/abstracts.MediaObject~getNameCallback} [callback]
   *
   * @return {external:Promise}
   */
  MediaObject.prototype.getName = function(callback){
    var transaction = (arguments[0] instanceof Transaction)
                    ? Array.prototype.shift.apply(arguments)
                    : undefined;
  
    var usePromise = false;
    
    if (callback == undefined) {
      usePromise = true;
    }
    
    if(!arguments.length) callback = undefined;
  
    callback = (callback || noop).bind(this)
  
    return disguise(this._invoke(transaction, 'getName', callback), this)
  };
  /**
   * @callback module:core/abstracts.MediaObject~getNameCallback
   * @param {external:Error} error
   * @param {external:String} result
   */
  
  /**
   * this <code>MediaObject</code>'s name. This is just a comodity to simplify 
   * developers' life debugging, it is not used internally for indexing nor 
   * idenfiying the objects. By default, it's the object's ID.
   *
   * @alias module:core/abstracts.MediaObject#setName
   *
   * @param {external:String} name
   * @param {module:core/abstracts.MediaObject~setNameCallback} [callback]
   *
   * @return {external:Promise}
   */
  MediaObject.prototype.setName = function(name, callback){
    var transaction = (arguments[0] instanceof Transaction)
                    ? Array.prototype.shift.apply(arguments)
                    : undefined;
  
    //  
    // checkType('String', 'name', name, {required: true});
    //  
  
    var params = {
      name: name
    };
  
    callback = (callback || noop).bind(this)
  
    return disguise(this._invoke(transaction, 'setName', params, callback), this)
  };
  /**
   * @callback module:core/abstracts.MediaObject~setNameCallback
   * @param {external:Error} error
   */
  
  /**
   * parent of this <code>MediaObject</code>. The parent of a {@link 
   * module:core/abstracts.Hub Hub} or a {@link module:core/abstracts.MediaElement
   *
   * @alias module:core/abstracts.MediaObject#getParent
   *
   * @param {module:core/abstracts.MediaObject~getParentCallback} [callback]
   *
   * @return {external:Promise}
   */
  MediaObject.prototype.getParent = function(callback){
    var transaction = (arguments[0] instanceof Transaction)
                    ? Array.prototype.shift.apply(arguments)
                    : undefined;
  
    var usePromise = false;
    
    if (callback == undefined) {
      usePromise = true;
    }
    
    if(!arguments.length) callback = undefined;
  
    callback = (callback || noop).bind(this)
  
    if (usePromise) {
      var self = this;
  
      var promise = new Promise(function(resolve, reject) {
  
        function callback2(error, values) {
          resolve(values)
        }
  
       self._invoke(transaction, 'getParent', function(error, result) {
          if (error) return callback(error);
  
          self.emit('_describe', result, callback2);
        })
      });
      return promise;
    } else {
      return disguise(this._invoke(transaction, 'getParent', function(error, result) {
        if (error) return callback(error);
  
        this.emit('_describe', result, callback);
      }), this)
    }
  };
  /**
   * @callback module:core/abstracts.MediaObject~getParentCallback
   * @param {external:Error} error
   * @param {module:core/abstracts.MediaObject} result
   */
  
  /**
   * flag activating or deactivating sending the element's tags in fired events.
   *
   * @alias module:core/abstracts.MediaObject#getSendTagsInEvents
   *
   * @param {module:core/abstracts.MediaObject~getSendTagsInEventsCallback} [callback]
   *
   * @return {external:Promise}
   */
  MediaObject.prototype.getSendTagsInEvents = function(callback){
    var transaction = (arguments[0] instanceof Transaction)
                    ? Array.prototype.shift.apply(arguments)
                    : undefined;
  
    var usePromise = false;
    
    if (callback == undefined) {
      usePromise = true;
    }
    
    if(!arguments.length) callback = undefined;
  
    callback = (callback || noop).bind(this)
  
    return disguise(this._invoke(transaction, 'getSendTagsInEvents', callback), this)
  };
  /**
   * @callback module:core/abstracts.MediaObject~getSendTagsInEventsCallback
   * @param {external:Error} error
   * @param {external:Boolean} result
   */
  
  /**
   * flag activating or deactivating sending the element's tags in fired events.
   *
   * @alias module:core/abstracts.MediaObject#setSendTagsInEvents
   *
   * @param {external:Boolean} sendTagsInEvents
   * @param {module:core/abstracts.MediaObject~setSendTagsInEventsCallback} [callback]
   *
   * @return {external:Promise}
   */
  MediaObject.prototype.setSendTagsInEvents = function(sendTagsInEvents, callback){
    var transaction = (arguments[0] instanceof Transaction)
                    ? Array.prototype.shift.apply(arguments)
                    : undefined;
  
    //  
    // checkType('boolean', 'sendTagsInEvents', sendTagsInEvents, {required: true});
    //  
  
    var params = {
      sendTagsInEvents: sendTagsInEvents
    };
  
    callback = (callback || noop).bind(this)
  
    return disguise(this._invoke(transaction, 'setSendTagsInEvents', params, callback), this)
  };
  /**
   * @callback module:core/abstracts.MediaObject~setSendTagsInEventsCallback
   * @param {external:Error} error
   */
  
  
  //
  // Public methods
  //
  
  /**
   * Adds a new tag to this <code>MediaObject</code>. If the tag is already 
   * present, it changes the value.
   *
   * @alias module:core/abstracts.MediaObject.addTag
   *
   * @param {external:String} key
   *  Tag name.
   *
   * @param {external:String} value
   *  Value associated to this tag.
   *
   * @param {module:core/abstracts.MediaObject~addTagCallback} [callback]
   *
   * @return {external:Promise}
   */
  MediaObject.prototype.addTag = function(key, value, callback){
    var transaction = (arguments[0] instanceof Transaction)
                    ? Array.prototype.shift.apply(arguments)
                    : undefined;
  
    //  
    // checkType('String', 'key', key, {required: true});
    //  
    // checkType('String', 'value', value, {required: true});
    //  
  
    var params = {
      key: key,
      value: value
    };
  
    callback = (callback || noop).bind(this)
  
    return disguise(this._invoke(transaction, 'addTag', params, callback), this)
  };
  /**
   * @callback module:core/abstracts.MediaObject~addTagCallback
   * @param {external:Error} error
   */
  
  /**
   * Returns the value of given tag, or MEDIA_OBJECT_TAG_KEY_NOT_FOUND if tag is 
   * not defined.
   *
   * @alias module:core/abstracts.MediaObject.getTag
   *
   * @param {external:String} key
   *  Tag key.
   *
   * @param {module:core/abstracts.MediaObject~getTagCallback} [callback]
   *
   * @return {external:Promise}
   */
  MediaObject.prototype.getTag = function(key, callback){
    var transaction = (arguments[0] instanceof Transaction)
                    ? Array.prototype.shift.apply(arguments)
                    : undefined;
  
    //  
    // checkType('String', 'key', key, {required: true});
    //  
  
    var params = {
      key: key
    };
  
    callback = (callback || noop).bind(this)
  
    return disguise(this._invoke(transaction, 'getTag', params, callback), this)
  };
  /**
   * @callback module:core/abstracts.MediaObject~getTagCallback
   * @param {external:Error} error
   * @param {external:String} result
   *  The value associated to the given key.
   */
  
  /**
   * Returns all tags attached to this <code>MediaObject</code>.
   *
   * @alias module:core/abstracts.MediaObject.getTags
   *
   * @param {module:core/abstracts.MediaObject~getTagsCallback} [callback]
   *
   * @return {external:Promise}
   */
  MediaObject.prototype.getTags = function(callback){
    var transaction = (arguments[0] instanceof Transaction)
                    ? Array.prototype.shift.apply(arguments)
                    : undefined;
  
    var usePromise = false;
    
    if (callback == undefined) {
      usePromise = true;
    }
    
    if(!arguments.length) callback = undefined;
  
    callback = (callback || noop).bind(this)
  
    return disguise(this._invoke(transaction, 'getTags', callback), this)
  };
  /**
   * @callback module:core/abstracts.MediaObject~getTagsCallback
   * @param {external:Error} error
   * @param {module:core/complexTypes.Tag} result
   *  An array containing all key-value pairs associated with this 
   *  <code>MediaObject</code>.
   */
  
  /**
   * Removes an existing tag. Exists silently with no error if tag is not defined.
   *
   * @alias module:core/abstracts.MediaObject.removeTag
   *
   * @param {external:String} key
   *  Tag name to be removed
   *
   * @param {module:core/abstracts.MediaObject~removeTagCallback} [callback]
   *
   * @return {external:Promise}
   */
  MediaObject.prototype.removeTag = function(key, callback){
    var transaction = (arguments[0] instanceof Transaction)
                    ? Array.prototype.shift.apply(arguments)
                    : undefined;
  
    //  
    // checkType('String', 'key', key, {required: true});
    //  
  
    var params = {
      key: key
    };
  
    callback = (callback || noop).bind(this)
  
    return disguise(this._invoke(transaction, 'removeTag', params, callback), this)
  };
  /**
   * @callback module:core/abstracts.MediaObject~removeTagCallback
   * @param {external:Error} error
   */
  
  
  function throwRpcNotReady()
  {
    throw new SyntaxError('RPC result is not ready, use .then() method instead');
  };
  
  /**
   * Send a command to a media object
   *
   * @param {external:String} method - Command to be executed by the server
   * @param {module:core/abstract.MediaObject.constructorParams} [params]
   * @param {module:core/abstract.MediaObject~invokeCallback} callback
   *
   * @return {external:Promise}
   */
  Object.defineProperty(MediaObject.prototype, '_invoke',
  {
    enumerable: true,
    value: function(transaction, method, params, callback){
      var self = this;
  
      // Fix optional parameters
      if(params instanceof Function)
      {
        if(callback)
          throw new SyntaxError("Nothing can be defined after the callback");
  
        callback = params;
        params = undefined;
      };
  
      var promise;
      var error = this._createError;
      if(error)
        promise = Promise.reject(error)
      else
      {
        promise = new Promise(function(resolve, reject)
        {
          // Generate request parameters
          var params2 =
          {
            object: self,
            operation: method
          };
  
          if(params)
            params2.operationParams = params;
  
          function callback(error, result)
          {
            if(error) return reject(error);
  
            var value = result.value;
            if(value === undefined)
              value = self
  
            resolve(value);
          }
  
          // Do request
          self.emit('_rpc', transaction, 'invoke', params2, callback);
        });
      }
  
      return promiseCallback(promise, callback, this)
    }
  })
  /**
   * @callback core/abstract.MediaObject~invokeCallback
   * @param {external:Error} error
   */
  
  /**
   * Explicity release a {@link module:core/abstract.MediaObject MediaObject} from memory
   *
   * All its descendants will be also released and collected
   *
   * @param {module:core/abstract.MediaObject~releaseCallback} callback
   *
   * @return {external:Promise}
   */
  MediaObject.prototype.release = function(callback){
    var transaction = (arguments[0] instanceof Transaction)
                    ? Array.prototype.shift.apply(arguments)
                    : undefined;
  
    var usePromise = false;
    
    if (callback == undefined) {
      usePromise = true;
    }
    
    if(!arguments.length) callback = undefined;
  
    callback = (callback || noop).bind(this)
  
    var self = this;
  
    var promise;
    var error = this._createError;
    if(error)
      promise = Promise.reject(error)
    else
      promise = new Promise(function(resolve, reject)
      {
        var params =
        {
          object: self
        };
  
        function callback(error)
        {
          if(error) return reject(error);
  
          // Object was sucessfully released on the server,
          // remove it from cache and all its events
          Object.keys(self._events).forEach(function(event)
          {
            if(event[0] == '_'
            || event == 'newListener'
            || event == 'removeListener')
              return;
  
            self.removeAllListeners(event);
          })
          self.emit('release');
  
          resolve();
        }
  
        self.emit('_rpc', transaction, 'release', params, callback);
      });
  
    return disguise(promiseCallback(promise, callback), this)
  };
  /**
   * @callback core/abstract.MediaObject~releaseCallback
   * @param {external:Error} error
   */
  
  
  // Promise interface ("thenable")
  
  MediaObject.prototype.then = function(onFulfilled, onRejected){
    if(this.id != null)
      var promise = Promise.resolve(disguise.unthenable(this))
    else if(this.id === null)
      var promise = Promise.reject()
    else {
      var self = this
  
      var promise = new Promise(function(resolve, reject) {
        return self.once('_id', function(error, id) {
          if(error) return reject(error);
  
          resolve(disguise.unthenable(self));
        })
      })
    }
  
    promise = promise.then(onFulfilled ? onFulfilled.bind(this) :
  function(result){return Promise.resolve(result)},
                           onRejected  ? onRejected .bind(this) :
  function(error) {return Promise.reject(error)});
  
    return disguise(promise, this);
  }
  
  MediaObject.prototype.catch = function(onRejected)
  {
    this.then(null, onRejected);
  }
  
  Object.defineProperty(MediaObject.prototype, 'commited',
  {
    get: function(){return this.id !== undefined;}
  });
  
  
  /**
   * @alias module:core/abstracts.MediaObject.constructorParams
   */
  MediaObject.constructorParams = {
  };
  
  /**
   * @alias module:core/abstracts.MediaObject.events
   */
  MediaObject.events = ['Error'];
  
  
  /**
   * Checker for {@link module:core/abstracts.MediaObject}
   *
   * @memberof module:core/abstracts
   *
   * @param {external:String} key
   * @param {module:core/abstracts.MediaObject} value
   */
  function checkMediaObject(key, value)
  {
    if(!(value instanceof MediaObject))
      throw ChecktypeError(key, MediaObject, value);
  };
  
  
  module.exports = MediaObject;
  
  MediaObject.check = checkMediaObject;
  