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

  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/vtkXMLObjectReader.h"

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

#include "XML/vtkXMLKWParser.h"

#include <vtksys/ios/sstream>

vtkCxxRevisionMacro(vtkXMLObjectReader, "$Revision: 1.21 $");

//----------------------------------------------------------------------------
vtkXMLObjectReader::vtkXMLObjectReader()
{
  this->XMLParser = 0;  
  this->LastParsedElement = 0;
}

//----------------------------------------------------------------------------
vtkXMLObjectReader::~vtkXMLObjectReader()
{
  this->DestroyXMLParser();
}

//----------------------------------------------------------------------------
void vtkXMLObjectReader::CreateXMLParser()
{
  this->DestroyXMLParser();
  if(!this->XMLParser)
    {
    this->XMLParser = vtkXMLKWParser::New();
    }
}

//----------------------------------------------------------------------------
void vtkXMLObjectReader::DestroyXMLParser()
{
  if (this->XMLParser)
    {
    this->XMLParser->Delete();
    this->XMLParser = 0;
    }
}

//----------------------------------------------------------------------------
int vtkXMLObjectReader::ParseStream(istream &is)
{
  const char *encodings[] = 
    {
      NULL,
      "ISO-8859-1",
      "US-ASCII"
    };

  for (int i = 0; i < (sizeof(encodings) / sizeof(encodings[0])); i++)
    {
    this->CreateXMLParser();
    this->XMLParser->SetEncoding(encodings[i]);
    this->XMLParser->SetStream(&is);
    this->XMLParser->SetAttributesEncoding(this->GetDefaultCharacterEncoding());
    
    if (this->XMLParser->Parse())
      {
      vtkXMLUtilities::UnFactorElements(this->XMLParser->GetRootElement());
      return this->Parse(this->XMLParser->GetRootElement());
      }
    }

  return 0;
}

//----------------------------------------------------------------------------
const char* vtkXMLObjectReader::GetParsedFromFileAttributeName()
{
  return "ParsedFromFile";
}

//----------------------------------------------------------------------------
int vtkXMLObjectReader::ParseFile(const char *filename)
{
  const char *encodings[] = 
    {
      NULL,
      "ISO-8859-1",
      "US-ASCII"
    };

  for (int i = 0; i < (sizeof(encodings) / sizeof(encodings[0])); i++)
    {
    ifstream is(filename);
    
    this->CreateXMLParser();
    this->XMLParser->SetEncoding(encodings[i]);
    this->XMLParser->SetStream(&is);
    this->XMLParser->SetAttributesEncoding(
      this->GetDefaultCharacterEncoding());

    if (this->XMLParser->Parse())
      {
      vtkXMLUtilities::UnFactorElements(this->XMLParser->GetRootElement());
      this->XMLParser->GetRootElement()->SetAttribute(
        vtkXMLObjectReader::GetParsedFromFileAttributeName(), filename);
      return this->Parse(this->XMLParser->GetRootElement());
      }
    }

  return 0;
}

//----------------------------------------------------------------------------
int vtkXMLObjectReader::ParseString(const char *str)
{
  if (!str)
    {
    return 0;
    }

  vtksys_ios::stringstream strstr;
  strstr << str;
  int res = this->ParseStream(strstr);
  return res;
}

//----------------------------------------------------------------------------
int vtkXMLObjectReader::InitializeParsing()
{
  this->LastParsedElement = 0;

  return 1;
}

//----------------------------------------------------------------------------
int vtkXMLObjectReader::Parse(vtkXMLDataElement *elem)
{
  this->InitializeParsing();

  if (!this->Object)
    {
    vtkErrorMacro(<< "The Object is not set!");
    return 0;
    }

  if (!elem)
    {
    vtkErrorMacro(<< "Can not parse a NULL XML element!");
    return 0;
    }

  this->LastParsedElement = elem;

  return 1;
}

//----------------------------------------------------------------------------
vtkXMLDataElement* 
vtkXMLObjectReader::ParseInElement(vtkXMLDataElement *parent)
{
  if (!parent)
    {
    return NULL;
    }

  // Look for the nested element matching the root name

  vtkXMLDataElement *nested_elem = 
    parent->FindNestedElementWithName(this->GetRootElementName());
  if (!nested_elem)
    {
    return NULL;
    }

  // Parse the nested element

  return this->Parse(nested_elem) ? nested_elem : NULL;
}

//----------------------------------------------------------------------------
int vtkXMLObjectReader::IsInElement(vtkXMLDataElement *parent)
{
  if (!parent)
    {
    return 0;
    }

  // Look for the nested element matching the root name

  vtkXMLDataElement *nested_elem = 
    parent->FindNestedElementWithName(this->GetRootElementName());
  if (!nested_elem)
    {
    return 0;
    }

  // It is in the nested element

  return 1;
}

//----------------------------------------------------------------------------
vtkXMLDataElement* vtkXMLObjectReader::ParseInNestedElement(
  vtkXMLDataElement *grandparent, const char *name)
{
  if (!grandparent || !name || !*name)
    {
    return NULL;
    }

  // Look for the nested parent element matching the name

  vtkXMLDataElement *parent = grandparent->FindNestedElementWithName(name);
  if (!parent)
    {
    return NULL;
    }

  // Parse the parent (look for the element inside)

  return this->ParseInElement(parent);
}

//----------------------------------------------------------------------------
int vtkXMLObjectReader::IsInNestedElement(vtkXMLDataElement *grandparent,
                                          const char *name)
{
  if (!grandparent || !name || !*name)
    {
    return 0;
    }

  // Look for the nested parent element matching the name

  vtkXMLDataElement *parent = grandparent->FindNestedElementWithName(name);
  if (!parent)
    {
    return 0;
    }

  // Is it in parent ?

  return this->IsInElement(parent);
}
