// **********************************************************************
//
// Copyright (c) 1999
// Object Oriented Concepts, Inc.
// Billerica, MA, USA
//
// All Rights Reserved
//
// **********************************************************************

#include <OB/CORBA.h>
#include <Types.h>
#include <Util.h>
#include <GenUtil.h>
#include <GenCPP.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

#ifdef HAVE_FSTREAM
#   include <fstream>
#else
#   include <fstream.h>
#endif

//
// The header
//
const char* IdlCPPGenerator::header_ =
"// **********************************************************************\n"
"//\n"
"// Generated by the ORBacus IDL-to-C++ Translator\n"
"//\n"
"// Copyright (c) 1999\n"
"// Object Oriented Concepts, Inc.\n"
"// Billerica, MA, USA\n"
"//\n"
"// All Rights Reserved\n"
"//\n"
"// **********************************************************************\n";

// ----------------------------------------------------------------------
// Version check
// ----------------------------------------------------------------------

static void
checkVersion(ostream& s)
{
    s << '\n';
    s << "\n#ifndef OB_INTEGER_VERSION";
    s << "\n#   error No ORBacus version defined!";
    s << "\n#endif";
    s << '\n';
    s << "\n#ifndef OB_NO_VERSION_CHECK";
    s << "\n#   if (OB_INTEGER_VERSION != " << OB_INTEGER_VERSION << "L)";
    s << "\n#       error ORBacus version mismatch!";
    s << "\n#   endif";
    s << "\n#endif";
}

// ----------------------------------------------------------------------
// C++ code generator constructor and destructor
// ----------------------------------------------------------------------

IdlCPPGenerator::IdlCPPGenerator(const char* prog,
				 CORBA_Repository_ptr rep,
				 IdlStringSeq*& fileNameSeq,
				 bool noTC,
				 bool local,
				 bool noVirtual,
				 const char* headerDir,
				 const char* otherHeaderDir,
				 const char* dllImport,
				 const char* hSuff,
				 const char* cSuff)
    : prog_(prog),
      repository_(CORBA_Repository::_duplicate(rep)),
      fileNameSeq_(fileNameSeq),
      partial_(false),
      noTC_(noTC),
      local_(local),
      noVirtual_(noVirtual),
      headerDir_(headerDir),
      otherHeaderDir_(otherHeaderDir),
      dllImport_(dllImport),
      hSuff_(hSuff),
      cSuff_(cSuff)
{
    if(local_)
	noTC_ = true;
}

IdlCPPGenerator::IdlCPPGenerator(const char* prog,
				 CORBA_Repository_ptr rep,
				 const CORBA_RepositoryIdSeq& idSeq,
				 const IdlStringSeq& includeSeq,
				 IdlStringSeq*& fileNameSeq,
				 bool noTC,
				 bool local,
				 bool noVirtual,
				 const char* headerDir,
				 const char* otherHeaderDir,
				 const char* dllImport,
				 const char* hSuff,
				 const char* cSuff)
    : prog_(prog),
      repository_(CORBA_Repository::_duplicate(rep)),
      idSeq_(idSeq),
      includeSeq_(includeSeq),
      fileNameSeq_(fileNameSeq),
      partial_(true),
      noTC_(noTC),
      local_(local),
      noVirtual_(noVirtual),
      headerDir_(headerDir),
      otherHeaderDir_(otherHeaderDir),
      dllImport_(dllImport),
      hSuff_(hSuff),
      cSuff_(cSuff)
{
    if(local_)
	noTC_ = true;
}

// ----------------------------------------------------------------------
// Generate C++ code
// ----------------------------------------------------------------------

int
IdlCPPGenerator::gen(const char* dir, const char* name)
{
    //
    // Open output files
    //
    CORBA_String_var outputFileH = dir;
    outputFileH += name;
    CORBA_String_var outputFileC = dir;
    outputFileC += name;
    
    outputFileH += hSuff_;
    outputFileC += cSuff_;
    
    ofstream outH(outputFileH);
    if(!outH.good())
    {
	cerr << prog_ << ": can't open \"" << outputFileH
	     << "\": " << strerror(errno) << endl;
	
	return 1;
    }

    fileNameSeq_ -> append(outputFileH);

    ofstream outC(outputFileC);
    if(!outC.good())
    {
	outH.close();

	cerr << prog_ << ": can't open \"" << outputFileC
	     << "\": " << strerror(errno) << endl;
	
	return 1;
    }

    fileNameSeq_ -> append(outputFileC);

    //
    // Print headers
    //
    CORBA_String_var macro = IdlPathToIdent(outputFileH);
    
    outH << header_;
    outH << "\n// Version: " << OBVersion;
    outH << "\n// License: " << OBLicense;
    outH << '\n';
    outH << "\n#ifndef __" << macro << "__";
    outH << "\n#define __" << macro << "__";

    for(CORBA_ULong i = 0 ; i < includeSeq_.length() ; i++)
    {
	if(i == 0)
	    outH << '\n';
	
	outH << "\n#include <";
	if(strlen(otherHeaderDir_) > 0)
	    outH << otherHeaderDir_ << '/';
	else if(strlen(headerDir_) > 0)
	    outH << headerDir_ << '/';
	outH << includeSeq_[i] << hSuff_ << '>';
    }
    
    outC << header_;
    outC << "\n// Version: " << OBVersion;
    outC << "\n// License: " << OBLicense;
    outC << '\n';
    outC << "\n#include <OB/CORBA.h>";
    outC << "\n#include <OB/TemplateI.h>";
    outC << "\n#include <";
    if(strlen(headerDir_) > 0)
	outC << headerDir_ << '/';
    outC << name << hSuff_ << '>';

    //
    // Clear anonymous types
    //
    anonTCSeq_.length(0);

    //
    // Generate code to check version
    //
    checkVersion(outH);
    checkVersion(outC);
    
    //
    // Generate code
    //
    IdlPrettyPrint ppH(outH);
    IdlPrettyPrint ppC(outC);

    genCPlusPlusH(repository_, ppH);
    genCPlusPlusHGlobalNamespace(repository_, ppH);
    genCPlusPlusHAnonTypes(ppH, macro);
    genCPlusPlusC(repository_, ppC);
    genCPlusPlusCAnonTypes(ppC, macro);
    
    //
    // That's it
    //
    outH << '\n';
    outH << "\n#endif";
    outH << '\n';
    outC << '\n';

    return 0;
}

int
IdlCPPGenerator::genSkel(const char* dir, const char* name)
{
    if(local_)
	return 0;

    //
    // Open output files
    //
    CORBA_String_var outputFileH = dir;
    outputFileH += name;
    CORBA_String_var outputFileSkelH = dir;
    outputFileSkelH += name;
    CORBA_String_var outputFileSkelC = dir;
    outputFileSkelC += name;
     
    outputFileSkelH += "_skel";
    outputFileSkelC += "_skel";

    outputFileH += hSuff_;
    outputFileSkelH += hSuff_;
    outputFileSkelC += cSuff_;
    
    ofstream outSkelH(outputFileSkelH);
    if(!outSkelH.good())
    {
	cerr << prog_ << ": can't open \"" << outputFileSkelH
	     << "\": " << strerror(errno) << endl;
	
	return 1;
    }
    
    fileNameSeq_ -> append(outputFileSkelH);

    ofstream outSkelC(outputFileSkelC);
    if(!outSkelC.good())
    {
	outSkelH.close();

	cerr << prog_ << ": can't open \"" << outputFileSkelC
	     << "\": " << strerror(errno) << endl;
	
	return 1;
    }

    fileNameSeq_ -> append(outputFileSkelC);

    //
    // Print headers
    //
    CORBA_String_var macroSkel = IdlPathToIdent(outputFileSkelH);

    outSkelH << header_;
    outSkelH << "\n// Version: " << OBVersion;
    outSkelH << "\n// License: " << OBLicense;
    outSkelH << '\n';
    outSkelH << "\n#ifndef __" << macroSkel << "__";
    outSkelH << "\n#define __" << macroSkel << "__";
    outSkelH << '\n';
    outSkelH << "\n#include <";
    if(strlen(headerDir_) > 0)
	outSkelH << headerDir_ << '/';
    outSkelH << name << hSuff_ << '>';
    
    for(CORBA_ULong i = 0 ; i < includeSeq_.length() ; i++)
    {
	if(i == 0)
	    outSkelH << '\n';
	
	outSkelH << "\n#include <";
	if(strlen(otherHeaderDir_) > 0)
	    outSkelH << otherHeaderDir_ << '/';
	else if(strlen(headerDir_) > 0)
	    outSkelH << headerDir_ << '/';
	outSkelH << includeSeq_[i] << "_skel" << hSuff_ << '>';
    }

    outSkelC << header_;
    outSkelC << "\n// Version: " << OBVersion;
    outSkelC << "\n// License: " << OBLicense;
    outSkelC << '\n';
    outSkelC << "\n#include <OB/CORBA.h>";
    outSkelC << "\n#include <";
    if(strlen(headerDir_) > 0)
	outSkelC << headerDir_ << '/';
    outSkelC << name << "_skel" << hSuff_ << '>';
    
    //
    // Generate code to check version
    //
    checkVersion(outSkelH);
    checkVersion(outSkelC);
    
    //
    // Generate code
    //
    IdlPrettyPrint ppSkelH(outSkelH);
    IdlPrettyPrint ppSkelC(outSkelC);

    genCPlusPlusSkelH(repository_, ppSkelH);
    genCPlusPlusSkelC(repository_, ppSkelC);
    
    //
    // That's it
    //
    outSkelH << '\n';
    outSkelH << "\n#endif";
    outSkelH << '\n';
    outSkelC << '\n';

    return 0;
}

int
IdlCPPGenerator::genTie(const char* dir, const char* name)
{
    if(local_)
	return 0;

    //
    // Open output files
    //
    CORBA_String_var outputFileSkelH = dir;
    outputFileSkelH += name;
    CORBA_String_var outputFileTieH = dir;
    outputFileTieH += name;
     
    outputFileSkelH += "_skel";
    outputFileTieH += "_skel_tie";

    outputFileSkelH += hSuff_;
    outputFileTieH += hSuff_;
    
    ofstream outTieH(outputFileTieH);
    if(!outTieH.good())
    {
	cerr << prog_ << ": can't open \"" << outputFileTieH
	     << "\": " << strerror(errno) << endl;
	
	return 1;
    }
    
    fileNameSeq_ -> append(outputFileTieH);

    //
    // Print headers
    //
    CORBA_String_var macroTie = IdlPathToIdent(outputFileTieH);

    outTieH << header_;
    outTieH << "\n// Version: " << OBVersion;
    outTieH << "\n// License: " << OBLicense;
    outTieH << '\n';
    outTieH << "\n#ifndef __" << macroTie << "__";
    outTieH << "\n#define __" << macroTie << "__";
    outTieH << '\n';
    outTieH << "\n#include <";
    if(strlen(headerDir_) > 0)
	outTieH << headerDir_ << '/';
    outTieH << name << "_skel" << hSuff_ << '>';
    
    //
    // Generate code to check version
    //
    checkVersion(outTieH);
    
    //
    // Generate code
    //
    IdlPrettyPrint ppTieH(outTieH);

    genCPlusPlusTieH(repository_, ppTieH);
    
    //
    // That's it
    //
    outTieH << '\n';
    outTieH << "\n#endif";
    outTieH << '\n';

    return 0;
}
