// **********************************************************************
//
// 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 <GenRTF.h>
#include <IntRep_impl.h>

#include <sys/types.h>
#include <sys/stat.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"
	"-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-sort                 Don't sort symbols alphabetically.\n"
	"--ignore-case             Sort case-insensitive.\n"
	"--output-dir DIR          Write .rtf files to directory DIR.\n"
	"--single-file FILE        Create a single .rtf file called FILE.\n"
	"--with-index              Create index entries.\n"
	"--font NAME               Use font NAME as font for the text body.\n"
	"--literal-font NAME       Use font NAME as font for literals.\n"
	"--title-font NAME         Use font NAME as font for the title.\n"
	"--heading-font NAME       Use font NAME as font for headings.\n"
	"--font-size SIZE          Text body font size in points.\n"
	"--literal-font-size SIZE  Literal font size in points.\n"
	"--title-font-size SIZE    Title font size in points.\n"
	"--heading-font-size SIZE  Heading font size in points.\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 noSort = false;
    bool ignoreCase = false;
    bool withIndex = false;
    CORBA_String_var outDir = CORBA_string_dup("");
    CORBA_String_var singleFile = CORBA_string_dup("");
    CORBA_String_var bodyFont = CORBA_string_dup("roman Times New Roman");
    CORBA_String_var literalFont = CORBA_string_dup("modern Courier New");
    CORBA_String_var titleFont = CORBA_string_dup("");
    CORBA_String_var headingFont = CORBA_string_dup("");
    unsigned bodySize = 10;
    unsigned int literalSize = 0;
    unsigned int titleSize = 0;
    unsigned int headingSize = 0;

    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-sort") == 0)
        {
            noSort = true;
        }
        else if(strcmp(argv[i], "--ignore-case") == 0)
        {
            ignoreCase = true;
        }
        else if(strcmp(argv[i], "--with-index") == 0)
        {
            withIndex = true;
        }
	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], "--single-file") == 0)
	{
	    if(++i < argc)
		singleFile = CORBA_string_dup(argv[i]);
	    else
	    {
		cerr << progName << ": argument expected for `"
		     << argv[i - 1] << "'" << endl;
		return 1;
	    }
	}
	else if(strcmp(argv[i], "--font") == 0)
	{
	    if(++i < argc)
		bodyFont = CORBA_string_dup(argv[i]);
	    else
	    {
		cerr << progName << ": argument expected for `"
		     << argv[i - 1] << "'" << endl;
		return 1;
	    }
	}
	else if(strcmp(argv[i], "--literal-font") == 0)
	{
	    if(++i < argc)
		literalFont = CORBA_string_dup(argv[i]);
	    else
	    {
		cerr << progName << ": argument expected for `"
		     << argv[i - 1] << "'" << endl;
		return 1;
	    }
	}
	else if(strcmp(argv[i], "--title-font") == 0)
	{
	    if(++i < argc)
		titleFont = CORBA_string_dup(argv[i]);
	    else
	    {
		cerr << progName << ": argument expected for `"
		     << argv[i - 1] << "'" << endl;
		return 1;
	    }
	}
	else if(strcmp(argv[i], "--heading-font") == 0)
	{
	    if(++i < argc)
		headingFont = CORBA_string_dup(argv[i]);
	    else
	    {
		cerr << progName << ": argument expected for `"
		     << argv[i - 1] << "'" << endl;
		return 1;
	    }
	}
	else if(strcmp(argv[i], "--font-size") == 0)
	{
	    if(++i < argc)
		bodySize = atoi(argv[i]);
	    else
	    {
		cerr << progName << ": argument expected for `"
		     << argv[i - 1] << "'" << endl;
		return 1;
	    }
	}
	else if(strcmp(argv[i], "--literal-font-size") == 0)
	{
	    if(++i < argc)
		literalSize = atoi(argv[i]);
	    else
	    {
		cerr << progName << ": argument expected for `"
		     << argv[i - 1] << "'" << endl;
		return 1;
	    }
	}
	else if(strcmp(argv[i], "--title-font-size") == 0)
	{
	    if(++i < argc)
		titleSize = atoi(argv[i]);
	    else
	    {
		cerr << progName << ": argument expected for `"
		     << argv[i - 1] << "'" << endl;
		return 1;
	    }
	}
	else if(strcmp(argv[i], "--heading-font-size") == 0)
	{
	    if(++i < argc)
		headingSize = atoi(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;
    }

    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)
	{
	    cerr << progName << ": couldn't open " << file << endl;
	    return 1;
	}

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

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

	if((yyin = popen(cmd, "r")) == NULL)
	    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)
	{
	    //
	    // Check font defaults
	    //
	    if(!strlen(titleFont))
		titleFont = bodyFont;
	    if(!strlen(headingFont))
		headingFont = bodyFont;
	    if(!literalSize)
		literalSize = bodySize - 1;
	    if(!titleSize)
		titleSize = bodySize + 4;
	    if(!headingSize)
		headingSize = bodySize + 2;

	    //
	    // Convert points to RTF half-points
	    //
	    bodySize *= 2;
	    literalSize *= 2;
	    titleSize *= 2;
	    headingSize *= 2;

	    //
	    // Generate RTF code
	    //
#ifdef WIN32
	    const char* name1 = strrchr(base, '\\');
	    const char* name2 = strrchr(base, '/');
	    const char* name = name1 > name2 ? name1 : name2;
#else
	    const char* name = strrchr(base, '/');
#endif
	    if(name)
		name++;
	    else
		name = base;

	    IdlRTFGenerator generator(progName, repository, commentSeq, outDir,
				      singleFile, noSort, ignoreCase,
				      withIndex, bodyFont,
				      literalFont, titleFont, headingFont,
				      bodySize, literalSize, titleSize,
				      headingSize);
	    generator.gen();
	}
	
	//
	// Release internal object references to resolve
	// cyclic dependencies
	//
	repository -> releaseInternal();
	
	//
	// Release repository
	//
	CORBA_release(repository);
    }

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