/*
	Copyright (c) 2024 PTC Inc. and/or Its Subsidiary Companies. All Rights Reserved.
*/


/*------------------------------------------------------------------*\
Pro/TOOLKIT includes
\*------------------------------------------------------------------*/
#include "ProToolkit.h"
#include "ProCore.h"
#include "ProMenu.h"
#include "ProMessage.h"
#include "ProMenuBar.h"
#include "ProMdl.h"
#include "ProNotify.h"
#include "ProUtil.h"
#include "ProToolkitDll.h"
#include "ProTKRunTime.h"


/*------------------------------------------------------------------*\
Application includes
\*------------------------------------------------------------------*/
#include "TestError.h"
#include "UtilString.h"
#include "UtilFiles.h"

#include <stdlib.h>
#include <time.h>

/*  STATICS  */
static int event = 0;
static int Action_done = 1;
static ProFileName msgfil;
static ProProcessHandle proe_handle;
static int connect_mode = 0;


void UserTerminateAction  PRO_PROTO ((ProeTerminationStatus term_type));
int UserActionFunc   PRO_PROTO (( ProAppData ptr, int opt));
int UserRestartProeTest (char*, char*, int, char*);
int UserTerm ();
int ProTestShowMenu ();
ProError UserNotificationFunc();

#define USER_RETRIEVE_ACTION    1
#define USER_MESSAGE_ACTION     2
#define USER_EXIT_ACTION        3

#define MSGFIL msgfil

/*====================================================================*\
    FUNCTION :	TestAccessDefault()
    PURPOSE  :   enable access to the -Main button	
\*====================================================================*/
static uiCmdAccessState TestAccessDefault(uiCmdAccessMode access_mode)
{
    return (ACCESS_AVAILABLE);
}

static int PTAsyncMain (int argc, char*argv[]);

#ifdef PT_ASYNC_MD

/*============================================================================*\
  Function : InvokePTAsyncByDll
  Purpose  : Demonstrate the asynchronous mode of operations invoked from an
				external DLL
\*============================================================================*/
__declspec (dllexport) int InvokePTAsyncMDByDll(int argc, char *argv[])
{
	return PTAsyncMain (argc, argv);
}

#else

/*============================================================================*\
  Function : main
  Purpose  : Demonstrate the asynchronous mode of operations
\*============================================================================*/
int main(int argc, char *argv[])
{
	return PTAsyncMain (argc, argv);
}


#endif


/*============================================================================*\
  Function : PTAsyncMain
  Purpose  : Asynchronous demonstration procedure
\*============================================================================*/
int PTAsyncMain (int argc, char* argv[])
{
  int   i, action, bad_status = 0;
  char  *part_to_retrieve=NULL;
  char  proe_command[PRO_PATH_SIZE], text_path[PRO_PATH_SIZE];
  char  ua_msg[20];
  int counter = 0, timeout = 10;
  char *hostname ;   /* hostname not supported in Release 20. */
  char *display=NULL, *user=NULL;
  ProBoolean random, asy_flag = PRO_B_FALSE, random_choice;
  ProError err, last_err = PRO_TK_NO_ERROR;
  time_t action_time = 0, report_time = 0, cur_time; 
  wchar_t workspace[PRO_NAME_SIZE];
  ProProcessHandle p_handle;

  ProStringToWstring(MSGFIL, "usermsg.txt");
  ProTestErrlogOpen("pt_toolkit", "", "");

  if (argc >= 2)
    if (strncmp (argv[1], "-rpc", 4) == 0)
      asy_flag = PRO_B_TRUE;
            
  if (asy_flag == PRO_B_TRUE)
    /*  Start application from Pro/E session */
    {
      err = ProAsynchronousMain (argc, argv);
      TEST_CALL_REPORT ("ProAsynchronousMain()", "main()",
			err, err != PRO_TK_NO_ERROR);
        
      if (err != PRO_TK_NO_ERROR)
        {
	  ProTKFprintf (stderr, "Could not connect to Pro/E\n");
	  exit (0);
        }
      else 
	ProTKPrintf ("Connection may have succeeded!\n");
    }
  else
    /*  Start application from command line */
    {
      /* Check command line args and make the proe command */ 
      for(i=0; i<argc; i++)
        {
	  ProTKFprintf(stderr, "   argv[%d]:  %s\n", i, argv[i]);
        }
      if (argc < 3 || ((ProUtilStrcmp(argv[1], "-connect")==0) && (argc<3)))
        {
	  ProTKFprintf(stderr, "\t%s  <Pro/E command>  <text_path> or\n", argv[0]);
	  ProTKFprintf(stderr, 
		       "\t%s  <Pro/E command>  <text_path> <model/trailfile> or\n", argv[0]);
	  ProTKFprintf(stderr, 
		       "\t%s  <Pro/E command>  <text_path> <runmode> <graphics mode>"
		       " <trail mode> <trailfile>\n", argv[0]);
	  ProTKFprintf(stderr, 
		       "\t%s  -connect <text_path> [user [display ]]\n", argv[0]);
	  exit(0);
        }

/*------------------------------------------------------------------*\
        Connect to a Pro/ENGINNER session.
\*------------------------------------------------------------------*/
      if (ProUtilStrcmp(argv[1], "-connect")==0)
        {
	  connect_mode = 1;
	  strcpy(text_path, argv[2]);
	  hostname = "" ; 
	  if (argc>4)
	    display = argv[4];
	  if (argc>3)
	    user = argv[3];
    
	  ProTKPrintf("Attempt to connect ... user %s, display %s\n",
		      user == NULL ? "any" : user,
		      display == NULL ? "any" : display);
	  ProTKPrintf("Text path %s\n", text_path);
  
	  if (argc>5)
	    {
	      if (argc<7)
		
            	{
		  ProTKFprintf(stderr, 
			       "\t%s  -connect <text_path> [user [display [workspace timeout]] ]\n", 
			       argv[0]);
            	}
	      ProStringToWstring (workspace, argv[5]);
	      timeout = atoi (argv[6]);
	      err = ProEngineerConnectWS ("", display, user, workspace,
					  text_path, PRO_B_TRUE, timeout, &random_choice,
					  &p_handle);
	      TEST_CALL_REPORT("ProEngineerConnectWS()",
			       "main()", err, err != PRO_TK_NO_ERROR);
       	    } else{
	    err = ProEngineerConnect(hostname, display, user, text_path, 
				     PRO_B_TRUE, 20, &random, &proe_handle);
	    TEST_CALL_REPORT("ProEngineerConnect()",
			     "main()", err, err != PRO_TK_NO_ERROR);
	  }	
	    			
	  if (err != PRO_TK_NO_ERROR)
            {
	      /* print error directly, so users don't have to look for it. */
	      ProTKPrintf("Attempt unsuccessfull (error %d), exiting\n",
			  err);
	      return (0);
            }

        }
/*------------------------------------------------------------------*\
        Spawn a Pro/ENGINNER session.
\*------------------------------------------------------------------*/
      else
        {
	  strcpy(proe_command, argv[1]); /* Pro/E command */
	  if(argc == 4)
	    {
	      strcat(proe_command, " ");
	      strcat(proe_command, argv[argc-1]); /* model or trail file */
	    }
	  else if (argc > 4)
	    {
	      strcat(proe_command, " ");
	      strcat(proe_command, argv[3]); /* run mode */
	      strcat(proe_command, " ");
	      strcat(proe_command, argv[4]); /* graphics mode */
	      strcat(proe_command, " ");
	      strcat(proe_command, argv[5]); /* trail mode */
	      strcat(proe_command, " ");
	      strcat(proe_command, argv[6]); /* trail file */
	    } 
	  strcpy(text_path, argv[2]);
    
	  /* Start up Pro/ENGINEER */
	  ProTKFprintf(stderr, "\n*****\tStarting Pro/Engineer\t*****\n");
	  ProTKFprintf(stderr, "\tProe command: \"%s\"\t\n\tPlease wait...\n",
		       proe_command);

	  err = ProEngineerStart(proe_command, text_path);
	  TEST_CALL_REPORT("ProEngineerStart()",
			   "main()", err, err != PRO_TK_NO_ERROR);
        }
    }
  /* Interrupt handling */ 
   
  ProAsynchronousEventLoop();
   
  ProEventProcess();
  if (asy_flag == PRO_B_FALSE)
    {
/*------------------------------------------------------------------*\
    If the Pro/ENGINEER session was spawned by this application,
    restart Pro/ENGINEER after initial Pro/ENGINEER termination.
    (Restart is simple asynchronous model.)
\*------------------------------------------------------------------*/
      if (!connect_mode) 
        {
	  /*  Now re-start Pro/E  */ 
	  UserRestartProeTest(proe_command, text_path, argc, 
			      part_to_retrieve);
        }
    }
  ProTestErrlogClose();
    
  return (0);
}

/*===========================================================================*\
  Function : user_initialize()
  Purpost  : demo another way to add user's menu item
\*===========================================================================*/
int user_initialize(int argc, char *argv[])
{
    char  *pre = "Pre", *post = "Post";
    ProError err;
    uiCmdCmdId   cmd_id;

/*------------------------------------------------------------------*\
    Parse the command line
\*------------------------------------------------------------------*/
    ProUtilCommandLineParse( argc, argv );

/*------------------------------------------------------------------*\
    Initialize termination notification function.
\*------------------------------------------------------------------*/
    err = ProTermFuncSet((ProTerminationAction)UserTerminateAction);
    TEST_CALL_REPORT("ProTermFuncSet()", 
		    	    "user_initialize()", err, err != PRO_TK_NO_ERROR);

    if( err != PRO_TK_NO_ERROR)
       ProTKFprintf(stderr, "\nTermination function is NOT SET !!!\n");

/*---------------------------------------------------------------------*\
    Add new button to the menu bar
\*---------------------------------------------------------------------*/
    err = ProCmdActionAdd("-Pt Async",
        (uiCmdCmdActFn)ProTestShowMenu,
        uiProe2ndImmediate, TestAccessDefault,
        PRO_B_TRUE, PRO_B_TRUE, &cmd_id);
    TEST_CALL_REPORT("ProCmdActionAdd", "user_initialize",
                      err, err != PRO_TK_NO_ERROR);

    err = ProMenubarmenuPushbuttonAdd(
        "Utilities", "-Pt Async", "-Pt Async",
        "Async mode test commands", "Utilities.psh_util_aux",
	 PRO_B_TRUE, cmd_id, MSGFIL);
    TEST_CALL_REPORT("ProMenubarmenuPushbuttonAdd", "user_initialize",
                      err, err != PRO_TK_NO_ERROR);

    err = ProCmdCmdIdFind("exit", &cmd_id);
    TEST_CALL_REPORT("ProCmdCmdIdFind()", 
			"user_initialize()", err, err != PRO_TK_NO_ERROR &&
                        err != PRO_TK_GENERAL_ERROR);
 
    err = ProCmdBracketFuncAdd(cmd_id, (uiCmdCmdBktFn)UserTerm, "UserTerm",
	NULL);

    err = ProMenuFileRegister("Async Test", "tkasync.mnu", NULL);
    TEST_CALL_REPORT("ProMenuFileRegister()", 
			"user_initialize()", err, err !=PRO_TK_NO_ERROR);

    err = ProMenubuttonActionSet("Async Test", "Async Test",
        (ProMenubuttonAction)ProMenuDelete, NULL, 0);
    TEST_CALL_REPORT("ProMenubuttonActionSet()",
                        "user_initialize()", err, err !=PRO_TK_NO_ERROR);

    err = ProMenubuttonActionSet("Async Test", "Done/Return",
        (ProMenubuttonAction)ProMenuDelete, NULL, 0);
    TEST_CALL_REPORT("ProMenubuttonActionSet()",
                        "user_initialize()", err, err !=PRO_TK_NO_ERROR);

    err = ProMenubuttonActionSet("Async Test", "-Retrieve Obj",
        (ProMenubuttonAction)UserActionFunc, NULL, USER_RETRIEVE_ACTION);
    TEST_CALL_REPORT("ProMenubuttonActionSet()",
                        "user_initialize()", err, err !=PRO_TK_NO_ERROR);
    
    err = ProMenubuttonLocationSet("Async Test", "-Exit", -1);
    TEST_CALL_REPORT("ProMenubuttonLocationSet()",
                        "user_initialize()", err, err !=PRO_TK_NO_ERROR);
    err = ProMenubuttonActionSet("Async Test", "-Exit",
        (ProMenubuttonAction)UserActionFunc, NULL, USER_EXIT_ACTION);
    TEST_CALL_REPORT("ProMenubuttonActionSet()",
                        "user_initialize()", err, err !=PRO_TK_NO_ERROR);

    err = ProNotificationSet(PRO_MDL_DISPLAY_PRE, 
	(ProFunction)UserNotificationFunc);
    TEST_CALL_REPORT("ProNotificationSet()",
                        "user_initialize()", err, err !=PRO_TK_NO_ERROR);
    return(0);
}

/*===========================================================================*\
  Function : user_terminate()
  Purpost  : end Pro/T application
\*===========================================================================*/
void  user_terminate()
{
    ProError err;

    err = ProNotificationUnset(PRO_MDL_DISPLAY_PRE);
    TEST_CALL_REPORT("ProNotificationUnset()",
                        "user_terminate()", err, err !=PRO_TK_NO_ERROR);
    
    TEST_CALL_REPORT("user_terminate()", 
			"user_terminate()", PRO_TK_NO_ERROR, 0);
    ProTKPrintf("user terminate\n");
    ProAsynchronousEventLoopInterrupt();
}

/*===========================================================================*\
  Function : UserTerm()
  Purpose  : Calling Async Event Loop Interrupt telling Pro/DEVELOP application
      that Pro/E terminated 
\*===========================================================================*/
int UserTerm()
{
    ProLine wcmd;
    ProError err;

    ProAsynchronousEventLoopInterrupt();

    ProStringToWstring(wcmd, "yes");
    
    err = ProMacroLoad(wcmd);
    TEST_CALL_REPORT("ProMacroLoad()",
               		         "UserTerm()", err, err !=PRO_TK_NO_ERROR);
    return(0);
}

/*===========================================================================*\
  Function : UserRestartProeTest()
  Purpose  : test restarting Pro/E and simple async call
\*===========================================================================*/
int UserRestartProeTest(
    char *pro_command, 
    char *text_path,
    int arg_num,
    char *part_to_retrieve)
{
    char name[PRO_FAMILY_MDLNAME_SIZE];
    ProMdlfileType type;
    char name_type[PRO_FAMILY_MDLNAME_SIZE + PRO_TYPE_SIZE + 2];
    wchar_t w_name_type[PRO_FAMILY_MDLNAME_SIZE + PRO_TYPE_SIZE + 2];
    ProMdl model;
    char *proe;
    int w_id, ret = 0;
    ProError err;

    if(arg_num>3)
     	if (arg_num==4)
	{
       	    proe=strrchr(pro_command, ' '); 
            *proe='\0';
      	}
     	else
      	{
       	    return (0);
      	}

    /* Start up Pro/ENGINEER */
    ProTKFprintf(stderr, "\n*****\tRe-Starting Pro/Engineer\t*****\n");
    ProTKFprintf(stderr, "\tProe command: \"%s\"\t\n", pro_command);
    ProTKFprintf(stderr, "\t\tPlease wait...\n"); 
     

    err = ProEngineerStart(pro_command, text_path);
    TEST_CALL_REPORT("ProEngineerStart()",
                        "UserRestartProeTest()", err, err !=PRO_TK_NO_ERROR);

    if (part_to_retrieve!=NULL)
    {
        ProTKFprintf(stderr, "\nRetrieving part %s\n", part_to_retrieve); 
        /*  Query for an object  */
        ret = ProUtilConfirmNameType(part_to_retrieve, name, (ProType*)&type);
    }
    else
    {
         /*  Query for an object  */
	err = ProMessageDisplay(MSGFIL, "USER %0s", "Enter object name.type: ");
        TEST_CALL_REPORT("ProMessageDisplay()",
             "UserRestartProeTest()", err, err !=PRO_TK_NO_ERROR);
	err = ProMessageStringRead(79, w_name_type);
        if (err == PRO_TK_NO_ERROR)
	{
	    ProWstringToString(name_type, w_name_type);
	    part_to_retrieve = name_type ; 
	    ret = ProUtilConfirmNameType(name_type, name, (ProType*)&type); 
	}
    }

        /*  Retrieve an object  */
    if (err == PRO_TK_NO_ERROR && ret > 0)
    {
	ProStringToWstring(w_name_type, name);
    	err = ProMdlnameRetrieve (w_name_type, type, &model);
    	TEST_CALL_REPORT("ProMdlnameRetrieve ()",
                        "UserRestartProeTest()", err, err !=PRO_TK_NO_ERROR);

        /*  Display the object  */
        err = ProObjectwindowMdlnameCreate(w_name_type, (ProType)type, &w_id);
	TEST_CALL_REPORT("ProObjectwindowMdlnameCreate()",
                        "UserRestartProeTest()", err, err !=PRO_TK_NO_ERROR);

        err = ProMdlDisplay(model);
	TEST_CALL_REPORT("ProMdlDisplay()",
                        "UserRestartProeTest()", err, err !=PRO_TK_NO_ERROR);

        ProTKFprintf(stderr, "After retrieving '%s' part\n", part_to_retrieve);
        ProTKFprintf(stderr, "Exit Pro/E\n");
    }
 
    err = ProEngineerEnd();
    TEST_CALL_REPORT("ProEngineerEnd()", "UserRestartProeTest()",
		     err, err != PRO_TK_NO_ERROR);    
    return (0);
}

/*---------------------------------------------------------------------*\
 Function: UserTerminateAction()
 Purpose: called on Pro/E exit
\*---------------------------------------------------------------------*/
void UserTerminateAction(
    ProeTerminationStatus term_type)
{
    TEST_CALL_REPORT("ProTerminationAction()", "UserTerminateAction()", 
        PRO_TK_NO_ERROR, 0);
    ProAsynchronousEventLoopInterrupt();
    switch (term_type)
    {
     	case PROTERM_EXIT:  
	  ProTKFprintf(stderr, "\nMenu item 'EXIT' was picked\n");
	  break;
    } 
}

/*============================================================================*\
  Function : ProTestShowMenu()
  Purpose  : Perform a Pro/E operation
\*============================================================================*/
int ProTestShowMenu()
{
    int action, menu_id;
    ProError err;
    
  
    err = ProMenuCreate(PROMENUTYPE_MAIN, "Async Test", &menu_id);
    TEST_CALL_REPORT("ProMenuCreate()",
		     "ProTestShowMenu()", err, err != PRO_TK_NO_ERROR);

    err = ProMenuProcess("Async Test", &action);
    TEST_CALL_REPORT("ProMenuProcess()",
		     "ProTestShowMenu()", err, err != PRO_TK_NO_ERROR);

    return(0);
}

/*============================================================================*\
  Function : UserActionFunc()
  Purpose  : Perform a Pro/E operation
\*============================================================================*/
int UserActionFunc(
    ProAppData ptr, 
    int opt)
{
    char name[PRO_FAMILY_MDLNAME_SIZE];
    ProMdlfileType type;
    ProMdl model;
    ProCharLine msgbuf;
    char name_type[PRO_FAMILY_MDLNAME_SIZE + PRO_TYPE_SIZE + 2];
    wchar_t w_name_type[PRO_FAMILY_MDLNAME_SIZE + PRO_TYPE_SIZE + 2];
    ProError err;
	int strLength;
    int w_id, ret=0;

    switch (opt)
    {
    case USER_RETRIEVE_ACTION :
        Action_done = 1;
         /*  Query for an object  */
        err = ProMessageDisplay(MSGFIL, "USER %0s", "Enter object name.type: ");
        TEST_CALL_REPORT("ProMessageDisplay()",
            "UserActionFunc()", err, err !=PRO_TK_NO_ERROR);
        err = ProMessageStringRead(79, w_name_type);
        if (err == PRO_TK_NO_ERROR)
        {
            ProWstringToString(name_type, w_name_type);
            ret = ProUtilConfirmNameType(name_type, name, (ProType*)&type);
        }
	/*  Retrieve the object  */
    	if (err == PRO_TK_NO_ERROR && ret > 0)
    	{
            ProStringToWstring(w_name_type, name);
            err = ProMdlnameRetrieve(w_name_type, type, &model);
            TEST_CALL_REPORT("ProMdlnameRetrieve()",
                        "UserActionFunc()", err, err !=PRO_TK_NO_ERROR);

            /*  Display the object  */
            err = ProObjectwindowMdlnameCreate(w_name_type, (ProType)type, &w_id);
            TEST_CALL_REPORT("ProObjectwindowMdlnameCreate()",
                        "UserActionFunc()", err, err !=PRO_TK_NO_ERROR);

	    err = ProWindowActivate(w_id);
            TEST_CALL_REPORT("ProWindowActivate()",
                        "UserActionFunc()", err, err !=PRO_TK_NO_ERROR);

            err = ProMdlDisplay(model);
            TEST_CALL_REPORT("ProMdlDisplay()",
                        "UserActionFunc()", err, err !=PRO_TK_NO_ERROR);

	    if (err == PRO_TK_NO_ERROR)
	    {
		/*  Confirm back to user  */
			err = ProWstringLengthGet (w_name_type, &strLength);
			TEST_CALL_REPORT("ProWstringLengthGet()",
                        "UserActionFunc()", err, err !=PRO_TK_NO_ERROR);

			if (strLength < 80)
			{
	   			ProTKSprintf(msgbuf, "Retrieved: %s", name_type);
            	err = ProMessageDisplay(MSGFIL, "USER %0s", msgbuf);
                TEST_CALL_REPORT("ProMessageDisplay()",
                        "UserActionFunc()", err, err !=PRO_TK_NO_ERROR);
            	ProTKFprintf(stderr, "\t%s\n", msgbuf);
			}
			else
			{
				return PRO_TK_LINE_TOO_LONG;
			}
	    }
    	}
        break;

    case USER_MESSAGE_ACTION :
        err = ProMessageDisplay(MSGFIL, "USER %0s", msgbuf);
        TEST_CALL_REPORT("ProMessageDisplay()",
                        "UserActionFunc()", err, err !=PRO_TK_NO_ERROR);
        ProTKFprintf(stderr, "\t%s\n", msgbuf);
        break;

    case USER_EXIT_ACTION :
        if (!connect_mode)
        {
    	    err = ProEngineerEnd();
    	    TEST_CALL_REPORT("ProEngineerEnd()",
			"UserActionFunc()", err, err != PRO_TK_NO_ERROR);
            ProTKFprintf(stderr, "\n\tHello World! After ProEngineerEnd()\n");
	}
	else
	{
	    err = ProEngineerDisconnect(&proe_handle, 20);
    	    TEST_CALL_REPORT("ProEngineerDisconnect()",
			"UserActionFunc()", err, err != PRO_TK_NO_ERROR);
            ProTKFprintf(stderr, "\n\tHello World! After ProDisconnectProe()\n");
	}
        ProAsynchronousEventLoopInterrupt();
        break;

    default :
        break;
    }
    return(0);
}

/*============================================================================*\
  Function : UserNotificationFunc()
  Purpose  : Test Notification function in async mode
\*============================================================================*/
ProError UserNotificationFunc()
{
   ProTKFprintf(stderr, "Notification function\n");
   TEST_CALL_REPORT("ProMdlDisplayPre()",
			"UserNotificationFunc()", PRO_TK_NO_ERROR, 0);

   return (PRO_TK_NO_ERROR);
}