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

  Program:   ParaView
  Module:    vtkSMTransferFunctionPresets.cxx

  Copyright (c) Kitware, Inc.
  All rights reserved.
  See Copyright.txt or http://www.paraview.org/HTML/Copyright.html 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 "vtkSMTransferFunctionPresets.h"
#include "vtkSMTransferFunctionPresetsBuiltin.h"

#include "vtkNew.h"
#include "vtkObjectFactory.h"
#include "vtkSMSettings.h"
#include "vtkSMTrace.h"
#include "vtkSMTransferFunctionProxy.h"
#include "vtkTuple.h"

#include "vtk_jsoncpp.h"
#include "vtksys/SystemTools.hxx"

#include <cassert>
#include <sstream>
#include <set>
#include <list>

class vtkSMTransferFunctionPresets::vtkInternals
{
public:
  vtkInternals()
    : Modified(false),
    CustomPresetsLoaded(false)
    {
    }

  ~vtkInternals()
    {
    if (this->Modified)
      {
      assert(this->CustomPresetsLoaded);
      if (vtkSMSettings* settings = vtkSMSettings::GetInstance())
        {
        std::ostringstream stream;
        stream << "[\n";
        bool not_first = false;
        for (std::vector<Json::Value>::const_iterator iter = this->CustomPresets.begin();
          iter != this->CustomPresets.end(); ++iter)
          {
          if (not_first)
            {
            stream << ",\n";
            }
          not_first = true;
          stream << iter->toStyledString().c_str();
          }
        stream << "]";
        settings->SetSetting("TransferFuctionPresets.CustomPresets", stream.str());
        }
      }
    }

  const std::vector<Json::Value>& GetPresets()
    {
    if (this->Presets.empty())
      {
      this->LoadBuiltinPresets();
      this->LoadCustomPresets();
      this->Presets.insert(this->Presets.end(), this->BuiltinPresets.begin(), this->BuiltinPresets.end());
      this->Presets.insert(this->Presets.end(), this->CustomPresets.begin(), this->CustomPresets.end());
      }
    return this->Presets;
    }

  bool RemovePreset(unsigned int index)
    {
    this->GetPresets();
    if (index >= static_cast<unsigned int>(this->Presets.size()) ||
      index < static_cast<unsigned int>(this->BuiltinPresets.size()))
      {
      return false;
      }
    this->Presets.erase(this->Presets.begin() + index);
    index = (index - static_cast<unsigned int>(this->BuiltinPresets.size()));

    this->Modified = true;
    assert(this->CustomPresets.size() > index);
    this->CustomPresets.erase(this->CustomPresets.begin() + index);
    return true;
    }

  void AddPreset(const char* name, const Json::Value& value)
    {
    this->LoadCustomPresets();
    this->CustomPresets.push_back(value);
    this->CustomPresets.back()["Name"] = name;
    this->Presets.clear();
    this->Modified = true;
    }

  bool IsPresetBuiltin(unsigned int index)
    {
    this->LoadBuiltinPresets();
    return index < static_cast<unsigned int>(this->BuiltinPresets.size());
    }

  bool RenamePreset(unsigned int index, const char* newname)
    {
    if (newname && newname[0])
      {
      const std::vector<Json::Value> &presets = this->GetPresets();
      if (index < static_cast<unsigned int>(presets.size()) &&
        index >= static_cast<unsigned int>(this->BuiltinPresets.size()))
        {
        index = (index - static_cast<unsigned int>(this->BuiltinPresets.size()));
        assert(this->CustomPresets.size() > index);

        this->CustomPresets[index]["Name"] = newname;
        this->Presets.clear();
        this->Modified = true;
        return true;
        }
      }
    return false;
    }

  bool ImportPresets(const char* filename)
    {
    Json::Reader reader;
    Json::Value root;
    ifstream file;
    file.open(filename);
    if (!file)
      {
      vtkGenericWarningMacro("Failed to open file: " << filename);
      return false;
      }
    if (!reader.parse(file, root))
      {
      vtkGenericWarningMacro("File is not is a format suitable for presets: " << filename);
      return false;
      }
    if (!root.isArray())
      {
      vtkGenericWarningMacro("File may not contain presets: " << filename);
      return false;
      }
    return this->ImportPresets(root);
    }

  bool ImportPresets(const Json::Value& root)
    {
    this->LoadCustomPresets();
    this->CustomPresets.insert(this->CustomPresets.end(), root.begin(), root.end());
    this->Modified = true;
    this->Presets.clear();
    return true;
    }

private:
  std::vector<Json::Value> BuiltinPresets;
  std::vector<Json::Value> CustomPresets;
  std::vector<Json::Value> Presets;
  bool Modified;
  bool CustomPresetsLoaded;

  void LoadBuiltinPresets()
    {
    if (this->BuiltinPresets.empty() == false) { return; }
    char* rawJSON = vtkSMTransferFunctionPresetsColorMapsJSON();
    Json::Reader reader;
    Json::Value value;
    if (!reader.parse(std::string(rawJSON), value))
      {
      vtkGenericWarningMacro(
        << "Failed to parse builtin transfer function presets: "
        << reader.getFormattedErrorMessages().c_str());
      }
    delete [] rawJSON;
    this->BuiltinPresets.insert(this->BuiltinPresets.end(), value.begin(), value.end());
    }

  void LoadCustomPresets()
    {
    if (this->CustomPresetsLoaded) { return; }
    this->CustomPresetsLoaded = true;

    const char* const settingsKey = "TransferFuctionPresets.CustomPresets";
    vtkSMSettings* settings = vtkSMSettings::GetInstance();
    if (settings == NULL || !settings->HasSetting(settingsKey))
      {
      return;
      }

    std::string presetJSON = settings->GetSettingAsString(settingsKey, "");
    Json::Reader reader;
    Json::Value value;
    if (!presetJSON.empty() && !reader.parse(presetJSON, value))
      {
      vtkGenericWarningMacro(
        << "Failed to parse custom transfer function presets: "
        << reader.getFormattedErrorMessages().c_str());
      }
    this->CustomPresets.insert(this->CustomPresets.end(), value.begin(), value.end());
    }
};

vtkStandardNewMacro(vtkSMTransferFunctionPresets);
//----------------------------------------------------------------------------
vtkSMTransferFunctionPresets::vtkSMTransferFunctionPresets()
  : Internals(new vtkSMTransferFunctionPresets::vtkInternals())
{

}

//----------------------------------------------------------------------------
vtkSMTransferFunctionPresets::~vtkSMTransferFunctionPresets()
{
  delete this->Internals;
  this->Internals = NULL;
}

//----------------------------------------------------------------------------
vtkStdString vtkSMTransferFunctionPresets::GetPresetAsString(unsigned int index)
{
  const std::vector<Json::Value> &presets = this->Internals->GetPresets();
  return index < static_cast<unsigned int>(presets.size())?
    presets[index].toStyledString() : vtkStdString();
}

//----------------------------------------------------------------------------
const Json::Value& vtkSMTransferFunctionPresets::GetPreset(unsigned int index)
{
  static Json::Value nullValue;
  const std::vector<Json::Value> &presets = this->Internals->GetPresets();
  return index < static_cast<unsigned int>(presets.size())?
    presets[index] : nullValue;
}

//----------------------------------------------------------------------------
const Json::Value& vtkSMTransferFunctionPresets::GetFirstPresetWithName(
  const char* name)
{
  static Json::Value nullValue;
  if (name == NULL)
    {
    return nullValue;
    }
  const std::vector<Json::Value> &presets = this->Internals->GetPresets();
  for (std::vector<Json::Value>::const_iterator iter = presets.begin(); iter != presets.end(); ++iter)
    {
    if (iter->get("Name", Json::Value()).asString() == name)
      {
      return (*iter);
      }
    }
  return nullValue;
}

//----------------------------------------------------------------------------
vtkStdString vtkSMTransferFunctionPresets::GetPresetName(unsigned int index)
{
  const std::vector<Json::Value> &presets = this->Internals->GetPresets();
  return index < static_cast<unsigned int>(presets.size())?
    presets[index]["Name"].asString() : vtkStdString();
}

//----------------------------------------------------------------------------
bool vtkSMTransferFunctionPresets::RenamePreset(unsigned int index, const char* newname)
{
  return this->Internals->RenamePreset(index, newname);
}

//----------------------------------------------------------------------------
bool vtkSMTransferFunctionPresets::AddPreset(
  const char* name, const vtkStdString& preset)
{
  if (!name)
    {
    vtkErrorMacro("Invalid 'name' specified.");
    return false;
    }

  Json::Value value;
  Json::Reader reader;
  if (!reader.parse(preset, value))
    {
    vtkErrorMacro("Invalid preset string.");
      vtkGenericWarningMacro(
        << "Failed to parse builtin transfer function presets: "
        << reader.getFormattedErrorMessages().c_str());
    return false;
    }
  this->Internals->AddPreset(name, value);
  return true;
}

//----------------------------------------------------------------------------
bool vtkSMTransferFunctionPresets::AddPreset(
  const char* name, const Json::Value& preset)
{
  this->Internals->AddPreset(name, preset);
  return true;
}

//----------------------------------------------------------------------------
vtkStdString vtkSMTransferFunctionPresets::AddUniquePreset(
  const Json::Value& preset, const char* prefix/*=NULL*/)
{
  prefix = prefix? prefix : "Preset";

  std::set<std::string> names;
  const std::vector<Json::Value> &presets = this->Internals->GetPresets();
  for (std::vector<Json::Value>::const_iterator iter = presets.begin(); iter != presets.end(); ++iter)
    {
    names.insert(iter->get("Name", Json::Value()).asString());
    }

  std::string name = prefix;
  std::string separator = name.size() > 0? " " : "";
  int suffix = 0;
  while (names.find(name) != names.end())
    {
    std::ostringstream stream;
    stream << prefix << separator.c_str() << suffix;
    name = stream.str();
    suffix++;
    if (suffix > 1000)
      {
      vtkErrorMacro("Giving up. Cannot find a unique name. Please provide a good prefix.");
      return vtkStdString();
      }
    }
  this->AddPreset(name.c_str(), preset);
  return name;
}

//----------------------------------------------------------------------------
bool vtkSMTransferFunctionPresets::RemovePreset(unsigned int index)
{
  return this->Internals->RemovePreset(index);
}

//----------------------------------------------------------------------------
unsigned int vtkSMTransferFunctionPresets::GetNumberOfPresets()
{
  return static_cast<unsigned int>(this->Internals->GetPresets().size());
}

//----------------------------------------------------------------------------
bool vtkSMTransferFunctionPresets::GetPresetHasOpacities(const Json::Value& preset)
{
  return (!preset.empty() && preset.isMember("Points"));
}

//----------------------------------------------------------------------------
bool vtkSMTransferFunctionPresets::GetPresetHasIndexedColors(const Json::Value& preset)
{
  return (!preset.empty() && preset.isMember("IndexedColors"));
}

//----------------------------------------------------------------------------
bool vtkSMTransferFunctionPresets::GetPresetHasAnnotations(const Json::Value& preset)
{
  return (!preset.empty() && preset.isMember("Annotations"));
}

//----------------------------------------------------------------------------
bool vtkSMTransferFunctionPresets::IsPresetBuiltin(unsigned int index)
{
  if (index >= this->GetNumberOfPresets())
    {
    vtkWarningMacro("Invalid 'index' specified:" << index);
    return false;
    }

  return this->Internals->IsPresetBuiltin(index);
}

//----------------------------------------------------------------------------
bool vtkSMTransferFunctionPresets::ImportPresets(const char* filename)
{
  if (!filename)
    {
    vtkErrorMacro("Invalid filename specified.");
    return false;
    }

  SM_SCOPED_TRACE(CallFunction)
    .arg("ImportPresets")
    .arg("filename", filename);
  if (vtksys::SystemTools::LowerCase(
      vtksys::SystemTools::GetFilenameLastExtension(filename)) == ".xml")
    {
    ifstream in(filename);
    if (in)
      {
      std::ostringstream contents;
      contents << in.rdbuf();
      in.close();
      return this->ImportPresets(
        vtkSMTransferFunctionProxy::ConvertMultipleLegacyColorMapXMLToJSON(
          contents.str().c_str()));
      }
    else
      {
      vtkErrorMacro("Failed to open file: " << filename);
      return false;
      }
    }
  else
    {
    return this->Internals->ImportPresets(filename);
    }
}

//----------------------------------------------------------------------------
bool vtkSMTransferFunctionPresets::ImportPresets(const Json::Value& presets)
{
  return this->Internals->ImportPresets(presets);
}

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