// Filename: parasiteBuffer.cxx
// Created by:  drose (27Feb04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University.  All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license.  You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////

#include "parasiteBuffer.h"
#include "texture.h"

TypeHandle ParasiteBuffer::_type_handle;

////////////////////////////////////////////////////////////////////
//     Function: ParasiteBuffer::Constructor
//       Access: Public
//  Description: Normally, the ParasiteBuffer constructor is not
//               called directly; these are created instead via the
//               GraphicsEngine::make_parasite() function.
////////////////////////////////////////////////////////////////////
ParasiteBuffer::
ParasiteBuffer(GraphicsOutput *host, const string &name,
               int x_size, int y_size, int flags) :
  GraphicsOutput(host->get_engine(), host->get_pipe(), 
                 name, host->get_fb_properties(),
                 WindowProperties::size(x_size, y_size), flags, 
                 host->get_gsg(), host, false)
{
#ifdef DO_MEMORY_USAGE
  MemoryUsage::update_type(this, this);
#endif
  
  if (display_cat.is_debug()) {
    display_cat.debug()
      << "Creating new parasite buffer " << get_name()
      << " on " << _host->get_name() << "\n";
  }

  _creation_flags = flags;
  
  if (flags & GraphicsPipe::BF_size_track_host) {
    x_size = host->get_x_size();
    y_size = host->get_y_size();
  }
  
  _x_size = x_size;
  _y_size = y_size;
  _has_size = true;
  _overlay_display_region->compute_pixels(_x_size, _y_size);
  _is_valid = true;
  
  set_inverted(host->get_gsg()->get_copy_texture_inverted());
}

////////////////////////////////////////////////////////////////////
//     Function: ParasiteBuffer::Destructor
//       Access: Published, Virtual
//  Description: 
////////////////////////////////////////////////////////////////////
ParasiteBuffer::
~ParasiteBuffer() {
  _is_valid = false;
}

////////////////////////////////////////////////////////////////////
//     Function: ParasiteBuffer::is_active
//       Access: Published, Virtual
//  Description: Returns true if the window is ready to be rendered
//               into, false otherwise.
////////////////////////////////////////////////////////////////////
bool ParasiteBuffer::
is_active() const {
  return GraphicsOutput::is_active() && _host->is_active();
}

////////////////////////////////////////////////////////////////////
//     Function: ParasiteBuffer::set_size
//       Access: Public, Virtual
//  Description: This is called by the GraphicsEngine to request that
//               the buffer resize itself.  Although calls to get the
//               size will return the new value, much of the actual
//               resizing work doesn't take place until the next
//               begin_frame.  Not all buffers are resizeable.
////////////////////////////////////////////////////////////////////
void ParasiteBuffer::
set_size(int x, int y) {
  if ((_creation_flags & GraphicsPipe::BF_resizeable) == 0) {
    nassert_raise("Cannot resize buffer unless it is created with BF_resizeable flag");
    return;
  }
  set_size_and_recalc(x, y);
}

////////////////////////////////////////////////////////////////////
//     Function: ParasiteBuffer::set_size_and_recalc
//       Access: Public
//  Description: 
////////////////////////////////////////////////////////////////////
void ParasiteBuffer::
set_size_and_recalc(int x, int y) {
  if (!(_creation_flags & GraphicsPipe::BF_size_track_host)) {
    if (_creation_flags & GraphicsPipe::BF_size_power_2) {
      x = Texture::down_to_power_2(x);
      y = Texture::down_to_power_2(y);
    }
    if (_creation_flags & GraphicsPipe::BF_size_square) {
      x = y = min(x, y);
    }
  }

  GraphicsOutput::set_size_and_recalc(x, y);
}

////////////////////////////////////////////////////////////////////
//     Function: ParasiteBuffer::flip_ready
//       Access: Public, Virtual
//  Description: Returns true if a frame has been rendered and needs
//               to be flipped, false otherwise.
////////////////////////////////////////////////////////////////////
bool ParasiteBuffer::
flip_ready() const {
  nassertr(_host != NULL, false);
  return _host->flip_ready();
}

////////////////////////////////////////////////////////////////////
//     Function: ParasiteBuffer::begin_flip
//       Access: Public, Virtual
//  Description: This function will be called within the draw thread
//               after end_frame() has been called on all windows, to
//               initiate the exchange of the front and back buffers.
//
//               This should instruct the window to prepare for the
//               flip at the next video sync, but it should not wait.
//
//               We have the two separate functions, begin_flip() and
//               end_flip(), to make it easier to flip all of the
//               windows at the same time.
////////////////////////////////////////////////////////////////////
void ParasiteBuffer::
begin_flip() {
  nassertv(_host != NULL);
  _host->begin_flip();
}

////////////////////////////////////////////////////////////////////
//     Function: ParasiteBuffer::ready_flip
//       Access: Public, Virtual
//  Description: This function will be called within the draw thread
//               after end_frame() has been called on all windows, to
//               initiate the exchange of the front and back buffers.
//
//               This should instruct the window to prepare for the
//               flip when it is command but not actually flip
//
////////////////////////////////////////////////////////////////////
void ParasiteBuffer::
ready_flip() {
  nassertv(_host != NULL);
  _host->ready_flip();
}

////////////////////////////////////////////////////////////////////
//     Function: ParasiteBuffer::end_flip
//       Access: Public, Virtual
//  Description: This function will be called within the draw thread
//               after begin_flip() has been called on all windows, to
//               finish the exchange of the front and back buffers.
//
//               This should cause the window to wait for the flip, if
//               necessary.
////////////////////////////////////////////////////////////////////
void ParasiteBuffer::
end_flip() {
  nassertv(_host != NULL);
  _host->end_flip();
  _flip_ready = false;
}

////////////////////////////////////////////////////////////////////
//     Function: ParasiteBuffer::begin_frame
//       Access: Public, Virtual
//  Description: This function will be called within the draw thread
//               before beginning rendering for a given frame.  It
//               should do whatever setup is required, and return true
//               if the frame should be rendered, or false if it
//               should be skipped.
////////////////////////////////////////////////////////////////////
bool ParasiteBuffer::
begin_frame(FrameMode mode, Thread *current_thread) {
  begin_frame_spam(mode);

  if (!_host->begin_frame(FM_parasite, current_thread)) {
    return false;
  }

  if (_creation_flags & GraphicsPipe::BF_size_track_host) {
    if ((_host->get_x_size() != _x_size)||
        (_host->get_y_size() != _y_size)) {
      set_size_and_recalc(_host->get_x_size(),
                          _host->get_y_size());
    }
  } else {
    if (_host->get_x_size() < _x_size ||
        _host->get_y_size() < _y_size) {
      set_size_and_recalc(min(_x_size, _host->get_x_size()),
                          min(_y_size, _host->get_y_size()));
    }
  }
  
  clear_cube_map_selection();
  return true;
}

////////////////////////////////////////////////////////////////////
//     Function: ParasiteBuffer::end_frame
//       Access: Public, Virtual
//  Description: This function will be called within the draw thread
//               after rendering is completed for a given frame.  It
//               should do whatever finalization is required.
////////////////////////////////////////////////////////////////////
void ParasiteBuffer::
end_frame(FrameMode mode, Thread *current_thread) {
  end_frame_spam(mode);

  nassertv(_gsg != (GraphicsStateGuardian *)NULL);

  _host->end_frame(FM_parasite, current_thread);

  if (mode == FM_refresh) {
    return;
  }
  
  if (mode == FM_render) {
    promote_to_copy_texture();
    copy_to_textures();
    clear_cube_map_selection();
  }
}

////////////////////////////////////////////////////////////////////
//     Function: ParasiteBuffer::get_host
//       Access: Public, Virtual
//  Description: This is normally called only from within
//               make_texture_buffer().  When called on a
//               ParasiteBuffer, it returns the host of that buffer;
//               but when called on some other buffer, it returns the
//               buffer itself.
////////////////////////////////////////////////////////////////////
GraphicsOutput *ParasiteBuffer::
get_host() {
  return _host;
}

