/*=========================================================================

  Copyright (c) Kitware, Inc.
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/VolViewCopyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "XML/vtkXMLVVWindowBaseReader.h"

#include "vtkObjectFactory.h"
#include "vtkXMLDataElement.h"

#include "vtkKWProgressGauge.h"
#include "vtkKWMessageDialog.h"
#include "vtkKWOpenFileProperties.h"
#include "vtkKW2DRenderWidget.h"

#include "vtkVVApplication.h"
#include "vtkVVDataItemPool.h"
#include "vtkVVFileInstance.h"
#include "vtkVVFileInstancePool.h"
#include "vtkVVSelectionFrameLayoutManager.h"
#include "vtkVVSnapshot.h"
#include "vtkVVSnapshotPool.h"
#include "vtkVVWindowBase.h"

#include "XML/vtkXMLVVFileInstancePoolReader.h"
#include "XML/vtkXMLVVDataItemPoolReader.h"
#include "XML/vtkXMLKWImageWidgetWriter.h"

#include <vtksys/SystemTools.hxx>
#include <vtksys/stl/string>

vtkStandardNewMacro(vtkXMLVVWindowBaseReader);
vtkCxxRevisionMacro(vtkXMLVVWindowBaseReader, "$Revision: 1.23 $");

//----------------------------------------------------------------------------
const char* vtkXMLVVWindowBaseReader::GetRootElementName()
{
  return "VVWindowBase";
}

//----------------------------------------------------------------------------
int vtkXMLVVWindowBaseReader::Parse(vtkXMLDataElement *elem)
{
  if (!this->Superclass::Parse(elem))
    {
    return 0;
    }

  vtkVVWindowBase *obj = vtkVVWindowBase::SafeDownCast(this->Object);
  if (!obj)
    {
    vtkWarningMacro(<< "The VVWindowBase is not set!");
    return 0;
    }

  // Get Attributes

#if 0
  int ival;
  
  if (elem->GetScalarAttribute("SupportVolumeWidget", ival))
    {
    obj->SetSupportVolumeWidget(ival);
    }

  if (elem->GetScalarAttribute("SupportObliqueProbeWidget", ival))
    {
    obj->SetSupportObliqueProbeWidget(ival);
    }

  if (elem->GetScalarAttribute("SupportLightboxWidget", ival))
    {
    obj->SetSupportLightboxWidget(ival);
    }

  if (elem->GetScalarAttribute("MaximumNumberOfSimultaneousDataItems", ival))
    {
    obj->SetMaximumNumberOfSimultaneousDataItems(ival);
    }
#endif

  // Get nested elements

  int i, j;

  //obj->CloseAllDataItems();
  //obj->CloseAllFileInstances();

  obj->GetProgressGauge()->SetValue(5.0);

  // First retrieve the file(s) to load in a temporary pool
  // Set the application here, so as to allow us to query the authentication
  // method from the application and apply it to the files before loading them.

  vtkVVFileInstancePool *temp_pool = vtkVVFileInstancePool::New();
  temp_pool->SetApplication(obj->GetApplication());

  vtkXMLVVFileInstancePoolReader *fip_xmlr = 
    vtkXMLVVFileInstancePoolReader::SafeDownCast(temp_pool->GetNewXMLReader());
  fip_xmlr->ParseInElement(elem);
  fip_xmlr->Delete();
  
  obj->GetProgressGauge()->SetValue(10.0);

  // Now remove from the current pool those files that we don't need anymore
  // and add the new files to the current pool.
  
  vtkVVFileInstancePool *pool = obj->GetFileInstancePool();
  if (pool)
    {
    int done = 0, released = 0;
    while (!done)
      {
      done = 1;
      for (i = 0; i < pool->GetNumberOfFileInstances(); i++)
        {
        vtkVVFileInstance *file = pool->GetNthFileInstance(i);
        if (!temp_pool->HasSimilarFileInstance(file))
          {
          obj->ReleaseFileInstance(file);
          released++;
          done = 0;
          break;
          }
        }
      }
    if (released)
      {
      obj->Update(); // needed for more memory to be de-allocated
      }
    
    for (i = 0; i < temp_pool->GetNumberOfFileInstances(); i++)
      {
      vtkVVFileInstance *file = temp_pool->GetNthFileInstance(i);
      int nb_similar = pool->GetNumberOfSimilarFileInstances(file);
      int same_file_found = 0;
      for (j = 0; j < nb_similar; j++)
        {
        vtkVVFileInstance *candidate = 
          pool->GetNthSimilarFileInstance(j, file);
        if (!strcmp(candidate->GetName(), file->GetName()))
          {
          same_file_found = 1;
          break;
          }
        }
      if (!same_file_found)
        {
        pool->AddFileInstance(file);
        }
      }
    }
  temp_pool->RemoveAllFileInstances();

  obj->GetProgressGauge()->SetValue(20.0);

  // Load the data

  vtksys_stl::string not_loaded;

  for (i = 0; i < pool->GetNumberOfFileInstances(); i++)
    {
    vtkVVFileInstance *file = pool->GetNthFileInstance(i);
    file->SetApplication(obj->GetApplication()); // Propagate the application.

    if (!file->GetDataItemPool()->GetNumberOfDataItems() && 
        !file->Load() &&
        !file->LoadFromURIs())
      {
      if (not_loaded.size())
        {
        not_loaded += "\n\n";
        }
      not_loaded += file->GetFileName();
      temp_pool->AddFileInstance(file); // can't remove directly while looping
      }
    else
      {
      for (j = 0; j < file->GetDataItemPool()->GetNumberOfDataItems(); j++)
        {
        vtkVVDataItem *item = file->GetDataItemPool()->GetNthDataItem(j);
        if (!obj->GetDataItemPool()->HasDataItem(item))
          {
          obj->GetDataItemPool()->AddDataItem(item);
          }
        }
      }

    obj->GetProgressGauge()->SetValue(
      20.0 + ((double)(i+1) / (double)pool->GetNumberOfFileInstances() * 50));
    }

  for (i = 0; i < temp_pool->GetNumberOfFileInstances(); i++)
    {
    pool->RemoveFileInstance(temp_pool->GetNthFileInstance(i));
    }

  temp_pool->Delete();

  obj->GetProgressGauge()->SetValue(70.0);

  if (not_loaded.size())
    {
    not_loaded = 
      "The following file(s) could not be loaded:\n\n" + not_loaded + "\n\n" +
      "Please make sure that the paths are correct and accessible to the "
      "current user. If you are trying to read a session file that was "
      "created on a different machine but know where the corresponding data "
      "files are located on the current machine, try moving the session file "
      "to the same directory as the data files.";
    vtkKWMessageDialog::PopupMessage(
      obj->GetApplication(), NULL, "Load Data Error", not_loaded.c_str(),
      vtkKWMessageDialog::ErrorIcon);
    }

  // At this point we have loaded the data, and vtkVVDataItem have been
  // created automatically, reference from both each 
  // vtkVVFileInstance::DataItemPool and the vtkVVWindowBase::DataItemPool.
  // Let's unserialize the DataItemPool in UpdateMode, so that the references
  // are not changed (i.e. no new DataItem is created), but the current
  // DataItem are updated with the value in the session (by checking for
  // instances with the same name)

  vtkXMLVVDataItemPoolReader *dip_xmlr = 
    vtkXMLVVDataItemPoolReader::SafeDownCast(
      obj->GetDataItemPool()->GetNewXMLReader());
  dip_xmlr->UpdateModeOn();
  dip_xmlr->ParseInElement(elem);
  dip_xmlr->Delete();
 
  // DO NOT ADD ANY PROGRESS REPORT BETWEEN HERE AND THE END OF THE
  // LAYOUT MANAGER DESERIALIZATION. 
  // Doing so would trigger an "update idletask" which would give a change
  // to the layout manager to put each of the below widget on screen. We
  // do not want that, we want the final layout directly, without flickering
  // or slowdown.

  // Add the default render widgets

  for (i = 0; i < pool->GetNumberOfFileInstances(); i++)
    {
    vtkVVFileInstance *file = pool->GetNthFileInstance(i);

    // Add all render widgets

    if (!file->HasRenderWidgetInWindow(obj))
      {
      file->AddDefaultRenderWidgets(obj);
      }
    }

  // Read the Widget Layout Manager

  vtkVVSelectionFrameLayoutManager *mgr = obj->GetDataSetWidgetLayoutManager();
  if (mgr)
    {
    vtkXMLObjectReader *xmlr = mgr->GetNewXMLReader();
    xmlr->ParseInElement(elem);
    xmlr->Delete();

    // If some data was not loaded, try to avoid "holes" in the manager by
    // adjusting the resolution automatically (this may show many more 
    // widgets than intended in the session, but that's OK).
    if (not_loaded.size())
      {
      mgr->AdjustResolution();
      }
    }

  // Retrieve the snapshots

  obj->GetProgressGauge()->SetValue(90.0);

  vtkVVSnapshotPool *snapshot_pool = obj->GetSnapshotPool();
  if (snapshot_pool)
    {
    vtkXMLObjectReader *xmlr = snapshot_pool->GetNewXMLReader();
    xmlr->ParseInElement(elem);
    xmlr->Delete();
    }
  
  obj->GetProgressGauge()->SetValue(100.0);
  obj->GetProgressGauge()->SetValue(0.0);

  return 1;
}

//----------------------------------------------------------------------------
void vtkXMLVVWindowBaseReader::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);
}
