text: /***********************************************************************
  	created:	21/2/2004
  	author:		Paul D Turner
  
  	purpose:	Implements the WindowManager class
  *************************************************************************/
  /***************************************************************************
   *   Copyright (C) 2004 - 2006 Paul D Turner & The CEGUI Development Team
   *
   *   Permission is hereby granted, free of charge, to any person obtaining
   *   a copy of this software and associated documentation files (the
   *   "Software"), to deal in the Software without restriction, including
   *   without limitation the rights to use, copy, modify, merge, publish,
   *   distribute, sublicense, and/or sell copies of the Software, and to
   *   permit persons to whom the Software is furnished to do so, subject to
   *   the following conditions:
   *
   *   The above copyright notice and this permission notice shall be
   *   included in all copies or substantial portions of the Software.
   *
   *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
   *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
   *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
   *   OTHER DEALINGS IN THE SOFTWARE.
   ***************************************************************************/
  #include "CEGUI/WindowManager.h"
  #include "CEGUI/WindowFactoryManager.h"
  #include "CEGUI/System.h"
  #include "CEGUI/Logger.h"
  #include "CEGUI/Window.h"
  #include "CEGUI/GUILayout_xmlHandler.h"
  #include "CEGUI/XMLParser.h"
  #include "CEGUI/XMLSerializer.h"
  #include "CEGUI/RenderEffectManager.h"
  #include "CEGUI/RenderingWindow.h"
  #include "CEGUI/SharedStringStream.h"
  #include <fstream>
  #include <algorithm>
  
  // Start of CEGUI namespace section
  namespace CEGUI
  {
  /*************************************************************************
  	Static Data Definitions
  *************************************************************************/
  // singleton instance pointer
  template<> WindowManager* Singleton<WindowManager>::ms_Singleton	= nullptr;
  // default resource group
  String WindowManager::d_defaultResourceGroup;
  
  /*************************************************************************
  	Definition of constant data for WindowManager
  *************************************************************************/
  // Declared in WindowManager
  const String WindowManager::GUILayoutSchemaName("GUILayout.xsd");
  const String WindowManager::GeneratedWindowNameBase("__cewin_uid_");
  const String WindowManager::EventNamespace("WindowManager");
  const String WindowManager::EventWindowCreated("WindowCreated");
  const String WindowManager::EventWindowDestroyed("WindowDestroyed");
      
  
  /*************************************************************************
      Constructor
  *************************************************************************/
  WindowManager::WindowManager(void) :
      d_uid_counter(0),
      d_lockCount(0)
  {
      String addressStr = SharedStringstream::GetPointerAddressAsString(this);
  
      Logger::getSingleton().logEvent(
          "CEGUI::WindowManager Singleton created. (" + addressStr + ")");
  }
  
  
  /*************************************************************************
  	Destructor
  *************************************************************************/
  WindowManager::~WindowManager(void)
  {
  	destroyAllWindows();
      cleanDeadPool();
  
      String addressStr = SharedStringstream::GetPointerAddressAsString(this);
  
      Logger::getSingleton().logEvent(
          "CEGUI::WindowManager singleton destroyed (" + addressStr + ")");
  }
  
  
  /*************************************************************************
  	Create a new window of the specified type
  *************************************************************************/
  Window* WindowManager::createWindow(const String& type, const String& name)
  {
      // only allow creation of Window objects if we are in unlocked state
      if (isLocked())
          throw InvalidRequestException(
              "WindowManager is in the locked state.");
  
      String finalName(name.empty() ? generateUniqueWindowName() : name);
  
      WindowFactoryManager& wfMgr = WindowFactoryManager::getSingleton();
      WindowFactory* factory = wfMgr.getFactory(type);
  
      Window* newWindow = factory->createWindow(finalName);
  
      String addressStr = SharedStringstream::GetPointerAddressAsString(newWindow);
      Logger::getSingleton().logEvent("Window '" + finalName +"' of type '" +
          type + "' has been created. " + addressStr, LoggingLevel::Informative);
  
      // see if we need to assign a look to this window
      if (wfMgr.isFalagardMappedType(type))
      {
          const WindowFactoryManager::FalagardWindowMapping& fwm = wfMgr.getFalagardMappingForType(type);
          // this was a mapped type, so assign a look to the window so it can finalise
          // its initialisation
          newWindow->d_falagardType = type;
          newWindow->setWindowRenderer(fwm.d_rendererType);
          newWindow->setLookNFeel(fwm.d_lookName);
  
          initialiseRenderEffect(newWindow, fwm.d_effectName);
      }
  
  	d_windowRegistry.push_back(newWindow);
  
      // fire event to notify interested parites about the new window.
      WindowEventArgs args(newWindow);
      fireEvent(EventWindowCreated, args, EventNamespace);
      
  	return newWindow;
  }
  
  //---------------------------------------------------------------------------//
  void WindowManager::initialiseRenderEffect(
          Window* wnd, const String& effect) const
  {
      Logger& logger(Logger::getSingleton());
  
      // nothing to do if effect is empty string
      if (effect.empty())
          return;
  
      // if requested RenderEffect is not available, log that and continue
      if (!RenderEffectManager::getSingleton().isEffectAvailable(effect))
      {
          logger.logEvent("Missing RenderEffect '" + effect + "' requested for "
              "window '" + wnd->getName() + "' - continuing without effect...",
              LoggingLevel::Error);
  
         return;
      }
  
      // If we do not have a RenderingSurface, enable AutoRenderingSurface to
      // try and create one
      if (!wnd->getRenderingSurface())
      {
          logger.logEvent("Enabling AutoRenderingSurface on '" +
              wnd->getName() + "' for RenderEffect support.");
  
          wnd->setUsingAutoRenderingSurface(true);
      }
  
      // If we have a RenderingSurface and it's a RenderingWindow
      if (wnd->getRenderingSurface() &&
          wnd->getRenderingSurface()->isRenderingWindow())
      {
          // Set an instance of the requested RenderEffect
          static_cast<RenderingWindow*>(wnd->getRenderingSurface())->
                  setRenderEffect(&RenderEffectManager::getSingleton().
                          create(effect, wnd));
      }
      // log fact that we could not get a usable RenderingSurface
      else
      {
          logger.logEvent("Unable to set effect for window '" +
              wnd->getName() + "' since RenderingSurface is either missing "
              "or of wrong type (i.e. not a RenderingWindow).",
              LoggingLevel::Error);
      }
  }
  
  /*************************************************************************
  	Destroy the given window by pointer
  *************************************************************************/
  void WindowManager::destroyWindow(Window* window)
  {
  	WindowVector::iterator iter =
          std::find(d_windowRegistry.begin(),
                    d_windowRegistry.end(),
                    window);
  
      String addressStr = SharedStringstream::GetPointerAddressAsString(&window);
  
  	if (iter == d_windowRegistry.end())
      {
          Logger::getSingleton().logEvent("[WindowManager] Attempt to delete "
              "Window that does not exist!  Address was: " + addressStr +
              ". WARNING: This could indicate a double-deletion issue!!",
              LoggingLevel::Error);
          return;
      }
  
      d_windowRegistry.erase(iter);
  
      Logger::getSingleton().logEvent("Window at '" + window->getNamePath() +
          "' will be added to dead pool. " + addressStr, LoggingLevel::Informative);
  
      // do 'safe' part of cleanup
      window->destroy();
  
      // add window to dead pool
      d_deathrow.push_back(window);
  
      // fire event to notify interested parites about window destruction.
      // TODO: Perhaps this should fire first, so window is still usable?
      WindowEventArgs args(window);
      fireEvent(EventWindowDestroyed, args, EventNamespace);
  }
  
  
  /*************************************************************************
  	Destroy all Window objects
  *************************************************************************/
  void WindowManager::destroyAllWindows(void)
  {
  	while (!d_windowRegistry.empty())
  		destroyWindow(*d_windowRegistry.begin());
  }
  
  //----------------------------------------------------------------------------//
  bool WindowManager::isAlive(const Window* window) const
  {
  	WindowVector::const_iterator iter =
          std::find(d_windowRegistry.begin(),
                    d_windowRegistry.end(),
                    window);
  
  	return iter != d_windowRegistry.end();
  }
  
  Window* WindowManager::loadLayoutFromContainer(const RawDataContainer& source, PropertyCallback* callback, void* userdata)
  {
      // log the fact we are about to load a layout
      Logger::getSingleton().logEvent("---- Beginning loading of GUI layout from a RawDataContainer ----", LoggingLevel::Informative);
  
      // create handler object
      GUILayout_xmlHandler handler(callback, userdata);
  
      // do parse (which uses handler to create actual data)
      try
      {
          System::getSingleton().getXMLParser()->parseXML(handler, source, GUILayoutSchemaName);
      }
      catch (...)
      {
          Logger::getSingleton().logEvent("WindowManager::loadWindowLayout - loading of layout from a RawDataContainer failed.", LoggingLevel::Error);
          throw;
      }
  
      // log the completion of loading
      Logger::getSingleton().logEvent("---- Successfully completed loading of GUI layout from a RawDataContainer ----", LoggingLevel::Standard);
  
      return handler.getLayoutRootWindow();
  }
  
  Window* WindowManager::loadLayoutFromFile(const String& filename, const String& resourceGroup, PropertyCallback* callback, void* userdata)
  {
  	if (filename.empty())
  	{
  		throw InvalidRequestException(
              "Filename supplied for gui-layout loading must be valid.");
  	}
  
  	// log the fact we are about to load a layout
  	Logger::getSingleton().logEvent("---- Beginning loading of GUI layout from '" + filename + "' ----", LoggingLevel::Informative);
  
      // create handler object
      GUILayout_xmlHandler handler(callback, userdata);
  
  	// do parse (which uses handler to create actual data)
  	try
  	{
          System::getSingleton().getXMLParser()->parseXMLFile(handler,
              filename, GUILayoutSchemaName, resourceGroup.empty() ? d_defaultResourceGroup : resourceGroup);
  	}
  	catch (...)
  	{
          Logger::getSingleton().logEvent("WindowManager::loadLayoutFromFile - loading of layout from file '" + filename +"' failed.", LoggingLevel::Error);
          throw;
  	}
  
      // log the completion of loading
      Logger::getSingleton().logEvent("---- Successfully completed loading of GUI layout from '" + filename + "' ----", LoggingLevel::Standard);
  
  	return handler.getLayoutRootWindow();
  }
  
  Window* WindowManager::loadLayoutFromString(const String& source, PropertyCallback* callback, void* userdata)
  {
      // log the fact we are about to load a layout
      Logger::getSingleton().logEvent("---- Beginning loading of GUI layout from string ----", LoggingLevel::Informative);
  
      // create handler object
      GUILayout_xmlHandler handler(callback, userdata);
  
      // do parse (which uses handler to create actual data)
      try
      {
          System::getSingleton().getXMLParser()->parseXMLString(handler, source, GUILayoutSchemaName);
      }
      catch (...)
      {
          Logger::getSingleton().logEvent("WindowManager::loadLayoutFromString - loading of layout from string failed.", LoggingLevel::Error);
          throw;
      }
  
      // log the completion of loading
      Logger::getSingleton().logEvent("---- Successfully completed loading of GUI layout from string ----", LoggingLevel::Standard);
  
      return handler.getLayoutRootWindow();
  }
  
  bool WindowManager::isDeadPoolEmpty(void) const
  {
      return d_deathrow.empty();
  }
  
  void WindowManager::cleanDeadPool(void)
  {
      WindowVector::reverse_iterator curr = d_deathrow.rbegin();
      for (; curr != d_deathrow.rend(); ++curr)
      {
  // in debug mode, log what gets cleaned from the dead pool (insane level)
  #if defined(DEBUG) || defined (_DEBUG)
          CEGUI_LOGINSANE("Window '" + (*curr)->getName() + "' about to be finally destroyed from dead pool.");
  #endif
  
          WindowFactory* factory = WindowFactoryManager::getSingleton().getFactory((*curr)->getType());
          factory->destroyWindow(*curr);
      }
  
      // all done here, so clear all pointers from dead pool
      d_deathrow.clear();
  }
  
  void WindowManager::writeLayoutToStream(const Window& window, OutStream& out_stream) const
  {
  
      XMLSerializer xml(out_stream);
      // output GUILayout start element
      xml.openTag(GUILayout_xmlHandler::GUILayoutElement);
      xml.attribute(GUILayout_xmlHandler::GUILayoutVersionAttribute,
                    GUILayout_xmlHandler::NativeVersion);
      
      // write windows
      window.writeXMLToStream(xml);
      // write closing GUILayout element
      xml.closeTag();
  }
  
  String WindowManager::getLayoutAsString(const Window& window) const
  {
      std::ostringstream str;
      writeLayoutToStream(window, str);
  
      return String(str.str().c_str());
  }
  
  //----------------------------------------------------------------------------//
  void WindowManager::saveLayoutToFile(const Window& window,
                                       const String& filename) const
  {
      std::ofstream stream;
      stream << filename;
  
      if (!stream.good())
          throw FileIOException(
              "failed to create stream for writing.");
  
      writeLayoutToStream(window, stream);
  }
  
  String WindowManager::generateUniqueWindowName()
  {
      const String ret = GeneratedWindowNameBase +
          PropertyHelper<std::uint32_t>::toString(d_uid_counter);
  
      // update counter for next time
      std::uint32_t old_uid = d_uid_counter;
      ++d_uid_counter;
  
      // log if we ever wrap-around (which should be pretty unlikely)
      if (d_uid_counter < old_uid)
          Logger::getSingleton().logEvent("UID counter for generated Window "
              "names has wrapped around - the fun shall now commence!");
  
      return ret;
  }
  
  /*************************************************************************
  	Return a WindowManager::WindowIterator object to iterate over the
  	currently defined Windows.
  *************************************************************************/
  WindowManager::WindowIterator WindowManager::getIterator(void) const
  {
  	return WindowIterator(d_windowRegistry.begin(), d_windowRegistry.end());
  }
  
  /*************************************************************************
      Outputs the names of ALL existing windows to log (DEBUG function).
  *************************************************************************/
  void WindowManager::DEBUG_dumpWindowNames(String zone) const
  {
      Logger::getSingleton().logEvent("WINDOW NAMES DUMP (" + zone + ")");
      Logger::getSingleton().logEvent("-----------------");
  
      for (WindowVector::const_iterator i = d_windowRegistry.begin();
           i != d_windowRegistry.end();
           ++i)
      {
          Logger::getSingleton().logEvent("Window : " + (*i)->getNamePath());
      }
      Logger::getSingleton().logEvent("-----------------");
  }
  
  //----------------------------------------------------------------------------//
  void WindowManager::lock()
  {
      ++d_lockCount;
  }
  
  //----------------------------------------------------------------------------//
  void WindowManager::unlock()
  {
      if (d_lockCount > 0)
          --d_lockCount;
  }
  
  //----------------------------------------------------------------------------//
  bool WindowManager::isLocked() const
  {
      return d_lockCount != 0;
  }
  
  //----------------------------------------------------------------------------//
  
  } // End of  CEGUI namespace section
  