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

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

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

#include <stdlib.h>
#include <stdio.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"
	"-i, --ior              Print repository IOR on standard output.\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"
	;
}

int
run(int argc, char* argv[])
{
    //
    // Set program name
    //
    const char* progName = argv[0];

    //
    // Create the ORB
    //
    CORBA_ORB_var orb = CORBA_ORB_init(argc, argv);

    //
    // Resolve the service object, if available as an initial reference.
    //
    const char* service = "InterfaceRepository";
    CORBA_Object_var serviceObject;
    try
    {
        serviceObject = orb -> resolve_initial_references(service);
    }
    catch(CORBA_ORB::InvalidName&)
    {
    }

    //
    // Set the IIOP port based on the port defined in the IIOP profile
    // of the service object (if it exists). Then create the BOA.
    //
    CORBA_BOA::set_iiop_port_from_object(serviceObject);
    CORBA_BOA_var boa = orb -> BOA_init(argc, argv);
    
    //
    // 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";
    }
    else
	cppCmd = CORBA_string_dup("idlcpp");

    //
    // Get options
    //
    CORBA_String_var cppArgs;
    bool ior = false;

    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(strcmp(argv[i], "--ior") == 0 ||
		strcmp(argv[i], "-i") == 0)
	{
	    ior = true;
	}
	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
	{
	    cerr << progName << ": unknown option `"
		 << argv[i] << "'\n" << endl;
	    usage(progName);
	    return 1;
	}
    }
    
    //
    // Create implementation object
    //
    CORBA_Repository_impl* impl = new CORBA_Repository_impl(orb);
    orb -> connect(impl, "DefaultRepository");
    CORBA_Repository_var repository = impl;

    //
    // Check for C++ proprocessor
    //
    if(!IdlCheckForCPP(progName, cppCmd))
	return 1;

    int status = 0;
    while(status == 0 && i < argc)
    {
	CORBA_String_var file = CORBA_string_dup(argv[i++]);

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

	if((yyin = popen(cmd, "r")) == NULL)
	    return 1;

	//
	// 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);
    }

    //
    // Ensure that the acceptors are running before we display the
    // IOR.
    //
    if(status == 0)
    {
	if(boa -> conc_model() == CORBA_BOA::ConcModelBlocking)
	{
	    boa -> create_new_servers();
	    OCI_AccRegistry_var registry = boa -> get_acc_registry();
	    OCI_AcceptorSeq_var acceptors = registry -> get_acceptors();
	    for(CORBA_ULong i = 0 ; i < acceptors -> length() ; i++)
		if(acceptors[i] -> tag() == IOP_TAG_INTERNET_IOP)
		    acceptors[i] -> listen();
	}
	else
	{
	    boa -> init_servers();
	}
    }

    //
    // Print IOR on standard output
    //
    if(status == 0 && ior)
    {
	CORBA_String_var s = orb -> object_to_string(repository);
	cout << s << endl;
    }

    //
    // Run implementation
    //
    if(status == 0)
	boa -> impl_is_ready(CORBA_ImplementationDef::_nil());
    
    //
    // Release internal object references to resolve
    // cyclic dependencies
    //
    impl -> releaseInternal();

    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;
}
