text: /*global define*/
  define([
          '../Core/defaultValue',
          '../Core/defined',
          '../Core/defineProperties',
          '../Core/destroyObject',
          '../Core/DeveloperError',
          '../Core/PixelFormat',
          './ContextLimits'
      ], function(
          defaultValue,
          defined,
          defineProperties,
          destroyObject,
          DeveloperError,
          PixelFormat,
          ContextLimits) {
      "use strict";
  
      function attachTexture(framebuffer, attachment, texture) {
          var gl = framebuffer._gl;
          gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, texture._target, texture._texture, 0);
      }
  
      function attachRenderbuffer(framebuffer, attachment, renderbuffer) {
          var gl = framebuffer._gl;
          gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, renderbuffer._getRenderbuffer());
      }
  
      /**
       * Creates a framebuffer with optional initial color, depth, and stencil attachments.
       * Framebuffers are used for render-to-texture effects; they allow us to render to
       * textures in one pass, and read from it in a later pass.
       *
       * @param {Object} options The initial framebuffer attachments as shown in the example below. <code>context</code> is required. The possible properties are <code>colorTextures</code>, <code>colorRenderbuffers</code>, <code>depthTexture</code>, <code>depthRenderbuffer</code>, <code>stencilRenderbuffer</code>, <code>depthStencilTexture</code>, and <code>depthStencilRenderbuffer</code>.
       *
       * @exception {DeveloperError} Cannot have both color texture and color renderbuffer attachments.
       * @exception {DeveloperError} Cannot have both a depth texture and depth renderbuffer attachment.
       * @exception {DeveloperError} Cannot have both a depth-stencil texture and depth-stencil renderbuffer attachment.
       * @exception {DeveloperError} Cannot have both a depth and depth-stencil renderbuffer.
       * @exception {DeveloperError} Cannot have both a stencil and depth-stencil renderbuffer.
       * @exception {DeveloperError} Cannot have both a depth and stencil renderbuffer.
       * @exception {DeveloperError} The color-texture pixel-format must be a color format.
       * @exception {DeveloperError} The depth-texture pixel-format must be DEPTH_COMPONENT.
       * @exception {DeveloperError} The depth-stencil-texture pixel-format must be DEPTH_STENCIL.
       * @exception {DeveloperError} The number of color attachments exceeds the number supported.
       *
       * @example
       * // Create a framebuffer with color and depth texture attachments.
       * var width = context.canvas.clientWidth;
       * var height = context.canvas.clientHeight;
       * var framebuffer = new Framebuffer({
       *   context : context,
       *   colorTextures : [new Texture({
       *     context : context,
       *     width : width,
       *     height : height,
       *     pixelFormat : PixelFormat.RGBA
       *   })],
       *   depthTexture : new Texture({
       *     context : context,
       *     width : width,
       *     height : height,
       *     pixelFormat : PixelFormat.DEPTH_COMPONENT,
       *     pixelDatatype : PixelDatatype.UNSIGNED_SHORT
       *   })
       * });
       *
       * @private
       */
      var Framebuffer = function(options) {
          options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  
          //>>includeStart('debug', pragmas.debug);
          if (!defined(options.context)) {
              throw new DeveloperError('options.context is required.');
          }
          //>>includeEnd('debug');
  
          var gl = options.context._gl;
          var maximumColorAttachments = ContextLimits.maximumColorAttachments;
  
          this._gl = gl;
          this._framebuffer = gl.createFramebuffer();
  
          this._colorTextures = [];
          this._colorRenderbuffers = [];
          this._activeColorAttachments = [];
  
          this._depthTexture = undefined;
          this._depthRenderbuffer = undefined;
          this._stencilRenderbuffer = undefined;
          this._depthStencilTexture = undefined;
          this._depthStencilRenderbuffer = undefined;
  
          /**
           * When true, the framebuffer owns its attachments so they will be destroyed when
           * {@link Framebuffer#destroy} is called or when a new attachment is assigned
           * to an attachment point.
           *
           * @type {Boolean}
           * @default true
           *
           * @see Framebuffer#destroy
           */
          this.destroyAttachments = defaultValue(options.destroyAttachments, true);
  
          // Throw if a texture and renderbuffer are attached to the same point.  This won't
          // cause a WebGL error (because only one will be attached), but is likely a developer error.
  
          //>>includeStart('debug', pragmas.debug);
          if (defined(options.colorTextures) && defined(options.colorRenderbuffers)) {
              throw new DeveloperError('Cannot have both color texture and color renderbuffer attachments.');
          }
          if (defined(options.depthTexture) && defined(options.depthRenderbuffer)) {
              throw new DeveloperError('Cannot have both a depth texture and depth renderbuffer attachment.');
          }
          if (defined(options.depthStencilTexture) && defined(options.depthStencilRenderbuffer)) {
              throw new DeveloperError('Cannot have both a depth-stencil texture and depth-stencil renderbuffer attachment.');
          }
          //>>includeEnd('debug');
  
          // Avoid errors defined in Section 6.5 of the WebGL spec
          var depthAttachment = (defined(options.depthTexture) || defined(options.depthRenderbuffer));
          var depthStencilAttachment = (defined(options.depthStencilTexture) || defined(options.depthStencilRenderbuffer));
  
          //>>includeStart('debug', pragmas.debug);
          if (depthAttachment && depthStencilAttachment) {
              throw new DeveloperError('Cannot have both a depth and depth-stencil attachment.');
          }
          if (defined(options.stencilRenderbuffer) && depthStencilAttachment) {
              throw new DeveloperError('Cannot have both a stencil and depth-stencil attachment.');
          }
          if (depthAttachment && defined(options.stencilRenderbuffer)) {
              throw new DeveloperError('Cannot have both a depth and stencil attachment.');
          }
          //>>includeEnd('debug');
  
          ///////////////////////////////////////////////////////////////////
  
          this._bind();
  
          var texture;
          var renderbuffer;
          var i;
          var length;
          var attachmentEnum;
  
          if (defined(options.colorTextures)) {
              var textures = options.colorTextures;
              length = this._colorTextures.length = this._activeColorAttachments.length = textures.length;
  
              //>>includeStart('debug', pragmas.debug);
              if (length > maximumColorAttachments) {
                  throw new DeveloperError('The number of color attachments exceeds the number supported.');
              }
              //>>includeEnd('debug');
  
              for (i = 0; i < length; ++i) {
                  texture = textures[i];
  
                  //>>includeStart('debug', pragmas.debug);
                  if (!PixelFormat.isColorFormat(texture.pixelFormat)) {
                      throw new DeveloperError('The color-texture pixel-format must be a color format.');
                  }
                  //>>includeEnd('debug');
  
                  attachmentEnum = this._gl.COLOR_ATTACHMENT0 + i;
                  attachTexture(this, attachmentEnum, texture);
                  this._activeColorAttachments[i] = attachmentEnum;
                  this._colorTextures[i] = texture;
              }
          }
  
          if (defined(options.colorRenderbuffers)) {
              var renderbuffers = options.colorRenderbuffers;
              length = this._colorRenderbuffers.length = this._activeColorAttachments.length = renderbuffers.length;
  
              //>>includeStart('debug', pragmas.debug);
              if (length > maximumColorAttachments) {
                  throw new DeveloperError('The number of color attachments exceeds the number supported.');
              }
              //>>includeEnd('debug');
  
              for (i = 0; i < length; ++i) {
                  renderbuffer = renderbuffers[i];
                  attachmentEnum = this._gl.COLOR_ATTACHMENT0 + i;
                  attachRenderbuffer(this, attachmentEnum, renderbuffer);
                  this._activeColorAttachments[i] = attachmentEnum;
                  this._colorRenderbuffers[i] = renderbuffer;
              }
          }
  
          if (defined(options.depthTexture)) {
              texture = options.depthTexture;
  
              //>>includeStart('debug', pragmas.debug);
              if (texture.pixelFormat !== PixelFormat.DEPTH_COMPONENT) {
                  throw new DeveloperError('The depth-texture pixel-format must be DEPTH_COMPONENT.');
              }
              //>>includeEnd('debug');
  
              attachTexture(this, this._gl.DEPTH_ATTACHMENT, texture);
              this._depthTexture = texture;
          }
  
          if (defined(options.depthRenderbuffer)) {
              renderbuffer = options.depthRenderbuffer;
              attachRenderbuffer(this, this._gl.DEPTH_ATTACHMENT, renderbuffer);
              this._depthRenderbuffer = renderbuffer;
          }
  
          if (defined(options.stencilRenderbuffer)) {
              renderbuffer = options.stencilRenderbuffer;
              attachRenderbuffer(this, this._gl.STENCIL_ATTACHMENT, renderbuffer);
              this._stencilRenderbuffer = renderbuffer;
          }
  
          if (defined(options.depthStencilTexture)) {
              texture = options.depthStencilTexture;
  
              //>>includeStart('debug', pragmas.debug);
              if (texture.pixelFormat !== PixelFormat.DEPTH_STENCIL) {
                  throw new DeveloperError('The depth-stencil pixel-format must be DEPTH_STENCIL.');
              }
              //>>includeEnd('debug');
  
              attachTexture(this, this._gl.DEPTH_STENCIL_ATTACHMENT, texture);
              this._depthStencilTexture = texture;
          }
  
          if (defined(options.depthStencilRenderbuffer)) {
              renderbuffer = options.depthStencilRenderbuffer;
              attachRenderbuffer(this, this._gl.DEPTH_STENCIL_ATTACHMENT, renderbuffer);
              this._depthStencilRenderbuffer = renderbuffer;
          }
  
          this._unBind();
      };
  
      defineProperties(Framebuffer.prototype, {
          /**
           * The status of the framebuffer. If the status is not WebGLConstants.COMPLETE,
           * a {@link DeveloperError} will be thrown when attempting to render to the framebuffer.
           * @memberof Framebuffer.prototype
           * @type {Number}
           */
          status : {
              get : function() {
                  this._bind();
                  var status = this._gl.checkFramebufferStatus(this._gl.FRAMEBUFFER);
                  this._unBind();
                  return status;
              }
          },
          numberOfColorAttachments : {
              get : function() {
                  return this._activeColorAttachments.length;
              }
          },
          depthTexture: {
              get : function() {
                  return this._depthTexture;
              }
          },
          depthRenderbuffer: {
              get : function() {
                  return this._depthRenderbuffer;
              }
          },
          stencilRenderbuffer : {
              get : function() {
                  return this._stencilRenderbuffer;
              }
          },
          depthStencilTexture : {
              get : function() {
                  return this._depthStencilTexture;
              }
          },
          depthStencilRenderbuffer : {
              get : function() {
                  return this._depthStencilRenderbuffer;
              }
          },
  
          /**
           * True if the framebuffer has a depth attachment.  Depth attachments include
           * depth and depth-stencil textures, and depth and depth-stencil renderbuffers.  When
           * rendering to a framebuffer, a depth attachment is required for the depth test to have effect.
           * @memberof Framebuffer.prototype
           * @type {Boolean}
           */
          hasDepthAttachment : {
              get : function() {
                  return !!(this.depthTexture || this.depthRenderbuffer || this.depthStencilTexture || this.depthStencilRenderbuffer);
              }
          }
      });
  
      Framebuffer.prototype._bind = function() {
          var gl = this._gl;
          gl.bindFramebuffer(gl.FRAMEBUFFER, this._framebuffer);
      };
  
      Framebuffer.prototype._unBind = function() {
          var gl = this._gl;
          gl.bindFramebuffer(gl.FRAMEBUFFER, null);
      };
  
      Framebuffer.prototype._getActiveColorAttachments = function() {
          return this._activeColorAttachments;
      };
  
      Framebuffer.prototype.getColorTexture = function(index) {
          //>>includeStart('debug', pragmas.debug);
          if (!defined(index) || index < 0 || index >= this._colorTextures.length) {
              throw new DeveloperError('index is required, must be greater than or equal to zero and must be less than the number of color attachments.');
          }
          //>>includeEnd('debug');
  
          return this._colorTextures[index];
      };
  
      Framebuffer.prototype.getColorRenderbuffer = function(index) {
          //>>includeStart('debug', pragmas.debug);
          if (!defined(index) || index < 0 || index >= this._colorRenderbuffers.length) {
              throw new DeveloperError('index is required, must be greater than or equal to zero and must be less than the number of color attachments.');
          }
          //>>includeEnd('debug');
  
          return this._colorRenderbuffers[index];
      };
  
      Framebuffer.prototype.isDestroyed = function() {
          return false;
      };
  
      Framebuffer.prototype.destroy = function() {
          if (this.destroyAttachments) {
              // If the color texture is a cube map face, it is owned by the cube map, and will not be destroyed.
              var i = 0;
              var textures = this._colorTextures;
              var length = textures.length;
              for (; i < length; ++i) {
                  var texture = textures[i];
                  if (defined(texture)) {
                      texture.destroy();
                  }
              }
  
              var renderbuffers = this._colorRenderbuffers;
              length = renderbuffers.length;
              for (i = 0; i < length; ++i) {
                  var renderbuffer = renderbuffers[i];
                  if (defined(renderbuffer)) {
                      renderbuffer.destroy();
                  }
              }
  
              this._depthTexture = this._depthTexture && this._depthTexture.destroy();
              this._depthRenderbuffer = this._depthRenderbuffer && this._depthRenderbuffer.destroy();
              this._stencilRenderbuffer = this._stencilRenderbuffer && this._stencilRenderbuffer.destroy();
              this._depthStencilTexture = this._depthStencilTexture && this._depthStencilTexture.destroy();
              this._depthStencilRenderbuffer = this._depthStencilRenderbuffer && this._depthStencilRenderbuffer.destroy();
          }
  
          this._gl.deleteFramebuffer(this._framebuffer);
          return destroyObject(this);
      };
  
      return Framebuffer;
  });
  