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

#include <OB/CORBA.h>
#include <OB/Util.h>

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

#include <Types.h>
#include <GenUtil.h>
#include <GenJava.h>
#include <IntRep_impl.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

#ifdef WIN32
#   define popen _popen
#   define pclose _pclose
#endif

static void
usage(const char* progName)
{
    cerr << "Usage:\n";
    cerr << progName << " [options] idl-files ...\n"
	"\n"
	"Options:\n"
	"-h, --help               Show this message.\n"
	"-v, --version            Show ORBacus version.\n"
	"-e, --cpp NAME           Use NAME as preprocessor.\n"
	"-d, --debug              Print diagnostic messages.\n"
	"-DNAME                   Define NAME as 1.\n"
	"-DNAME=DEF               Define NAME as DEF.\n"
	"-UNAME                   Remove any definition for NAME.\n"
	"-IDIR                    Put DIR in the include file search path.\n"
	"--no-skeletons           Don't generate skeletons.\n"
	"--no-comments            Don't add comments to generated code.\n"
	"--locality-constrained   Create locality-constrained objects.\n"
	"--tie                    Generate tie classes.\n"
	"--clone                  Generate clone methods for data types.\n"
	"--all                    Generate code for included files.\n"
	"--package PKG            Generate everything relative to the\n"
	"                         package PKG.\n"
	"--prefix-package PRE PKG Generate code for the prefix PRE\n"
	"                         relative to the package PKG.\n"
	"--auto-package           Derive package names from prefixes.\n"
	"--output-dir DIR         Write .java files to directory DIR.\n"
	"--file-list FILE         Write a list of generated files to FILE.\n"
	;
}

int
run(int argc, char* argv[])
{
    //
    // Create ORB
    //
    CORBA_ORB_var orb = CORBA_ORB_init(argc, argv);

    //
    // Set program name
    //
    const char* progName = argv[0];

    //
    // Get the C preprocessor
    //
    CORBA_String_var cppCmd = CORBA_string_dup(progName);
#ifdef WIN32
    char* p1 = strrchr(cppCmd.inout(), '\\');
    char* p2 = strrchr(cppCmd.inout(), '/');
    char* p = p1 > p2 ? p1 : p2;
#else
    char* p = strrchr(cppCmd.inout(), '/');
#endif
    if(p)
    {
	p++;
	*p = '\0';
	cppCmd += "idlcpp -C";
    }
    else
	cppCmd = CORBA_string_dup("idlcpp -C");

    //
    // Get options
    //
    CORBA_String_var cppArgs;
    bool partial = true;
    bool local = false;
    bool tie = false;
    bool clone = false;
    bool noSkel = false;
    bool noComments = false;
    bool autoPackage = false;
    CORBA_String_var package = CORBA_string_dup("");
    CORBA_String_var outDir = CORBA_string_dup("");
    CORBA_String_var fileList = CORBA_string_dup("");
    IdlPrefixPackageSeq prefixPackageSeq;

    CORBA_Long i;
    for(i = 1 ; i < argc && *argv[i] == '-' ; i++)
    {
	if(strcmp(argv[i], "--help") == 0 ||
	   strcmp(argv[i], "-h") == 0)
	{
	    usage(progName);
	    return 0;
	}
	else if(strcmp(argv[i], "--version") == 0 ||
		strcmp(argv[i], "-v") == 0)
	{
	    cerr << "ORBacus " << OBVersion << endl;
	    return 0;
	}
	else if(strcmp(argv[i], "--cpp") == 0 ||
		strcmp(argv[i], "-e") == 0)
	{
	    if(++i < argc)
		cppCmd = CORBA_string_dup(argv[i]);
	    else
	    {
		cerr << progName << ": argument expected for `"
		     << argv[i - 1] << "'" << endl;
		return 1;
	    }
	}
	else if(strcmp(argv[i], "--debug") == 0 ||
		strcmp(argv[i], "-d") == 0)
	{
	    extern int yydebug;
	    yydebug = 1;
	}
	else if(strncmp(argv[i], "-D", 2) == 0 ||
		strncmp(argv[i], "-U", 2) == 0 ||
		strncmp(argv[i], "-I", 2) == 0)
	{
	    if(strlen(argv[i]) == 2)
	    {
		cerr << progName << ": argument expected for `"
		     << argv[i] << "'" << endl; // not argv[i - 1] !
		return 1;
	    }

	    cppArgs += ' ';
	    cppArgs += argv[i];
	}
	else if(strcmp(argv[i], "--no-skeletons") == 0)
	{
	    noSkel = true;
	}
	else if(strcmp(argv[i], "--no-comments") == 0)
	{
	    noComments = true;
	}
	else if(strcmp(argv[i], "--locality-constrained") == 0)
	{
	    local = true;
	}
	else if(strcmp(argv[i], "--tie") == 0)
	{
	    tie = true;
	}
	else if(strcmp(argv[i], "--clone") == 0)
	{
	    clone = true;
	}
	else if(strcmp(argv[i], "--all") == 0)
	{
	    partial = false;
	}
	else if(strcmp(argv[i], "--auto-package") == 0)
	{
	    autoPackage = true;
	}
	else if(strcmp(argv[i], "--package") == 0)
	{
	    if(++i < argc)
		package = CORBA_string_dup(argv[i]);
	    else
	    {
		cerr << progName << ": argument expected for `"
		     << argv[i - 1] << "'" << endl;
		return 1;
	    }
	}
	else if(strcmp(argv[i], "--prefix-package") == 0)
	{
	    i += 2;
	    if(i < argc)
	    {
		IdlPrefixPackage prefixPackage;

		prefixPackage.prefix = CORBA_string_dup("IDL:");
		prefixPackage.prefix += argv[i - 1];
		prefixPackage.package = CORBA_string_dup(argv[i]);
		prefixPackageSeq.append(prefixPackage);
	    }
	    else
	    {
		cerr << progName << ": two arguments expected for `"
		     << argv[i - 2] << "'" << endl;
		return 1;
	    }
	}
	else if(strcmp(argv[i], "--output-dir") == 0)
	{
	    if(++i < argc)
		outDir = CORBA_string_dup(argv[i]);
	    else
	    {
		cerr << progName << ": argument expected for `"
		     << argv[i - 1] << "'" << endl;
		return 1;
	    }
	}
	else if(strcmp(argv[i], "--file-list") == 0)
	{
	    if(++i < argc)
		fileList = CORBA_string_dup(argv[i]);
	    else
	    {
		cerr << progName << ": argument expected for `"
		     << argv[i - 1] << "'" << endl;
		return 1;
	    }
	}
	else
	{
	    cerr << progName << ": unknown option `"
		 << argv[i] << "'\n" << endl;
	    usage(progName);
	    return 1;
	}
    }

    if(i == argc)
    {
	cerr << progName << ": no input file" << endl;
	usage(progName);
	return 1;
    }

    //
    // Open file for generated file list
    //
    ofstream out;
    if(strlen(fileList))
    {
	out.open(fileList);
	if(!out.good())
	{
	    cerr << progName << ": can't open \"" << fileList
		 << "\": " << strerror(errno) << endl;
	    return 1;
	}
    }

    int status = 0;
    while(status == 0 && i < argc)
    {
	CORBA_String_var file = CORBA_string_dup(argv[i++]);
	
	//
	// Remove .idl or .IDL from idl file name
	//
	CORBA_String_var base = file;
	const char* suffix = strrchr(base, '.');
	if(suffix)
	{
	    if(strcmp(suffix, ".idl") == 0 || strcmp(suffix, ".IDL") == 0)
	    {
		char* newBase = CORBA_string_alloc(suffix - (const char*)base);
		strncpy(newBase, base, suffix - (const char*)base);
		newBase[suffix - (const char*)base] = '\0';
		base = newBase;
	    }
	}

	//
	// Check for input file
	//
	struct stat statBuf;
	if(stat(file, &statBuf) == -1)
	{
	    if(strlen(fileList))
		out.close();

	    cerr << progName << ": couldn't open " << file << endl;
	    return 1;
	}

	//
	// Check for C++ proprocessor
	//
	if(!IdlCheckForCPP(progName, cppCmd))
	{
	    if(strlen(fileList))
		out.close();

	    return 1;
	}

	//
	// Open input file
	//
	extern FILE* yyin;
	CORBA_String_var cmd = cppCmd;
	cmd += cppArgs;
	cmd += ' ';
	cmd += file;

	if((yyin = popen(cmd, "r")) == NULL)
	{
	    if(strlen(fileList))
		out.close();

	    return 1;
	}

	//
	// Create implementation object
	//
	CORBA_Repository_impl* repository = new CORBA_Repository_impl(orb);
	
	//
	// Do parsing
	//
	int IdlParse(CORBA_ORB_ptr, CORBA_Repository_ptr,
		     CORBA_RepositoryIdSeq*&, IdlStringSeq*&, IdlCommentSeq*&);
	CORBA_RepositoryIdSeq_var idSeq; // This is an out parameter
	IdlStringSeq_var includeSeq; // This is an out paramter
	IdlCommentSeq_var commentSeq; // This is an out paramter
	status = IdlParse(orb, repository,
			  idSeq.out(), includeSeq.out(), commentSeq.out());
	
	//
	// Close input file
	//
	pclose(yyin);
	
	if(status == 0)
	{
	    IdlStringSeq_var fileNameSeq = new IdlStringSeq;

	    if(partial)
	    {
		IdlJavaGenerator generator(progName, repository, idSeq,
					   commentSeq, prefixPackageSeq,
					   fileNameSeq,
					   package, autoPackage,
					   noComments, local, clone, outDir);

		status = generator.gen();
		
		if(status == 0 && !noSkel)
		    generator.genSkel();
		
		if(status == 0 && tie)
		    generator.genTie();
	    }
	    else
	    {
		IdlJavaGenerator generator(progName, repository,
					   commentSeq, prefixPackageSeq,
					   fileNameSeq,
					   package, autoPackage,
					   noComments, local, clone, outDir);

		status = generator.gen();
		
		if(status == 0 && !noSkel)
		    generator.genSkel();
		
		if(status == 0 && tie)
		    generator.genTie();
	    }

	    if(status == 0 && strlen(fileList))
	    {
		for(CORBA_ULong i = 0 ; i < fileNameSeq -> length() ; i++)
		    out << fileNameSeq[i] << endl;
	    }
	}
	
	//
	// Release internal object references to resolve
	// cyclic dependencies
	//
	repository -> releaseInternal();
	
	//
	// Release repository
	//
	CORBA_release(repository);
    }

    if(strlen(fileList))
	out.close();

    return status;
}

int
main(int argc, char* argv[], char*[])
{
    int status;

    try
    {
        //
	// Generate code
	//
	status = run(argc, argv);
    }
    catch(CORBA_SystemException& ex)
    {
	OBPrintException(ex);
	status = 1;
    }

    return status;
}
