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

  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.

=========================================================================*/
#define VTK_WINDOWS_FULL

#include "vtkWindows.h"

#include "vtkVVApplication.h"

#include "vtkAnalyzeReader.h"
#include "vtkDICOMReader.h"
#include "vtkGESignaReader.h"
#include "vtkGESignaReader3D.h"
#include "vtkImageData.h"
#include "vtkImageReader2Factory.h"
#include "vtkObjectFactory.h"
#include "vtkOutputWindow.h"
#include "vtkPICReader.h"
#include "vtkTclUtil.h"
#include "vtkURIHandler.h"

#include "vtkKWCacheManager.h"
#include "vtkKWClipboardHelper.h"
#include "vtkKWInternationalization.h"
#include "vtkKWLoadSaveDialog.h"
#include "vtkKWMessageDialog.h"
#include "vtkKWOpenFileProperties.h"
#include "vtkKWOpenWizard.h"
#include "vtkKWRemoteIOManager.h"
#include "vtkKWSimpleAnimationWidget.h"
#include "vtkKWSplashScreen.h"
#include "vtkKWRegistryHelper.h"
#include "vtkKWTkUtilities.h"
#include "vtkKWVolumePropertyPresetSelector.h"
#include "vtkKWProgressGauge.h"

#include "vtkVVDataItemPool.h"
#include "vtkVVDataItemVolume.h"
#include "vtkVVDisplayInterface.h"
#include "vtkVVFileAuthenticator.h"
#include "vtkVVFileInstance.h"
#include "vtkVVFileInstancePool.h"
#include "vtkVVMD5FileAuthenticator.h"
#include "vtkVVReviewInterface.h"
#include "vtkVVSelectionFrameLayoutManager.h"
#include "vtkVVWindow.h"
#include "vtkVVWindowBase.h"
#include "vtkVVPaintbrushWidgetEditor.h"

#include "XML/vtkXMLVVApplicationReader.h"
#include "XML/vtkXMLVVApplicationWriter.h"

#include <vtksys/stl/string>
#include <vtksys/stl/vector>
#include <sstream>

// Splash screen

#include "KWVolViewDefaultSplashScreen.h"

//#include "vtkKWVolViewConfigure.h"
#include "vtkKWWidgetsProConfigure.h"
#include "vtkKWVolViewBuildConfigure.h" // KWVolView_INSTALL_DATA_DIR

// Application icons

#include <vtksys/SystemTools.hxx>

const char *vtkVVApplication::ExternalApplicationPathRegKey = "ExternalApplicationPath";
const char *vtkVVApplication::ExternalApplicationParametersRegKey = "ExternalApplicationParameters";
const char *vtkVVApplication::PaintbrushNotSavedInSessionDialogName = "PaintbrushNotSavedInSession";

//----------------------------------------------------------------------------
vtkStandardNewMacro(vtkVVApplication);
vtkCxxRevisionMacro(vtkVVApplication, "$Revision: 1.99 $");
vtkCxxSetObjectMacro(vtkVVApplication, Authenticator, vtkVVFileAuthenticator);

#ifdef KWCommonPro_USE_XML_RW
#include "XML/vtkXMLVVApplicationReader.h"
#include "XML/vtkXMLVVApplicationWriter.h"
#endif
vtkKWGetXMLReaderWriterObjectsImplementationMacro(vtkVVApplication, vtkXMLVVApplicationReader, vtkXMLVVApplicationWriter);

extern "C" int Kwvolview_Init(Tcl_Interp *interp);

//----------------------------------------------------------------------------
Tcl_Interp *vtkVVApplication::InitializeTcl(int argc, 
                                            char *argv[], 
                                            ostream *err)
{
  Tcl_Interp *interp = vtkKWApplicationPro::InitializeTcl(argc, argv, err);
  if (interp)
    {
    Kwvolview_Init(interp);
   }

  // As a convenience, try to find the text domain binding for
  // VolView right now

  vtkKWInternationalization::FindTextDomainBinding(
    "KWVolView", KWVolView_INSTALL_DATA_DIR);

  return interp;
}

//----------------------------------------------------------------------------
vtkVVApplication::vtkVVApplication()
{
  this->SupportSplashScreen = 1;
  this->SendErrorLogBeforeExit = 1;

  // Initialize the readers

  this->InitializeReaders();

  this->SessionFileExtensions = NULL;
  this->SetSessionFileExtensions(".vse");

  this->ExternalApplicationPath = NULL;
  this->ExternalApplicationParameters = NULL;

  this->RemoteIOManager  = NULL;
  this->AuthenticateRead = 0;
  this->Authenticator    = vtkVVFileAuthenticator::New();
  this->Authenticator->SetApplication(this);
}

//----------------------------------------------------------------------------
vtkVVApplication::~vtkVVApplication()
{
  if (this->RemoteIOManager)
    {
    this->RemoteIOManager->Delete();
    this->RemoteIOManager = NULL;
    }

  this->SetSessionFileExtensions(NULL);
  this->SetExternalApplicationPath(NULL);
  this->SetExternalApplicationParameters(NULL);
}

//----------------------------------------------------------------------------
void vtkVVApplication::PrepareForDelete()
{
  if (this->Authenticator)
    {
    this->Authenticator->Delete();
    this->Authenticator = NULL;
    }

  this->Superclass::PrepareForDelete();
}

//----------------------------------------------------------------------------
void vtkVVApplication::InitializeReaders()
{
  vtkImageReader2* reader;
  
  reader = vtkPICReader::New();
  vtkImageReader2Factory::RegisterReader(reader);
  reader->Delete();
  
  reader = vtkAnalyzeReader::New();
  vtkImageReader2Factory::RegisterReader(reader);
  reader->Delete();
  
  reader = vtkDICOMReader::New();
  vtkImageReader2Factory::RegisterReader(reader);
  reader->Delete();
  
  reader = vtkGESignaReader::New();
  vtkImageReader2Factory::RegisterReader(reader);
  reader->Delete();

  reader = vtkGESignaReader3D::New();
  vtkImageReader2Factory::RegisterReader(reader);
  reader->Delete();
}

//----------------------------------------------------------------------------
void vtkVVApplication::ParseCommandLineArguments(int argc, char **argv)
{ 
  this->Superclass::ParseCommandLineArguments(argc, argv);

  int index = 0, pos = 0;
  int i, nb_windows = this->GetNumberOfWindows();

  // Command line args: script or data file
  // if a Tcl script was passed in as an arg then load it
  // otherwise try to load it as a volume
        
  if (argc > 1)
    {
    int argc_idx = 1;
    for (argc_idx = 1; argc_idx < argc; argc_idx++)
      {
      const char *file_arg = argv[argc_idx];
      if (file_arg && strlen(file_arg) > 4 && file_arg[0] != '-')
        {
        for (i = 0; i < nb_windows; i++)
          {
          vtkKWWindowBase *win = this->GetNthWindow(i);
          vtkVVWindowBase *vvwin = vtkVVWindowBase::SafeDownCast(win);
          if (win)
            {
            if (!strcmp(file_arg + strlen(file_arg) - 4, ".tcl"))
              {
              // In production mode, we do not want people to load their
              // own Tcl script.
              if (!this->GetReleaseMode())
                {
                win->LoadScript(file_arg);
                }
              }
            else if (vvwin)
              {
              vtksys_stl::string to_unix(file_arg);
              vtksys::SystemTools::ConvertToUnixSlashes(to_unix);
              vvwin->Open(to_unix.c_str());
              }
            win = this->GetNthWindow(i);
			if (win)
			  {
              win->Display();
			  }
            }
          }
        break;
        }
      }
    }

  // Command line args: Flickr
  
  if (vtkKWApplication::CheckForArgument(
        argc, argv, "--send-screenshot-to-flickr", index) == VTK_OK)
    {
    this->SendScreenshotToFlickr();
    }

  // Command line args: enable force redownload
  
  if (vtkKWApplication::CheckForArgument(
        argc, argv, "--cache-force-redownload", index) == VTK_OK)
    {
    vtkKWRemoteIOManager *remote_io_mgr = this->GetRemoteIOManager();
    if (remote_io_mgr)
      {
      vtkKWCacheManager *cache_mgr = remote_io_mgr->GetCacheManager();
      if (cache_mgr)
        {
        cache_mgr->EnableForceRedownloadOn();
        }
      }
    }

  // Command line args: select a view
  
  if (this->CheckForValuedArgument(
        argc, argv, "--select-view-at", index, pos) == VTK_OK)
    {
    //--select-view-at=x,y
    vtkVVWindow *win = vtkVVWindow::SafeDownCast(this->GetNthWindow(0));
    if (win)
      {
      vtkVVSelectionFrameLayoutManager *mgr = 
        win->GetDataSetWidgetLayoutManager();
      if (mgr)
        {
        vtksys_stl::vector<vtksys_stl::string> split_elems;
        vtksys::SystemTools::Split(argv[index] + pos, split_elems, ',');
        if (split_elems.size() == 2)
          {
          this->ProcessPendingEvents(); // important to bring the window up
          int col = atoi(split_elems[0].c_str());
          int row = atoi(split_elems[1].c_str());
          mgr->SelectWidget(mgr->GetWidgetAtPosition(col, row));
          }
        }
      }
    }

  // Command line args: apply a volume property preset
  
  if (this->CheckForValuedArgument(
        argc, argv, "--apply-nth-volume-property-preset", index, pos) == VTK_OK)
    {
    // --apply-nth-volume-property-preset=preset_index
    vtkVVWindow *win = vtkVVWindow::SafeDownCast(this->GetNthWindow(0));
    if (win)
      {
      vtkVVDisplayInterface *display = win->GetDisplayInterface();
      if (display)
        {
        vtkKWVolumePropertyPresetSelector *preset_selector = 
          display->GetVolumePropertyPresetSelector();
        if (preset_selector)
          {
          int row = atoi(argv[index] + pos);
          int id = preset_selector->GetIdOfPresetAtRow(row);
          preset_selector->SelectPreset(id);
          }
        }
      }
    }

  // Command line args: create movie 
  
  if (this->CheckForValuedArgument(
        argc, argv, "--create-camera-movie", index, pos) == VTK_OK)
    {
    // --create-camera-movie=x_start,x_rotation,y_start,y_rotation,z_start,z_rotation,zoom_start,zoom_factor,nb_frames,width,height,fps,fourcc,filename
    vtkVVWindow *win = vtkVVWindow::SafeDownCast(this->GetNthWindow(0));
    if (win)
      {
      vtkVVReviewInterface *review = win->GetReviewInterface();
      if (review)
        {
        vtkKWSimpleAnimationWidget *anim = review->GetAnimationWidget();
        if (anim)
          {
          vtksys_stl::vector<vtksys_stl::string> split_elems;
          vtksys::SystemTools::Split(argv[index] + pos, split_elems, ',');
          if (split_elems.size() == 14)
            {
            anim->SetXStart(atof(split_elems[0].c_str()));
            anim->SetXRotation(atof(split_elems[1].c_str()));
            anim->SetYStart(atof(split_elems[2].c_str()));
            anim->SetYRotation(atof(split_elems[3].c_str()));
            anim->SetZStart(atof(split_elems[4].c_str()));
            anim->SetZRotation(atof(split_elems[5].c_str()));
            anim->SetZoomStart(atof(split_elems[6].c_str()));
            anim->SetZoomFactor(atof(split_elems[7].c_str()));
            anim->SetNumberOfFrames(atoi(split_elems[8].c_str()));
            anim->CreateCameraAnimation(
              split_elems[13].c_str(),
              atoi(split_elems[9].c_str()), 
              atoi(split_elems[10].c_str()),
              atoi(split_elems[11].c_str()),
              split_elems[12].c_str()
              );
            win->Close();
            }
          }
        }
      }
    }
}

//----------------------------------------------------------------------------
void vtkVVApplication::Start(int argc, char*argv[])
{
  vtkOutputWindow::GetInstance()->PromptUserOff();

  if (this->InExit)
    {
    return;
    }

  // Set the font size

#ifdef _WIN32
  this->Script("option add *font {{Tahoma} 8}");
#endif
  
  // Splash screen ?

  if (this->SupportSplashScreen && this->SplashScreenVisibility)
    {
    this->CreateSplashScreen();
    this->GetSplashScreen()->SetProgressMessage(
      ks_("Startup|Progress|Initializing application..."));
    }

  // Create windows that were added so far

  int i, nb_windows = this->GetNumberOfWindows();
  for (i = 0; i < nb_windows; i++)
    {
    vtkKWWindowBase *win = this->GetNthWindow(i);
    if (win && !win->IsCreated())
      {
      win->Create();
	  win->Update();
      }
    }

  // Do not Set a progress message from now on, it will make the splash go
  // in front in a bad way. Basically do not send a message when the
  // ui has been created but not packed/displayed yet.

  if (this->SupportSplashScreen && this->SplashScreenVisibility)
    {
    this->GetSplashScreen()->Withdraw();
    }

  this->Superclass::Start(argc, argv);
}

//----------------------------------------------------------------------------
void vtkVVApplication::CreateSplashScreen()
{
  this->GetSplashScreen()->SetProgressMessageVerticalOffset(-17);
  const unsigned int width = 
    image_KWVolViewDefaultSplashScreen_width;
  const unsigned int height = 
    image_KWVolViewDefaultSplashScreen_height;
  const unsigned int pixel_size = 
    image_KWVolViewDefaultSplashScreen_pixel_size;
  const unsigned int nb_sections = 
    image_KWVolViewDefaultSplashScreen_nb_sections;
  const unsigned long length = 
    image_KWVolViewDefaultSplashScreen_length;
  const unsigned char **sections = 
    image_KWVolViewDefaultSplashScreen_sections;
  
  unsigned char *buffer = new unsigned char [length];
  unsigned char *ptr = buffer;
  for (unsigned int i = 0; i < nb_sections; i++)
    {
    size_t len = strlen((const char*)sections[i]);
    memcpy(ptr, sections[i], len);
    ptr += len;
    }
  
  if (!vtkKWTkUtilities::UpdatePhoto(
        this->GetMainInterp(),
        "KWVolViewDefaultSplashScreen", 
        buffer, width, height, pixel_size, length))
    {
    vtkWarningMacro("Error updating splashscreen ");
    }
  else
    {
    this->GetSplashScreen()->SetImageName("KWVolViewDefaultSplashScreen");
    }

  delete [] buffer; 
}

//----------------------------------------------------------------------------
int vtkVVApplication::LoadSession(const char *filename)
{
  if (!filename)
    {
    return 0;
    }

  vtkXMLKWApplicationReader *xmlr = this->GetXMLApplicationReader();
  xmlr->SetObject(this);
  int ok = xmlr->ParseFile(filename);
  if (!ok)
    {
    vtkKWMessageDialog::PopupMessage(
      this, NULL, "Load Session Error",
      "There was a problem loading the session file.",
      vtkKWMessageDialog::ErrorIcon);
    }
  xmlr->Delete();

  return 1;
}

//----------------------------------------------------------------------------
int vtkVVApplication::SaveSession()
{
  vtkVVWindowBase *vvwin = vtkVVWindowBase::SafeDownCast(this->GetNthWindow(0));
  int paintbrush_count = 
    vvwin->GetDataSetWidgetLayoutManager()->GetNumberOfPaintbrushWidgets();
  if (paintbrush_count)
    {
    vtkKWMessageDialog *dialog = vtkKWMessageDialog::New();
    dialog->SetApplication(this);
    dialog->SetStyleToMessage();
    dialog->SetMasterWindow(vvwin);
    dialog->SetOptions(
      vtkKWMessageDialog::WarningIcon | 
      vtkKWMessageDialog::RememberYes |
      vtkKWMessageDialog::YesDefault);
    dialog->SetDialogName(
      vtkVVApplication::PaintbrushNotSavedInSessionDialogName);
    dialog->SetText("Paintbrushes and segmentation maps can not be saved to sessions at the moment. Make sure to save them separately from the paintbrush panel.");
    dialog->SetTitle("Paintbrush not supported");
    dialog->Invoke();
    dialog->Delete();
    }

  vtkKWLoadSaveDialog *dlg = vtkKWLoadSaveDialog::New();
  dlg->SetApplication(this);
  dlg->SetParent(this->GetNthWindow(0));
  dlg->Create();
  dlg->RetrieveLastPathFromRegistry("OpenPath");
  dlg->SaveDialogOn();
  dlg->SetTitle("Save Session");

  if (this->GetSessionFileExtensions())
    {
    vtksys_stl::vector<vtksys_stl::string> split_elems;
    vtksys::SystemTools::Split(
      this->GetSessionFileExtensions(), split_elems, ' ');

    dlg->SetDefaultExtension(split_elems.front().c_str());

    vtksys_stl::string file_types("{{");
    file_types += this->GetName();
    file_types += " Session} {";
    file_types += split_elems.front();
    file_types += "}}";
    dlg->SetFileTypes(file_types.c_str());
    }

  int res = 0;
  if (dlg->Invoke() && this->SaveSession(dlg->GetFileName()))
    {
    dlg->SaveLastPathToRegistry("OpenPath");
    res = 1;
    
    vtkKWClipboardHelper* clipboard = vtkKWClipboardHelper::New();
    if(clipboard)
      {
      clipboard->CopyTextToClipboard(dlg->GetFileName());
      clipboard->Delete();
      }
    }

  dlg->Delete();

  return res;
}

//----------------------------------------------------------------------------
vtkXMLKWApplicationWriter* vtkVVApplication::GetXMLApplicationWriter()
{
  return vtkXMLVVApplicationWriter::New();
}

//----------------------------------------------------------------------------
vtkXMLKWApplicationReader* vtkVVApplication::GetXMLApplicationReader()
{
  return vtkXMLVVApplicationReader::New();
}

//----------------------------------------------------------------------------
int vtkVVApplication::SaveSession(const char *filename)
{
  if (!filename)
    {
    return 0;
    }

  vtkXMLKWApplicationWriter *xmlw = this->GetXMLApplicationWriter();
  xmlw->SetObject(this);
  xmlw->WriteIndentedOff();
  int ok = xmlw->WriteToFile(filename);
  if (!ok)
    {
    vtkKWMessageDialog::PopupMessage(
      this, NULL, "Save Session Error",
      "There was a problem writing the session file.\n"
      "Please check the location and make sure you have write\n"
      "permissions and enough disk space.",
      vtkKWMessageDialog::ErrorIcon);
    }
  xmlw->Delete();

  return ok;
}

//----------------------------------------------------------------------------
int vtkVVApplication::SendScreenshotToFlickr()
{
# if 0
  // To be fixed

  if (!this->GetDataItemPool()->GetNumberOfDataItems())
    {
    return 0;
    }

  vtkKWWindowBase *win = this->GetNthWindow(0);
  vtkVVWindowBase *vvwin = vtkVVWindowBase::SafeDownCast(win);
  vtkVVDataItem *data_item = vvwin ? vvwin->GetSelectedDataItem() : NULL;
  if (!data_item)
    {
    data_item = this->GetDataItemPool()->GetNthDataItem(0);
    }
  vtkVVDataItemVolume *volume_item = 
    vtkVVDataItemVolume::SafeDownCast(data_item);

  // Append all widgets to an image

  vtkVVSelectionFrameLayoutManager *layout_mgr = 
    vvwin ? vvwin->GetDataSetWidgetLayoutManager() : NULL;
  if (!layout_mgr)
    {
    vtkErrorMacro("Failed sending screenshot to Flickr! (no layout manager)");
    return 0;
    }

  vtkImageData *img = vtkImageData::New();
  if (!layout_mgr->AppendAllWidgetsToImageData(img))
    {
    vtkErrorMacro("Failed sending screenshot to Flickr! (unable to append)");
    img->Delete();
    return 0;
    }

  // Tags

  char buffer[128];

  vtksys_stl::string tags;

  if (this->GetName())
    {
    tags = tags + " kw:app_name=\"" + this->GetName() + "\"";
    tags = tags + " \"" + this->GetName() + "\"";
    }

  if (this->GetVersionName())
    {
    tags = tags + " kw:app_version_name=\"" + this->GetVersionName() + "\"";
    tags = tags + " \"" + this->GetVersionName() + "\"";
    }

  if (this->GetPrettyName())
    {
    tags = tags + " kw:app_pretty_name=\"" + this->GetPrettyName() + "\"";
    tags = tags + " \"" + this->GetPrettyName() + "\"";
    }

  sprintf(buffer, "%d", this->GetMajorVersion());
  tags = tags + " kw:app_major_version=" + buffer;

  sprintf(buffer, "%d", this->GetMinorVersion());
  tags = tags + " kw:app_minor_version=" + buffer;

  if (this->GetReleaseName())
    {
    tags = tags + " kw:app_release_name=\"" + this->GetReleaseName() + "\"";
    }

  if (this->GetCompanyName())
    {
    tags = tags + " \"" + this->GetCompanyName() + "\"";
    const char *inc = ", Inc.";
    if (vtksys::SystemTools::StringEndsWith(this->GetCompanyName(), inc))
      {
      vtksys_stl::string cropped(this->GetCompanyName());
      tags = tags + " \"" + 
        cropped.substr(0, cropped.size() - strlen(inc)) + "\"";
      }
    }

  tags = tags + " Visualization \"Scientific Visualization\" VTK";

  if (volume_item && volume_item->GetMedicalImageProperties())
    {
    tags = tags + " \"Medical Imaging\"";
    }

  // Title

  vtksys_stl::string title;

  if (win && win->GetTitle())
    {
    title += win->GetTitle();
    }

  // Description

  vtksys_stl::string description;

  if (vvwin)
    {
    vtkVVDataItem *data_item = vvwin->GetSelectedDataItem();
    if (!data_item)
      {
      data_item = this->GetDataItemPool()->GetNthDataItem(0);
      }
    if (data_item)
      {
      description = description + 
        vtksys::SystemTools::GetFilenameName(data_item->GetName());
      }
    }

  if (this->GetPrettyName())
    {
    description = description + "\n" + this->GetPrettyName();
    }

  if (this->GetCompanyName())
    {
    description = description + "\n" + this->GetCompanyName();
    }

  if (this->GetPurchaseURL())
    {
    description = description + "\n" + this->GetPurchaseURL();
    }

  if (this->GetCompanySalesContact())
    {
    description = description + "\n" + this->GetCompanySalesContact();
    }

  if (this->GetPrimaryCopyright())
    {
    description = description + "\n" + this->GetPrimaryCopyright();
    }

  int res = this->SendImageToFlickr(img, 
                                    NULL, NULL, NULL,
                                    title.c_str(), 
                                    description.c_str(), 
                                    tags.c_str(), 
                                    0);

  img->Delete();
  return res;
#endif
  return 1;
}

//----------------------------------------------------------------------------
vtkKWRemoteIOManager* vtkVVApplication::GetRemoteIOManager()
{
  if (!this->RemoteIOManager)
    {
    this->RemoteIOManager = vtkKWRemoteIOManager::New();

    // The default cache directory is the user-data directory + "/Cache"

    vtkstd::string cache_dir(this->GetUserDataDirectory());
    cache_dir += "/Cache";
    this->RemoteIOManager->GetCacheManager()->SetRemoteCacheDirectory(
      cache_dir.c_str());
    this->RemoteIOManager->SetTransferStatusChangedCallback(
      vtkVVApplication::TransferUpdateCallback, this);
    this->RemoteIOManager->SetTransferUpdateCallback(
      vtkVVApplication::TransferUpdateCallback, this);
    }
  return this->RemoteIOManager;
}

//----------------------------------------------------------------------------
void vtkVVApplication::TransferCallback( 
   vtkKWRemoteIOManager *caller, unsigned long event, vtkKWDataTransfer *dt)
{
  vtkVVWindow *vvwin = 
    vtkVVWindow::SafeDownCast(this->GetNthWindow(0));

  std::ostringstream s;

  vtkstd::string destURI = dt->GetDestinationURI();
  vtkstd::string sourceURI = dt->GetSourceURI();
  const int transferStatus = dt->GetTransferStatus();
  double progress = 0.0;

  if (transferStatus == vtkKWDataTransfer::Completed)
    {
    s << "Finished downloading " << destURI << std::ends;
    progress = 100.0;
    }
  else if (transferStatus == vtkKWDataTransfer::Pending)
    {
    s << "Download " << sourceURI << " pending." << std::ends;
    progress = 0.0;
    }
  else if (transferStatus == vtkKWDataTransfer::Running)
    {
    const int progressPercent = 
      (dt->GetHandler()->GetProgress() +0.005) * 100;
    s << "Time remaining: " << dt->GetHandler()->GetEstimatedTimeRemaining()
      << ". Downloaded " 
      //<< progressPercent << "% " 
      << dt->GetHandler()->GetFormattedDownloadFractionAsString() << " " 
      << sourceURI << std::ends;
    progress = dt->GetHandler()->GetProgress();
    }
  else if (transferStatus == vtkKWDataTransfer::CompletedWithErrors)
    {
    s << "Failed to download " << sourceURI << std::ends;
    progress = 0.0;
    }

  // Report some status message about the remote IO transfer.

  if (vvwin)
    {
    vvwin->SetStatusText(s.str().c_str());
    vvwin->GetProgressGauge()->SetValue( static_cast<int>(100.0*progress) );
    if (!dt->GetAsynchronous())
      {
      this->ProcessPendingEvents();
      }
    }

  // 1. Ensure that the transfer is completed.
  // 2. Ensure that these datasets are not downloaded single-shot (previews for
  //    instance are). So they are not downloaded on the main blocking thread,
  //    but via a callback invoked from another thread. 

  if (dt->GetTransferStatus() == vtkKWDataTransfer::Completed &&
      dt->GetAsynchronous())
    {
    if (!vvwin)
      {
      return;
      }

    // Let's find the file instance(s) (vtkVVFileInstance) that reference this
    // data-transfer. If there is one (or more, since a session can reference
    // the same file more than once), then this is part of a "composite"
    // transfer process, such as load a low res, then a high res data. If
    // there isn't one, its obviously a single shot transfer process. For
    // a single shot transfer process, we will simply create a new file
    // instance and load this new data up. For a "composite" transfer, we
    // need to swap volumes etc.

    vtkVVFileInstance *file = NULL;
    vtkVVFileInstancePool *file_pool = vvwin->GetFileInstancePool();
    const int num_file_instances = file_pool->GetNumberOfFileInstances();
    for (int i = 0; i < num_file_instances; i++)
      {
      vtkVVFileInstance *nth_file = file_pool->GetNthFileInstance(i);
      if (nth_file->HasDataTransfer(dt))
        {
        file = nth_file;
        if (file)
          {
          // Composite transfer: We need to do some swap out the preview 
          // with this newly downloaded data and do some magic.

          vtkVVDataItemVolume *volume = 
            vtkVVDataItemVolume::SafeDownCast(
              file->GetDataItemPool()->GetNthDataItem(0));
          if (!volume)
            {
            // Something is surely wrong if we got here. 
            vtkErrorMacro("Failed loading downloaded data!");
            }
          else
            {
            // the volume that contains the data that needs to be replaced
            // Load via the open wizard, but make sure it is using the same
            // open properties that were used loading the real/original data
            vtkKWOpenWizard *openwizard = vtkKWOpenWizard::New();
            openwizard->SetMasterWindow(this->GetNthWindow(0));
            if (file->GetOpenFileProperties())
              {
              openwizard->SetIgnoreVVIOnRead(1);
              openwizard->SetIgnoreVVIOnWrite(1);
              openwizard->SetOpenWithCurrentOpenFileProperties(1);
              openwizard->GetOpenFileProperties()->DeepCopy(
                file->GetOpenFileProperties());
              }
            int successfulLoad = 
              (openwizard->Invoke(dt->GetIdentifier(), 0) &&
               volume->LoadFromOpenWizard(openwizard, 0));
            openwizard->Delete();
            
            if (successfulLoad)
              {
              // We managed to load the downloaded data successfully. Let's 
              // swap out the preview version of this data for this new one
              volume->ResetRenderWidgetsInput();
              vvwin->Update();
              }
            }
          }
        }
      }

    if (!file)
      {
      // This is a totally new file, ie. no preview file representing this data
      // has been loaded up by VolView. Let's load it and be done.
      // Yes but no, sorry, you can't do that! It's possible the file is not
      // there because we have already selected a new snapshot that has
      // different files! vtkVVFileInstance::CancelAllDataTransfers() is 
      // called when that happens, but as of now there does not seem K 
      // implemented anything to cancel a data transfer, so *assume* here
      // that if no file was found, then that's because it was closed
      // already (it should be there otherwise because of the preview loaded
      // asynchronously).
      //vvwin->Open(dt->GetIdentifier());
      }
    }  
}

//----------------------------------------------------------------------------
void vtkVVApplication::TransferUpdateCallback( 
                      vtkObject *caller,
                      unsigned long event, 
                      void *clientdata, 
                      void *calldata )
{
  vtkVVApplication * self = reinterpret_cast< vtkVVApplication *>(clientdata);
  vtkKWDataTransfer *dt = reinterpret_cast< vtkKWDataTransfer *>(calldata);
  self->TransferCallback(
      reinterpret_cast< vtkKWRemoteIOManager *>(caller), event, dt);
}

//----------------------------------------------------------------------------
void vtkVVApplication::SetAuthenticateRead(int r)
{
  if ((r && this->AuthenticateRead) || (r==0 && this->AuthenticateRead==0))
    {
    return;
    }

  this->AuthenticateRead = r ? 1 : 0;

  vtkVVFileAuthenticator *authenticator;
  if (this->AuthenticateRead)
    {
    authenticator = vtkVVMD5FileAuthenticator::New();
    }
  else
    {
    authenticator = vtkVVFileAuthenticator::New();
    }
  authenticator->SetApplication(this);
  this->SetAuthenticator( authenticator );
  authenticator->Delete();
}

//----------------------------------------------------------------------------
void vtkVVApplication::AddAboutText(ostream &os)
{
  this->Superclass::AddAboutText(os);

  vtkKWRemoteIOManager *remote_io_mgr = this->GetRemoteIOManager();
  if (remote_io_mgr)
    {
    vtkKWCacheManager *cache_mgr = remote_io_mgr->GetCacheManager();
    if (cache_mgr)
      {
      const char *cache_dir = cache_mgr->GetRemoteCacheDirectory();
      if (cache_dir)
        {
        os << k_("Remote IO Cache Directory") << ": " << cache_dir << endl;
        }
      }
    }

  vtkVVFileAuthenticator *file_auth = this->GetAuthenticator();
  if (file_auth)
    {
    if (file_auth->GetPublicKey())
      {
      os << k_("The File Authentication Public Key is set.") << endl;
      }
    else
      {
      os << k_("The File Authentication Public Key is NOT set.") << endl;
      }
    }
}

//----------------------------------------------------------------------------
void vtkVVApplication::SetLimitedEditionMode(int v)
{
  int old_v = this->GetLimitedEditionMode();
  this->Superclass::SetLimitedEditionMode(v);
  if (this->GetLimitedEditionMode() != old_v)
    {
    int i;
    for (i = 0; i < this->GetNumberOfWindows(); i++)
      {
      vtkVVWindowBase *win = vtkVVWindowBase::SafeDownCast(
        this->GetNthWindow(i));
      if (win)
        {
        win->GetDataSetWidgetLayoutManager()->UpdateRenderWidgetsAnnotations();
        }
      }
    }
}

//----------------------------------------------------------------------------
void vtkVVApplication::RestoreApplicationSettingsFromRegistry()
{ 
  this->Superclass::RestoreApplicationSettingsFromRegistry();

  char buffer[vtkKWRegistryHelper::RegistryKeyValueSizeMax];

  // External Application

  if (this->HasRegistryValue(
        2, "RunTime", 
        vtkVVApplication::ExternalApplicationPathRegKey) &&
      this->GetRegistryValue(
        2, "RunTime", 
        vtkVVApplication::ExternalApplicationPathRegKey, buffer) &&
      *buffer)

    {
    this->SetExternalApplicationPath(buffer);
    }

  if (this->HasRegistryValue(
        2, "RunTime", 
        vtkVVApplication::ExternalApplicationParametersRegKey) &&
      this->GetRegistryValue(
        2, "RunTime", 
        vtkVVApplication::ExternalApplicationParametersRegKey, buffer) &&
      *buffer)

    {
    this->SetExternalApplicationParameters(buffer);
    }
}

//----------------------------------------------------------------------------
void vtkVVApplication::SaveApplicationSettingsToRegistry()
{ 
  this->Superclass::SaveApplicationSettingsToRegistry();

  // External Applications

  if (this->GetExternalApplicationPath())
    {
    if (*this->GetExternalApplicationPath())
      {
      this->SetRegistryValue(
        2, "RunTime", vtkVVApplication::ExternalApplicationPathRegKey, 
        this->GetExternalApplicationPath());
      }
    else if (this->HasRegistryValue(
               2, "RunTime", 
               vtkVVApplication::ExternalApplicationPathRegKey))
      {
      this->DeleteRegistryValue(
        2, "RunTime", 
        vtkVVApplication::ExternalApplicationPathRegKey);
      }
    }

  if (this->GetExternalApplicationParameters())
    {
    if (*this->GetExternalApplicationParameters())
      {
      this->SetRegistryValue(
        2, "RunTime", vtkVVApplication::ExternalApplicationParametersRegKey, 
        this->GetExternalApplicationParameters());
      }
    else if (this->HasRegistryValue(
               2, "RunTime", 
               vtkVVApplication::ExternalApplicationParametersRegKey))
      {
      this->DeleteRegistryValue(
        2, "RunTime", 
        vtkVVApplication::ExternalApplicationParametersRegKey);
      }
    }
}

//----------------------------------------------------------------------------
int vtkVVApplication::LaunchExternalApplication()
{
  if (!this->GetExternalApplicationPath())
    {
    return 0;
    }

  vtksys_stl::string params;
  if (this->GetExternalApplicationParameters())
    {
    params.assign(this->GetExternalApplicationParameters());

    vtksys_stl::string selected_data_path;
    vtkKWWindowBase *win = this->GetNthWindow(0);
    vtkVVWindowBase *vvwin = vtkVVWindowBase::SafeDownCast(win);
    vtkVVDataItem *data_item = vvwin ? vvwin->GetSelectedDataItem() : NULL;
    vtkVVDataItemVolume *volume_item = 
      vtkVVDataItemVolume::SafeDownCast(data_item);
    if (volume_item && 
        volume_item->GetFileInstance() &&
        volume_item->GetFileInstance()->GetFileName())
      {
      selected_data_path.assign(volume_item->GetFileInstance()->GetFileName());
      }

    vtksys_stl::string last_saved_label_map_path;
    char buffer[vtkKWRegistryHelper::RegistryKeyValueSizeMax];
    if (this->HasRegistryValue(
          2, "RunTime", 
          vtkVVPaintbrushWidgetEditor::LastSavedLabelMapRegKey) &&
        this->GetRegistryValue(
          2, "RunTime", 
          vtkVVPaintbrushWidgetEditor::LastSavedLabelMapRegKey, buffer) &&
        *buffer)
      {
      last_saved_label_map_path.assign(buffer);
      }

    vtksys::SystemTools::ReplaceString(
      params, "#f", selected_data_path.c_str());
    vtksys::SystemTools::ReplaceString(
      params, "#l", last_saved_label_map_path.c_str());
    }
  this->Script("exec {%s} %s &", 
               this->GetExternalApplicationPath(),
               params.c_str());
  return 1;
}

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