int main(int argc, char *argv[])
{
    int ExitCode = 0;

    std::string			creoStartCommand;
    std::string			proeIsisExtensionsDir;

    std::string			templateFile_PathAndFileName;
    std::stringstream	exceptionErrorStringStream;

    bool Logging_Set_Up = false;

    isis::ProgramInputArguments  programInputArguments;
    ::boost::filesystem::path    workingDir;

    try
    {
        // Parse Input Arguments
        programInputArguments.ParseInputArguments(argc, argv);

        // Setup Boost logging
        SetupLogging(programInputArguments.logFileName, programInputArguments.logVerbosity);

        

        Logging_Set_Up = true;

        isis::ThrowException_If_InvalidInputArguments(argc, argv, programInputArguments);

        // Must get the complete path to the working directory.  This is necessary because when
        // isis_ProDirectoryChange is called to change to a STEP directory, workingDir must be fully
        // defined so that isis_ProDirectoryChange can be called to return to the original directory.
        workingDir = isis::SetupWorkingDirectory(programInputArguments.workingDirectory);

		isis::GlobalModelData::Instance.instanceId = programInputArguments.instanceID;

        // Log CADCreoParametricCreateAssembly version information
        std::string programName_Version_TimeStamp;
        programName_Version_TimeStamp = "CADCreoParametricMetaLink " + std::string(ISIS_PRODUCT_VERSION_WITH_v_AND_DOTS);

        ///////////////////
        // Add Time Stamp
        ///////////////////

        programName_Version_TimeStamp += isis_CADCommon::GetDayMonthTimeYear();
        isis_LOG(lg, isis_FILE, isis_INFO) << programName_Version_TimeStamp;

        isis_LOG(lg, isis_FILE, isis_INFO) << "Notes: " << isis_EOL
                                     << "   1. The \"Component Instance ID\"s in this file equate to ComponentInstanceIDs in CyPhy."  << isis_EOL
                                     << "   2. To map \"Component Instance ID\"s in this file to AVM-IDs, see .\\log\\CyPhy2CAD.log." << isis_EOL;

        time_t time_start; /* calendar time */
        time_start=time(NULL); /* get current cal time */

        // Log input line and parameters
        std::ostringstream inputLine;
        for(int i = 0; i < argc; ++i)
        {
            inputLine << argv[i] << std::string(" ");
        }
        isis_LOG(lg, isis_FILE, isis_INFO) << "Command line: " << inputLine.str();

		isis_LOG(lg, isis_FILE, isis_DEBUG) << "Input arguments (parsed): " << isis_EOL << programInputArguments;

        if(workingDir.generic_string().size() >= PRO_PATH_SIZE)      // PRO_PATH_SIZE   260
        {
            std::stringstream errorString;
            errorString << "WORKING_DIR string too long.  Maximum allowed number of characters: "  << PRO_PATH_SIZE - 1 << " WORKING_DIR string: " << workingDir;
            throw isis::application_exception(errorString);
        }

		isis::SetCreoEnvirVariable_RetrieveSystemSettings(programInputArguments.graphicsModeOn,
                programInputArguments.synchronizeWithCyPhy,
                creoStartCommand,
                proeIsisExtensionsDir,
                templateFile_PathAndFileName);

        std::map<std::string, isis::CADComponentData> CADComponentData_map;
        isis::CADAssemblies CADComponentAssemblies;

		writeConfigProFile(workingDir, programInputArguments);

        /////////////////////////////
        /////// Start Pro/E /////////
        /////////////////////////////

        const char* proeIsisExt = std::getenv("PROE_ISIS_EXTENSIONS");
        char* creoStartChar = const_cast<char*>(creoStartCommand.c_str());

        ::boost::filesystem::current_path(workingDir);
        std::string textPath=std::string(proeIsisExt)+"plugins\\";
        isis::isis_ProEngineerStart(creoStartChar, const_cast<char*>(textPath.c_str()));

        ProTermFuncSet(ProTermAction);

        isis_LOG(lg, isis_FILE, isis_INFO) << "Creo-Parametric successfully started.";

        boost::asio::io_service ios;
        std::string delimiters(":");
        std::vector<std::string> parts;
        boost::split(parts, programInputArguments.syncConnectionString, boost::is_any_of(delimiters));
        std::string host = "127.0.0.1";  // localhost may be subject to firewall and DNS restrictions and not work
        std::string service = "15152";   // it looks like ISIS with a zero

        if(parts.size() > 1)
        {
            host = parts[0];
            service = parts[1];
            isis_LOG(lg, isis_FILE, isis_INFO) << "host: " << host << ", service: " << service;
        }
        else if(parts.size() > 0)
        {
            host = parts[0];
            isis_LOG(lg, isis_FILE, isis_INFO) << "host: " << host << ", service: " << service << "(default)";
        }
        else
        {
            isis_LOG(lg, isis_FILE, isis_INFO) << "host: " << host << "(default), service: " << service << "(default)";
        }

        SetupCreoPlugins();
        SetupCreoSelectPlugin();

        ProNotificationSet(PRO_PARAM_MODIFY_POST, (ProFunction)metaParameterModifyAction);

        isis::cad::CadFactoryAbstract::ptr cad_factory = isis::cad::creo::create();

        isis::MetaLinkAssemblyEditor::Pointer assembler_ptr(new isis::MetaLinkAssemblyEditor(cad_factory, programInputArguments, isis::GlobalModelData::Instance.CadComponentData));

        boost::mutex eventloop_mutex;

        if(programInputArguments.is_passiveMode())
        {
            isis::GlobalModelData::Instance.designId = programInputArguments.designID;
        }

		isis::MetaLinkHandler metalink_handler(assembler_ptr, ios, host, service, programInputArguments.instanceID, programInputArguments.designID, eventloop_mutex);  // pass design ID pass in as argument

        isis::GlobalModelData::Instance.metalink_handler_ptr = &metalink_handler;
        isis::GlobalModelData::Instance.metalinkAssemblyEditorPtr = assembler_ptr;

        metalink_handler.change_m_operator("CAD");

        bool inputFileProcessed = false;

        while(!terminateProcess)
        {
            ProEventProcess();
            if(programInputArguments.inputXmlFileName.size()!=0 && !inputFileProcessed)
            {
                metalink_handler.CreateAssembly(programInputArguments.inputXmlFileName);
                inputFileProcessed = true;
            }
            if(metalink_handler.m_eventQueue.size()>0)
            {
                boost::unique_lock< boost::mutex > guard(eventloop_mutex);
                isis::EditPointer edit = metalink_handler.m_eventQueue.front();
                metalink_handler.m_eventQueue.pop();
                bool result = metalink_handler.processEdit(edit);
                if(result)
                {
                    isis::EditPointer ack(new meta::Edit());
                    ack->set_guid(edit->guid());
                    for(int i = 0; i < edit->topic_size(); i++)
                    {
                        *(ack->add_topic()) = edit->topic(i);
                    }
                    metalink_handler.send(ack);
                }
            }
        }

        metalink_handler.interrupt();
        metalink_handler.disconnect();

        ProEngineerEnd();


    } // END Try
    catch(isis::application_exception& ex)
    {
        exceptionErrorStringStream  << "application error: " << ex.what();
        ExitCode = -1;
    }
    catch(std::exception& ex)
    {
        exceptionErrorStringStream << "general exception: " << ex.what();
        ExitCode = -2;
    }
    catch(...)
    {
        exceptionErrorStringStream << "unspecified throwable (...):  Please report the error to the help desk.";
        ExitCode = -3;
    }

    if(ExitCode != 0)
    {
        // Write to _FAILED.txt
        std::string failedTxtFileName = "_FAILED.txt";
        bool addLineFeed = false;
        if(isis::FileExists(failedTxtFileName.c_str()))
        {
            addLineFeed = true;
        }

        ofstream failedTxtFileStream;
        failedTxtFileStream.open(failedTxtFileName, ios::app);
        if(failedTxtFileStream.is_open())
        {
            if(addLineFeed)
            {
                failedTxtFileStream << std::endl;
            }
            failedTxtFileStream <<  isis_CADCommon::GetDayMonthTimeYear() << ", CADCreoParametricCreateAssembly.exe error code: " << ExitCode << ".  For additional information, scroll to the bottom of " << programInputArguments.logFileName;
            failedTxtFileStream.close();
        }

        if(Logging_Set_Up)
        {
            
            isis_LOG(lg, isis_FILE, isis_ERROR) << exceptionErrorStringStream.str();
        }
        else
        {
            std::cerr << std::endl << std::endl << exceptionErrorStringStream.str() << std::endl << std::endl;
        }

    }


    /*
    ::boost::filesystem::current_path(original_directory);

    // Cleanup - Delete the working dir after execution
    ::boost::system::error_code ec;
    if (isis::GlobalModelData::Instance.mode == isis::DESIGNEDIT)
    {
    	// Remove files one-by-one so if the directory removal fails still something is removed
    	::boost::filesystem::path deleteFile = workingDir / "*";
    	isis_LOG(lg, isis_FILE, isis_DEBUG) << workingDir / "*";
    	if (::boost::filesystem::exists(deleteFile) ) {
    		::boost::filesystem::remove_all(deleteFile, ec);
    		if (ec != 0)
    		{
    			isis_LOG(lg, isis_FILE, isis_ERROR) << "Failed to remove working directory, ec: " << ec;
    		}
    	}
    	deleteFile = workingDir;
    	isis_LOG(lg, isis_FILE, isis_DEBUG) << workingDir;
    	if (::boost::filesystem::exists(deleteFile) ) {
    		::boost::filesystem::remove_all(deleteFile, ec);
    		if (ec != 0)
    		{
    			isis_LOG(lg, isis_FILE, isis_ERROR) << "Failed to remove working directory, ec: " << ec;
    		}
    	}
    } else if (isis::GlobalModelData::Instance.mode == isis::COMPONENTEDIT)
    {
    	::boost::filesystem::path deleteFile = workingDir / "config.pro";
    	isis_LOG(lg, isis_FILE, isis_DEBUG) << workingDir / "config.pro";
    	if (::boost::filesystem::exists(deleteFile) ) {
    		::boost::filesystem::remove_all(deleteFile, ec);
    		if (ec != 0)
    		{
    			isis_LOG(lg, isis_FILE, isis_ERROR) << "Failed to remove working directory, ec: " << ec;
    		}
    	}
    	deleteFile = workingDir / "std.err";
    	isis_LOG(lg, isis_FILE, isis_DEBUG) << workingDir / "std.err";
    	if (::boost::filesystem::exists(deleteFile) ) {
    		::boost::filesystem::remove_all(deleteFile, ec);
    		if (ec != 0)
    		{
    			isis_LOG(lg, isis_FILE, isis_ERROR) << "Failed to remove working directory, ec: " << ec;
    		}
    	}
    	deleteFile = workingDir / "trail.txt.*";
    	isis_LOG(lg, isis_FILE, isis_DEBUG) << workingDir / "trail.txt.*";
    	if (::boost::filesystem::exists(deleteFile) ) {
    		::boost::filesystem::remove_all(deleteFile, ec);
    		if (ec != 0)
    		{
    			isis_LOG(lg, isis_FILE, isis_ERROR) << "Failed to remove working directory, ec: " << ec;
    		}
    	}
    }
    */
  

    exit(ExitCode);
}
int main( int argc, char *argv[] )
{
	::boost::filesystem::path original_directory = ::boost::filesystem::current_path();

	int ExitCode = 0;

	std::string			creoStartCommand;
	std::string			proeIsisExtensionsDir;

	std::string			templateFile_PathAndFileName;
	std::stringstream	exceptionErrorStringStream;
	bool				promptBeforeExiting;

	bool Pro_E_Running = false;
	bool Template_Copied = false;

	bool Logging_Set_Up = false;

	bool logFileOpen = false;

	isis::ProgramInputArguments  programInputArguments;
	::boost::filesystem::path    workingDir;

	try
	{
		bool regenerationSucceeded_ForAllAssemblies = true;

		// Parse Input Arguments
		isis::ParseInputArguments(argc, argv, programInputArguments);
		isis::ThrowExecption_If_InvalidInputArguments(argc, argv, programInputArguments);

		SetupLogging(programInputArguments.logFileName);

		// In case of exception we need to know if log4cpp is available
		Logging_Set_Up = true;

		log4cpp::Category& logcat_fileonly = log4cpp::Category::getInstance(LOGCAT_LOGFILEONLY);


		// Must get the complete path to the working directory.  This is necessary because when
		// isis_ProDirectoryChange is called to change to a STEP directory, workingDir must be fully
		// defined so that isis_ProDirectoryChange can be called to return to the original directory.
		workingDir = isis::SetupWorkingDirectory( programInputArguments.workingDirectory );

		//cADPartsLibDir = programInputArguments.auxiliaryCADDirectory;
		//xmlInputFile_PathAndFileName = programInputArguments.inputXmlFileName;
		promptBeforeExiting = programInputArguments.promptBeforeExiting;

		// Log CADCreoParametricCreateAssembly version information
		std::string programName_Version_TimeStamp;
		programName_Version_TimeStamp = "CADCreoParametricCreateAssembly " + isis::ASSEMBLE_PTC_VERSION + "      ";

		///////////////////
		// Add Time Stamp
		///////////////////

		programName_Version_TimeStamp += isis_CADCommon::GetDayMonthTimeYear();
		
		logcat_fileonly.infoStream() << programName_Version_TimeStamp;

		logcat_fileonly.infoStream() << "";
		logcat_fileonly.infoStream() << "Notes: " << log4cpp::eol
		  << "   1. The \"Component Instance ID\"s in this file equate to ComponentInstanceIDs in CyPhy."  << log4cpp::eol
		  << "   2. To map \"Component Instance ID\"s in this file to AVM-IDs, see .\\log\\CyPhy2CAD.log.";
		
		//return 0;

		time_t time_start; /* calendar time */
		time_start=time(NULL); /* get current cal time */

		// Log input line and parameters
		std::ostringstream inputLine;
		for ( int i = 0; i < argc; ++i) inputLine << argv[i] << std::string(" ");
		logcat_fileonly.infoStream() << "";
		logcat_fileonly.infoStream() 
			<< "************** Begin Input Line *****************" << log4cpp::eol 
		  << inputLine.str() << log4cpp::eol
	      << "************** End Input Line *****************" ;
		  


		if ( workingDir.generic_string().size() >= PRO_PATH_SIZE )  // PRO_PATH_SIZE   260
		{
			std::stringstream errorString;
			errorString << "WORKING_DIR string too long.  Maximum allowed number of characters: "  << PRO_PATH_SIZE - 1 << " WORKING_DIR string: " << workingDir;
					throw isis::application_exception(errorString);
		}
		logcat_fileonly.infoStream() << "";
		logcat_fileonly.infoStream() 
		  <<  log4cpp::eol << "************** Begin Directory Settings *****************"
		  <<  log4cpp::eol << "workingDir:                    "	<< workingDir
		  <<  log4cpp::eol << "inputXmlFileName:              "	<< programInputArguments.inputXmlFileName
		  <<  log4cpp::eol << "logFileName:                   "	<< programInputArguments.logFileName
		  <<  log4cpp::eol << "*************** End Directory Settings *****************";
		 
		bool graphicsModeOn = false;
		bool creoExceptInputFromThisProgramAndCreoUI = false;

		if (programInputArguments.graphicsModeOn) graphicsModeOn = true;
		if (programInputArguments.synchronizeWithCyPhy) creoExceptInputFromThisProgramAndCreoUI = true;

		isis::SetCreoEnvirVariable_RetrieveSystemSettings(	graphicsModeOn,
															creoExceptInputFromThisProgramAndCreoUI,
															creoStartCommand,
															proeIsisExtensionsDir,
															templateFile_PathAndFileName );

		std::map<std::string, isis::CADComponentData> CADComponentData_map;
		isis::CADAssemblies CADComponentAssemblies;


		   unsigned int UniqueNameIndex = 1;

		   isis::CreateAssemblyViaInputFile(	programInputArguments,
												proeIsisExtensionsDir,
												programInputArguments.inputXmlFileName,
												workingDir.generic_string(),
												programInputArguments.auxiliaryCADDirectory,
												programInputArguments.logFileName,
												templateFile_PathAndFileName,
												creoStartCommand,
												programName_Version_TimeStamp,
												UniqueNameIndex,
												Pro_E_Running,
												CADComponentAssemblies,
												CADComponentData_map);

		 


	} // END Try
    catch ( isis::application_exception& ex )
	{
		exceptionErrorStringStream  << std::endl << "application error: " << ex.what();
		ExitCode = -1;
	}
	catch ( std::exception& ex )
	{
		exceptionErrorStringStream << std::endl  << "general exception: " << ex.what();
		ExitCode = -2;
	}
	catch ( ... )
	{
		exceptionErrorStringStream << "unspecified throwable (...):  Please report the error to the help desk.";
		ExitCode = -3;
	}

	if ( ExitCode != 0 )
	{
		// Write to _FAILED.txt
		std::string failedTxtFileName = "_FAILED.txt";
		bool addLineFeed = false;
		if ( isis::FileExists( failedTxtFileName.c_str() )) addLineFeed = true;

		ofstream failedTxtFileStream;
		failedTxtFileStream.open (failedTxtFileName, ios::app );
		if ( failedTxtFileStream.is_open() )
		{
			if ( addLineFeed ) failedTxtFileStream << std::endl;
			failedTxtFileStream <<  isis_CADCommon::GetDayMonthTimeYear() << ", CADCreoParametricCreateAssembly.exe error code: " << ExitCode << ". Error was: " << exceptionErrorStringStream.str();
			failedTxtFileStream << std::endl;
			failedTxtFileStream.close();
		}

		if (Logging_Set_Up)
		{
			log4cpp::Category& logcat_consoleandfile = log4cpp::Category::getInstance(LOGCAT_CONSOLEANDLOGFILE);
			logcat_consoleandfile.errorStream() << exceptionErrorStringStream.str();
		}
		else
		{
			std::cerr <<  std::endl << std::endl << exceptionErrorStringStream.str() << std::endl << std::endl;
		}	
	}

	// Delete the copied template assembly file if it exists.
	// Note - Added "if ( Template_Copied )" because the function call was returning a message to the
	// console if the file did not exist.
	if ( Template_Copied )
	{
		::boost::filesystem::path deleteFile = workingDir / (isis::TEMPLATE_MODEL_NAME_METRIC + isis::TEMPLATE_MODEL_NAME_METRIC_SUFFIX);
		if (::boost::filesystem::exists(deleteFile) ) 
		{
		   ::boost::filesystem::remove(deleteFile);
		}
		// isis::IfFileExists_DeleteFile( deleteFile.generic_string() );
	}

	/////// Stop Pro/E /////////
	if (Pro_E_Running) ProEngineerEnd();

	if ( promptBeforeExiting )
	{
		printf("\nType Enter to exit.");
		getc(stdin);
	}

    ::boost::filesystem::current_path(original_directory);

	log4cpp::Category::shutdown();

	exit(ExitCode);
}