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

  Program:   ParaView
  Module:    ProcessXML.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 "vtkObject.h"

#include <vtkstd/string>
#include <vtkstd/vector>
#include "kwsys/SystemTools.hxx"
#include "kwsys/RegularExpression.hxx"

class Output
{
public:
  Output()
    {
    this->MaxLen = 16000;
    this->CurrentPosition = 0;
    }
  ~Output()
    {
    this->Stream.rdbuf()->freeze(0);
    this->InitStream.rdbuf()->freeze(0);
    }

  ostrstream Stream;
  ostrstream InitStream;

  int MaxLen;
  long CurrentPosition;
  int Count;

  void PrintHeader(const char* title, const char* file)
    {
    this->Stream << endl
      << "// From file " << file << endl
      << "static const char* vtkPVInitializeStandard" << title << "Interfaces" << this->Count 
      << " =" << endl;
    this->CurrentPosition = this->Stream.tellp();
    this->InitStream << "  ostr << vtkPVInitializeStandard" << title << "Interfaces" 
      << this->Count << ";" << endl;
    }

  void CheckSplit(const char* title, const char* file, int force=0)
    {
    if ( (this->Stream.tellp() - this->CurrentPosition) > this->MaxLen ||
      force )
      {
      this->Count ++;
      this->Stream << ";" << endl;
      this->PrintHeader(title, file);
      }
    }

  int ProcessFile(const char* file, const char* title)
    {
    kwsys_ios::ifstream ifs(file);
    if ( !ifs )
      {
      cout << "Canot open file: " << file << endl;
      return VTK_ERROR;
      }
    int ch;
    int in_ifdef = 0;

    this->InitStream
      << "// Define the " << title << " interfaces." << endl
      << "//" << endl 
      << "// Generated from file: " << file << endl
      << "//" << endl
      << "char* vtkPVInitialize::GetStandard" << title << "Interfaces()" << endl
      << "{" << endl
      << "  ostrstream ostr;" << endl;

    this->Count = 0;
    this->PrintHeader(title, file);
    this->Stream << "\"";

    vtkstd::string line;
    vtkstd::string::size_type cc;

    kwsys::RegularExpression reIfDef("^[ \r\n\t]*#[ \r\n\t]*if");
    kwsys::RegularExpression reElse("^[ \r\n\t]*#[ \r\n\t]*el(se|if)");
    kwsys::RegularExpression reEndif("^[ \r\n\t]*#[ \r\n\t]*endif");

    while ( kwsys::SystemTools::GetLineFromStream(ifs, line) )
      {
      int regex = 0;
      int ifdef_line = 0;
      if ( reIfDef.find(line) )
        {
        in_ifdef ++;
        regex = 1;
        ifdef_line = 1;
        }
      else if ( reElse.find(line) )
        {
        regex = 1;
        }
      else if ( reEndif.find(line) )
        {
        in_ifdef --;
        regex = 1;
        }
      if ( regex )
        {
        this->Stream << "\\n\"" << endl;
        if ( ifdef_line )
          {
          this->CheckSplit(title, file, 1);
          }
        this->Stream << line.c_str() << endl;
        if ( !ifdef_line )
          {
          this->CheckSplit(title, file);
          }
        this->Stream << "\"";
        }
      else
        {
        for ( cc = 0; cc < line.size(); cc ++ )
          {
          ch = line[cc];
          if ( ch == '\\' )
            {
            this->Stream << "\\\\";
            }
          else if ( ch == '\"' )
            {
            this->Stream << "\\\"";
            }
          else
            {
            this->Stream << (unsigned char)ch;
            }
          }
        this->Stream << "\\n\"" << endl;
        if ( !in_ifdef )
          {
          this->CheckSplit(title, file);
          }
        this->Stream << "\"";
        }
      }
    this->Stream << "\\n\";" << endl;
    this->InitStream 
      << "  ostr << ends;" << endl
      << "  this->SetStandard" << title << "String(ostr.str());" << endl
      << "  ostr.rdbuf()->freeze(0);" << endl
      << "  return this->Standard" << title << "String;" << endl
      << "}" << endl << endl;

    return VTK_OK;
    }
};

int main(int argc, char* argv[])
{
  if ( argc < 4 )
    {
    cout << "Usage: " << argv[0] << " <output-file> <input-path> <modules>..." << endl;
    return 1;
    }
  Output ot;
  ot.Stream << "// Loadable modules" << endl
    << "//" << endl
    << "// Generated by " << argv[0] << endl
    << "//" << endl
    << "#ifndef __vtkPVDefaultModules_h" << endl
    << "#define __vtkPVDefaultModules_h" << endl
    << "" << endl
    << "#include \"vtkToolkits.h\"" << endl
    << "#include \"vtkPVConfig.h\"" << endl
    << "" << endl;

  vtkstd::string output = argv[1];
  vtkstd::string input = argv[2];

  int cc;
  for ( cc = 3; cc < argc; cc ++ )
    {
    vtkstd::string fname = input + "/" + argv[cc] + ".xml";

    if ( ot.ProcessFile(fname.c_str(), argv[cc]) != VTK_OK )
      {
      cout << "Problem generating header file from XML file: " << fname.c_str() << endl;
      return 1;
      }
    }
  ot.InitStream << ends;
  ot.Stream << endl << endl << ot.InitStream.str() << endl;
  ot.InitStream.rdbuf()->freeze(0);
  
  ot.Stream << "" << endl
    << "#endif" << endl;
  ot.Stream << ends;
  FILE* fp = fopen(output.c_str(), "w");
  if ( !fp )
    {
    cout << "Cannot open output file: " << output.c_str() << endl;
    return 1;
    }
  fprintf(fp, "%s", ot.Stream.str());
  fclose(fp);
  return 0;
}
