/*
  PanelManager  -  management of dockable GDL panels

  Copyright (C)         Lumiera.org
    2008,               Joel Holdsworth <joel@airwebreathe.org.uk>

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License as
  published by the Free Software Foundation; either version 2 of
  the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

* *****************************************************/


#include "gui/workspace/panel-manager.hpp"

#include "gui/panel/assets-panel.hpp"
#include "gui/panel/viewer-panel.hpp"
#include "gui/panel/timeline-panel.hpp"

#include "include/logging.h"

using namespace boost;
using namespace std;
using namespace Gtk;

namespace gui {
namespace workspace {
  
  const PanelManager::PanelDescription
    PanelManager::panelDescriptionList[] = {
      PanelManager::Panel<TimelinePanel>(),
      PanelManager::Panel<ViewerPanel>(),
      PanelManager::Panel<AssetsPanel>()
    };
  
  unsigned short PanelManager::panelID = 0;
  
  
  
  PanelManager::PanelManager (WorkspaceWindow& owner)
    : workspaceWindow_(owner)
    , dock_()
    , dockBar_(dock_)
    , dockLayout_()
    {
      /* Create the DockLayout */
      dockLayout_ = Gdl::DockLayout::create(dock_);
      
      /* Setup the Switcher Style */
      Glib::RefPtr<Gdl::DockMaster> dock_master = dock_.property_master();
      dock_master->property_switcher_style() = Gdl::SWITCHER_STYLE_ICON;
      
      memset(&dockPlaceholders_, 0, sizeof(dockPlaceholders_)); 
    }
  
  
  
  PanelManager::~PanelManager()
  {
      ///////////////////////////////////////////////////////TICKET #195 : violation of policy, dtors must not do any work 
      ///////////////////////////////////////////////////////TICKET #172 : observed as a reason for crashes when closing the GUI. It was invoked after end of main, when the GUI as already gone.
    
#if false ///////////////////////////////////////////////////TICKET #937 : disabled for GTK-3 transition. TODO investigate why this logic existed...    
    for(int i = 0; i < 4; i++)
      if(dockPlaceholders_[i])
        g_object_unref(dockPlaceholders_[i]);
        
    clearPanels();
#endif    ///////////////////////////////////////////////////TICKET #937 : (End)disabled for GTK-3 transition.
  }
  
  
  
  void
  PanelManager::setupDock()
  {
    REQUIRE(dockPlaceholders_[0] == NULL && dockPlaceholders_[1] == NULL &&
      dockPlaceholders_[2] == NULL && dockPlaceholders_[3] == NULL);
    dockPlaceholders_[0] = GDL_DOCK_PLACEHOLDER(gdl_dock_placeholder_new(
      "ph1", GDL_DOCK_OBJECT(dock_.gobj()), GDL_DOCK_TOP, FALSE));
    dockPlaceholders_[1] = GDL_DOCK_PLACEHOLDER(gdl_dock_placeholder_new(
      "ph2", GDL_DOCK_OBJECT(dock_.gobj()), GDL_DOCK_BOTTOM, FALSE));
    dockPlaceholders_[2] = GDL_DOCK_PLACEHOLDER(gdl_dock_placeholder_new(
      "ph3", GDL_DOCK_OBJECT(dock_.gobj()), GDL_DOCK_LEFT, FALSE));
    dockPlaceholders_[3] = GDL_DOCK_PLACEHOLDER(gdl_dock_placeholder_new(
      "ph4", GDL_DOCK_OBJECT(dock_.gobj()), GDL_DOCK_RIGHT, FALSE));
    ENSURE(dockPlaceholders_[0] && dockPlaceholders_[1] &&
      dockPlaceholders_[2] && dockPlaceholders_[3]);
    
    createPanels();
  }
  
  
  Gdl::Dock&
  PanelManager::getDock()
  {
    return dock_;
  }
  
  
  Gdl::DockBar&
  PanelManager::getDockBar()
  {
    return dockBar_;
  }
  
  
  WorkspaceWindow&
  PanelManager::getWorkspaceWindow()
  {
    return workspaceWindow_;
  }
  
  
  void
  PanelManager::showPanel (const int description_index)
  {
    // Try and find the panel and present it if possible
    list< panel::Panel* >::iterator i;
    for(i = panels_.begin(); i != panels_.end(); i++)
      {
        panel::Panel* const panel = *i;
        if (getPanelType(panel) == description_index)
          {
            if (!panel->is_shown()) panel->show();
            
            Gdl::DockItem &dock_item = panel->getDockItem();
         // ENSURE(dock_item);
            dock_item.present(dock_);
            return;
          }
      }
    
    // Create the new panel
    panel::Panel *new_panel = createPanel_by_index (description_index);
    
    // Dock the item
    dock_.add_item(new_panel->getDockItem(), Gdl::DOCK_FLOATING);
  }
  
  
  void
  PanelManager::switchPanel (panel::Panel& old_panel, const int description_index)
  {
    REQUIRE (description_index >= 0 &&
             description_index < getPanelDescriptionCount());
    
    // Get the dock item
    Gdl::DockItem &dock_item = old_panel.getDockItem();
    
    // Release the old panel
    removePanel (&old_panel);
    
    // Create the new panel
    createPanel_by_index (description_index, dock_item);
  }
  
  
  void
  PanelManager::splitPanel (panel::Panel& panel, Gtk::Orientation split_direction)
  {
    
    // Create the new panel
    const int index = getPanelType(&panel);
    panel::Panel *new_panel = createPanel_by_index(index);
    
    // Dock the panel
    Gdl::DockPlacement placement = Gdl::DOCK_NONE;
    switch(split_direction)
      {
      case ORIENTATION_HORIZONTAL:
        placement = Gdl::DOCK_RIGHT;
        break;
      
      case ORIENTATION_VERTICAL:
        placement = Gdl::DOCK_BOTTOM;
        break;
      
      default:
        ERROR(gui, "Unknown split_direction: %d", split_direction);
        return;
        break;
      }
      
      panel.getDockItem().dock(
          new_panel->getDockItem(),placement);
  }
  
  
  int
  PanelManager::getPanelDescriptionCount()
  {
    return sizeof(panelDescriptionList) / sizeof(PanelDescription);
  }
  
  
  const gchar*
  PanelManager::getPanelStockID (int index)
  {
    REQUIRE (index >= 0 && index < getPanelDescriptionCount());
    return panelDescriptionList[index].getStockID();
  }
  
  
  const char*
  PanelManager::getPanelTitle (int index)
  {
    REQUIRE (index >= 0 && index < getPanelDescriptionCount());
    return panelDescriptionList[index].getTitle();
  }
  
  
  void
  PanelManager::createPanels()
  {
    panel::Panel* assetsPanel =   createPanel_by_name("AssetsPanel");
    panel::Panel* viewerPanel =   createPanel_by_name("ViewerPanel");
    panel::Panel* timelinePanel = createPanel_by_name("TimelinePanel");
    
    dock_.add_item(assetsPanel->getDockItem(),Gdl::DOCK_LEFT);
    dock_.add_item(timelinePanel->getDockItem(),Gdl::DOCK_BOTTOM);
    dock_.add_item(viewerPanel->getDockItem(),Gdl::DOCK_RIGHT);
  }
  
  
  int
  PanelManager::findPanelDescription (const char* class_name)  const
  {
    REQUIRE(class_name);
    
    const int count = getPanelDescriptionCount();
    for(int i = 0; i < count; i++)
      {
        if (strstr(panelDescriptionList[i].getClassName(), class_name))
          return i;
      }
    
    ERROR (gui, "Unable to find a description with class name %s", class_name);
    return -1;
  }
  
  
  panel::Panel*
  PanelManager::createPanel_by_index (const int index)
  {
    REQUIRE(index >= 0 && index < getPanelDescriptionCount());
    
    // Make a unique name for the panel
    char name[5];
    snprintf(name, sizeof(name), "%X", panelID++);
    
    // Create a dock item
    return createPanel_by_index(index,
                                 *new Gdl::DockItem(name,"",Gdl::DOCK_ITEM_BEH_NORMAL));
  }
  
  
  panel::Panel*
  PanelManager::createPanel_by_index (const int index, Gdl::DockItem &dock_item)
  {
    // Create the panel object
    panel::Panel *panel =  panelDescriptionList[index].create(*this, dock_item);
    ENSURE(panel);
    panel->show_all();
    
    // Connect event handlers
    panel->signal_hidePanel().connect(sigc::bind(
      sigc::mem_fun(*this, &PanelManager::on_panel_shown), panel));
    
    // Add the panel to the list
    panels_.push_back(panel);
    
    return panel;
  }
  
  
  panel::Panel*
  PanelManager::createPanel_by_name (const char* class_name)
  {
    REQUIRE(class_name);
    const int index = findPanelDescription(class_name);
    return createPanel_by_index(index);
  }
  
  
  int
  PanelManager::getPanelType (panel::Panel* const panel)  const
  {
    REQUIRE(panel);
    
    const type_info &info = typeid(*panel); 
    const int count = getPanelDescriptionCount();
    for(int i = 0; i < count; i++)
      {
        if(info == panelDescriptionList[i].getClassInfo())
          return i;
      }
    
    ERROR(gui, "Unable to find a description with with this class type");
    return -1;
  }
  
  
  void
  PanelManager::removePanel (panel::Panel* const panel)
  {
    REQUIRE(panel);
    
    list< panel::Panel* >::iterator i;
    for(i = panels_.begin(); i != panels_.end(); i++)
      {
        if(*i == panel)
          {
            delete panel;
            panels_.erase(i);
            break;
          }
      }
  }
  
  
  void
  PanelManager::clearPanels()
  {
      ///////////////////////////////////////////////////////TICKET #195 : this whole approach smells like an obsolete "C-style" approach.  We should strive to let the runtime system do such stuff for us whenever possible, eg. by using smart pointers
    
    list< panel::Panel* >::iterator i;
    for(i = panels_.begin(); i != panels_.end(); i++)
      delete *i;
    panels_.clear();
  }
  
  
  void
  PanelManager::on_panel_shown (panel::Panel* panel)
  {
    REQUIRE(panel);
    if(panel->is_shown() || panel->is_iconified()) return;
    
    removePanel(panel);
  }
  
  
  
}}// namespace gui::workspace
