// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "webkit/glue/plugins/mac_accelerated_surface_container.h"

#include "app/surface/io_surface_support_mac.h"
#include "base/logging.h"
#include "webkit/glue/plugins/mac_accelerated_surface_container_manager.h"
#include "webkit/glue/plugins/webplugin.h"

MacAcceleratedSurfaceContainer::MacAcceleratedSurfaceContainer()
    : x_(0),
      y_(0),
      surface_(NULL),
      width_(0),
      height_(0),
      texture_(0) {
}

MacAcceleratedSurfaceContainer::~MacAcceleratedSurfaceContainer() {
  ReleaseIOSurface();
}

void MacAcceleratedSurfaceContainer::ReleaseIOSurface() {
  if (surface_) {
    CFRelease(surface_);
    surface_ = NULL;
  }
}

void MacAcceleratedSurfaceContainer::SetSizeAndIOSurface(
    int32 width,
    int32 height,
    uint64 io_surface_identifier,
    MacAcceleratedSurfaceContainerManager* manager) {
  ReleaseIOSurface();
  IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize();
  if (io_surface_support) {
    surface_ = io_surface_support->IOSurfaceLookup(
        static_cast<uint32>(io_surface_identifier));
    EnqueueTextureForDeletion(manager);
    width_ = width;
    height_ = height;
  }
}

void MacAcceleratedSurfaceContainer::SetSizeAndTransportDIB(
    int32 width,
    int32 height,
    TransportDIB::Handle transport_dib,
    MacAcceleratedSurfaceContainerManager* manager) {
  if (TransportDIB::is_valid(transport_dib)) {
    transport_dib_.reset(TransportDIB::Map(transport_dib));
    EnqueueTextureForDeletion(manager);
    width_ = width;
    height_ = height;
  }
}

void MacAcceleratedSurfaceContainer::MoveTo(
    const webkit_glue::WebPluginGeometry& geom) {
  x_ = geom.window_rect.x();
  y_ = geom.window_rect.y();
  // TODO(kbr): may need to pay attention to cutout rects.
  clipRect_ = geom.clip_rect;
}

void MacAcceleratedSurfaceContainer::Draw(CGLContextObj context) {
  IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize();
  GLenum target = GL_TEXTURE_RECTANGLE_ARB;
  if (!texture_) {
    if ((io_surface_support && !surface_) ||
        (!io_surface_support && !transport_dib_.get()))
      return;
    glGenTextures(1, &texture_);
    glBindTexture(target, texture_);
    glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    // When using an IOSurface, the texture does not need to be repeatedly
    // uploaded, so bind the IOSurface once during texture gen in this case.
    if (io_surface_support) {
      DCHECK(surface_);
      // Don't think we need to identify a plane.
      GLuint plane = 0;
      io_surface_support->CGLTexImageIOSurface2D(context,
                                                 target,
                                                 GL_RGBA,
                                                 width_,
                                                 height_,
                                                 GL_BGRA,
                                                 GL_UNSIGNED_INT_8_8_8_8_REV,
                                                 surface_,
                                                 plane);
    } else {
      // Reserve space on the card for the actual texture upload, done with the
      // glTexSubImage2D() call, below.
      glTexImage2D(target,
                   0,  // mipmap level 0
                   GL_RGBA,  // internal format
                   width_,
                   height_,
                   0,  // no border
                   GL_BGRA,  // The GPU plugin read BGRA pixels
                   GL_UNSIGNED_INT_8_8_8_8_REV,
                   NULL);  // No data, this call just reserves room.
    }
  }

  // If using TransportDIBs, the texture needs to be uploaded every frame.
  if (transport_dib_.get() != NULL) {
    void* pixel_memory = transport_dib_->memory();
    if (pixel_memory) {
      glBindTexture(target, texture_);
      glPixelStorei(GL_UNPACK_ALIGNMENT, 1);  // Needed for NPOT textures.
      glTexSubImage2D(target,
                      0,  // mipmap level 0
                      0,  // x-offset
                      0,  // y-offset
                      width_,
                      height_,
                      GL_BGRA,  // The GPU plugin gave us BGRA pixels
                      GL_UNSIGNED_INT_8_8_8_8_REV,
                      pixel_memory);
    }
  }

  if (texture_) {
    // TODO(kbr): convert this to use only OpenGL ES 2.0 functionality
    glBindTexture(target, texture_);
    glEnable(target);
    glBegin(GL_TRIANGLE_STRIP);
    // TODO(kbr): may need to pay attention to cutout rects.
    int clipX = clipRect_.x();
    int clipY = clipRect_.y();
    int clipWidth = clipRect_.width();
    int clipHeight = clipRect_.height();
    int x = x_ + clipX;
    int y = y_ + clipY;
    glTexCoord2f(clipX, height_ - clipY);
    glVertex3f(x, y, 0);
    glTexCoord2f(clipX + clipWidth, height_ - clipY);
    glVertex3f(x + clipWidth, y, 0);
    glTexCoord2f(clipX, height_ - clipY - clipHeight);
    glVertex3f(x, y + clipHeight, 0);
    glTexCoord2f(clipX + clipWidth, height_ - clipY - clipHeight);
    glVertex3f(x + clipWidth, y + clipHeight, 0);
    glEnd();
    glDisable(target);
  }
}

void MacAcceleratedSurfaceContainer::EnqueueTextureForDeletion(
    MacAcceleratedSurfaceContainerManager* manager) {
  manager->EnqueueTextureForDeletion(texture_);
  texture_ = 0;
}