/*  Project Tec3270

	 SUBSYSTEM:    Tec3270.exe (Tec3270 application)
	 FILE:         alffnspt.cpp
	 AUTHOR:       Alcindo Ferreira Filho

	 OVERVIEW:

    ALFFScript Class Implementation, the Tec3270 Script
    (Script File For Connections) class.

    COPYRIGHT:    © 1998-2007
                  Alcindo Ferreira Filho
                  <alcindo@inttec.com.br>

    LICENSE:      Tec3270_License (docs/Tec3270_License.txt)
*/
/*
	 COMMANDS Help:
	 ==============

    See http://www.inttec.com.br/tec3270 and select Using Script Files
    on Tutorial page, on help main index.

    NOTES:

    (1) <if> command (to be implemented on future versions):

	 if <expression> then <command> [else <command>]
	 se <expressao> entao <comando> [senao <comando>]

	 <expression> : screen position, length, operator, literal
			  screen position:  R:C ;
			  ===============
			  length: integer limited to screen size;
			  =======
			  operator: =|>=|>|<|<=
			  =========
			  literal:  any text as typed on keyboard or received in data
			  ========	streams
	 execution:
	 =========
			if <expression> is true, then <command> is executed,
			otherwise <else command> is executed if there is any.

    (2) Command Sequence:

    	  All "Connection related" commands must come before "session related"
        commands.  "Neutral" commands may occur at any point of script file.

        "Connection related" commands:  TERMINAL, TITLE, CONNECT, PORT
        "Session related"    commands:  TYPE, PASSWORD, KEY, LOOKFOR, LOOKLIST,
                                        PROMPT, MOVECURSOR "R:C"
        "Neutral"            commands:  GOTO "label", LABEL, IF, EXIT

*/

#include "alffnall.h"

//{{ALFFScript Implementation}}
//
//////////////////////////////////////////////////////////
// ALFFScript
// ==========
// Construction/Destruction handling.
ALFFScript::ALFFScript(TWindow* pParent):
				 fileData(OFN_HIDEREADONLY | OFN_NOCHANGEDIR |
							 OFN_FILEMUSTEXIST,
							 "Tec3270 Script (*.spt)|*.spt|"
							 "All Files   (*.*)|*.*",
							 0,0,"SPT")
{
 scriptParent = pParent;
 FileName = new char[MAXPATH];
 fileOpened = false;
 lineBuffer = new char[MAXLINESIZE+1];
 endOfFile = true;  // File isn't opened
 bufCount = 0;
 estado = BEFORECONNECT;
 nextCommandSame = false;
 maxSameCommand = 10;
}

ALFFScript::~ALFFScript()
{
 Close();
 if (FileName)
  delete [] FileName;
 if (lineBuffer)
  delete [] lineBuffer;
}

char*
ALFFScript::GetFileName()
{
 return FileName;
}

void
ALFFScript::TidyFileName()
{
//
// If you just try and use the file data raw, it
// gets stuck with a fully qualified path in the
// file name. This messes up the way the file appears
// as this overrides the path.
//
// For this function, the FDN_NOCHANGEDIR was set. This
// allows the user to choose a different directory for
// a file, but does not reset the path from one execution
// to another.
//
// The following function removes the fully qualified path
// from anything in the working directory. Anything not in
// the home directory is left with a fully qualified name.
//
// working file name
string filePath(FileName);
char currentDir[MAXPATH];
char drive[4];
// The win16 method of getting the current directory
// only provides the path. First get the drive
// Note this is DOS specific
drive[0] = getdisk() + 'A'; // Sums with letters

// getdisk returns 0 for A, 1 for B. We can turn
// 0,1 etc into a letter by adding to 'A' as they
// are next to each other
drive[1] = ':';  // Set up the rest of the drive
drive[2] = '\\'; // the hard way
drive[3] = '\0';
getcurdir(0,currentDir);     // get the directory
string fullDirectory(drive); // Make a new variable
fullDirectory += currentDir; // and concatenate

// Another string function to
// find the last '\'
unsigned pos = filePath.find_last_of("\\");

//
// If there is no directory - probably dodgy
// both should be the same length
// If directories are the same length - see if they match
if (pos != NPOS
	&& pos == fullDirectory.length()
	&& !filePath.compare(currentDir,pos))
 {
 //
 // They are the same - remove the directory
 //
 filePath = filePath.substr(pos + 1);
 // and put it back into the FileName
 strcpy(FileName,filePath.c_str());
 }
}

int
ALFFScript::Open(const char* fileName)
{
 if (!fileName[0])
	 return NOFILENAME;     // No File Name Could Not Open Log File
 else strcpy (FileName,fileName);
 TidyFileName();
 open(FileName, ios::in|ios::binary|ios::nocreate);
 if (this->good())
  {
   fileOpened = true;
   endOfFile  = false;
	return SUCCESSFULOPEN;
  }
 else return INVALIDOPEN;
}

int
ALFFScript::GetToken(string* tokenStr, char** buffer, int* length, int type)
{
 /* Type can be "COMMAND" or "PARM":

 	 - If type is "COMMAND" delimiters should be:

    		blank  '['

    - If type is "PARM" delimiters should be:

    		blank  '['  ']'
 */
 char token[256];
 int  tokenCount = 0;
 char tokenChar[2] = {0x00,0x00};
 token[0] = NULL;

 /* If there is no buffer available something was wrong */
 if (*length > 0)
  {
   /* Get the token */
   if (type == COMMAND)
    {
     /* First of all we must bypass blanks or delimiters from last command*/
     while ( (*buffer[0] == ' ') || (*buffer[0] == '"')
           && (*length > 0))
      {
       *buffer+=1;    // bypass blank
       *length -= 1; // count - 1
      }

     while ( ((*buffer[0] != ' ') &&  (*buffer[0] != '"'))
      && (*length > 0) )
     {
     if ((*buffer[0] != '\n') && (*buffer[0] != '\r'))
      {
       memcpy(tokenChar,*buffer,1);
       strcat(token,tokenChar);
       tokenCount++;
      }
     *buffer+=1; *length -= 1;
     }
    }
   else if (type == PARM)
    {
     /* Bypass all blanks that may be before parm begin */
     while ((*buffer[0] == ' ') && (*length > 0))
      {
       *buffer+=1;    // bypass blank
       *length -= 1; // count - 1
      }
     if ( (*buffer[0] == '"' ) && (*length > 0) )
     {
      *buffer+=1;    // bypass initial delimiter if it exists
      *length -= 1; // count - 1
     }
     else return INVALIDPARM;
     while ( (*buffer[0] != '"') && (*length > 0) )
     {
     if ((*buffer[0] != '\n') && (*buffer[0] != '\r'))
      {
       memcpy(tokenChar,*buffer,1);
       strcat(token,tokenChar);
       tokenCount++;
      }
     *buffer+=1; *length -= 1;
     }
    }

  }
 if (tokenCount > 0)
  {
   *tokenStr = token;
   return OPERATIONOK;
  }
 else return INVALIDPARM;
}

int
ALFFScript::GetNextCommand(ScriptCommand* scriptCommand)
{
 string  token;
 int rc;

 if (!fileOpened || endOfFile)
  return ENDOFFILE;

 if (nextCommandSame)
   {
    *scriptCommand = lastCommand;
    nextCommandSame = false;
    return OPERATIONOK;
   }
 scriptCommand->parm[0] = "";
 scriptCommand->parm[1] = "";
 scriptCommand->parm[2] = "";
 scriptCommand->parm[3] = "";

 /* Get the command */
 bool commandFound = false;
 while (!commandFound)
  {
   if (bufCount == 0) // BUFFER is EMPTY
    {
     this->getline(lineBuffer,MAXLINESIZE-1);
     bufCount = this->gcount()-1;   // Discount linefeed
     if (this->eof())
      {
       endOfFile = true;
       return ENDOFFILE;
      }
     else
      { lineBufPtr = lineBuffer; }
    }
   /* Get next command */
   if (lineBuffer[0] != '*')
    {
     rc = GetToken(&token, &lineBufPtr,&bufCount,COMMAND);
     if (rc == OPERATIONOK)
      {
       token.to_upper();
       commandFound = true;
      }
    }
   else bufCount = 0;  // Ignorar linha de comentário.
  }
  
 /* Command was found: Validating it */
 if ((token == "TITLE") || (token == "TITULO") )
    {
      scriptCommand->commandId = TITLE;
      /* Get Tag Parms */
      rc = GetToken(&token,&lineBufPtr,&bufCount,PARM);
      if (rc == OPERATIONOK)
       {
        scriptCommand->parm[0] = token;
       }
      else return INVALIDPARM;
    }
 else if (token == "TERMINAL")
       {
        scriptCommand->commandId = TERMINAL;
        /* Get Tag Parms */
        rc = GetToken(&token,&lineBufPtr,&bufCount,PARM);
        if (rc == OPERATIONOK)
         {
          scriptCommand->parm[0] = token;
         }
        else return INVALIDPARM;
       }
 else if ((token == "PORT") || (token == "PORTA") )
       {
        scriptCommand->commandId = PORT;
        /* Get Tag Parms */
        rc = GetToken(&token,&lineBufPtr,&bufCount,PARM);
        if (rc == OPERATIONOK)
         {
          scriptCommand->parm[0] = token;
         }
        else return INVALIDPARM;
       }
 else if ((token == "CONNECT") || (token == "CONECTAR") )
       {
         scriptCommand->commandId = CONNECT;       
         /* Get Tag Parms */
         rc = GetToken(&token,&lineBufPtr,&bufCount,PARM);
         if (rc == OPERATIONOK)
          {
           // This an Tec3270 Connection ...
           if (token.contains("TEC3270="))
            {
             scriptCommand->commandId = CONNECT_TEC3270;
             size_t equalPos = token.find("=");
             scriptCommand->parm[0] = token.substr(equalPos+1);
            }
           // This an IP address or an URL with  port specified
           else if (token.contains(":"))
             {
              scriptCommand->commandId = CONNECTPORT;
              size_t colonPos = token.find(":");
              scriptCommand->parm[0] = token.substr(0,colonPos-1); // URL or IP
              scriptCommand->parm[1] = token.substr(colonPos+1);   // Port
             }
           // This an IP address or an URL with no port specified
           else
            {
             scriptCommand->commandId = CONNECT;
             scriptCommand->parm[0] = token;
            }
          }
         else return INVALIDPARM;
       }
else if ((token == "MOVECURSOR"))
       {
        scriptCommand->commandId = MOVECURSOR;       
        /* Get Tag Parms */
        rc = GetToken(&token,&lineBufPtr,&bufCount,PARM);
        if (rc == OPERATIONOK)
         {
          if (token.contains(":"))     //  R:C -> Row and Collumn
           {
            size_t colonPos = token.find(":");
            scriptCommand->parm[0] = token.substr(0,colonPos); // Row
            scriptCommand->parm[1] = token.substr(colonPos+1); // Collumn
           }
          else
           {
            scriptCommand->parm[0] = token;
            return INVALIDPARM;
           }
         }
         else return INVALIDPARM;
       }
 else if ((token == "GOTO") || (token == "IRPARA") )
       {
        scriptCommand->commandId = GOTOLABEL;
        /* Get Tag Parms */
        rc = GetToken(&token,&lineBufPtr,&bufCount,PARM);
        if (rc == OPERATIONOK)
           scriptCommand->parm[0] = token;
        else return INVALIDPARM;
       }
 else if ((token == "LABEL") || (token == "PARAGRAFO") )
       {
        scriptCommand->commandId = LABEL;
        /* Get Tag Parms */
        rc = GetToken(&token,&lineBufPtr,&bufCount,PARM);
        if (rc == OPERATIONOK)
          scriptCommand->parm[0] = token;
        else return INVALIDPARM;
       }
 else if ((token == "TYPE") || (token == "DIGITE") )
       {
        scriptCommand->commandId = TYPE;
        /* Get Tag Parms */
        rc = GetToken(&token,&lineBufPtr,&bufCount,PARM);
        if (rc == OPERATIONOK)
         {
          scriptCommand->parm[0] = token;
         }
        else return INVALIDPARM;
       }
 else if ((token == "KEY") || (token == "TECLE") )
       {
        scriptCommand->commandId = KEY;
        /* Get Tag Parms */
        rc = GetToken(&token,&lineBufPtr,&bufCount,PARM);
        if (rc == OPERATIONOK)
         {
          scriptCommand->parm[0] = token;
         }
        else return INVALIDPARM;
       }
 else if ((token == "PASSWORD") || (token == "SENHA") )
       {
        scriptCommand->commandId = PASSWORD;
        /* Get Tag Parms */
        rc = GetToken(&token,&lineBufPtr,&bufCount,PARM);
        if (rc == OPERATIONOK)
         {
          scriptCommand->parm[0] = token;
         }
        else return INVALIDPARM;
       }
 else if ((token == "LOOKFOR") || (token == "ESPERARPOR") )
       {
        scriptCommand->commandId = LOOKFOR;
        /* Get Tag Parms */
        rc = GetToken(&token,&lineBufPtr,&bufCount,PARM);
        if (rc == OPERATIONOK)
         {
          scriptCommand->parm[0] = token;
         }
        else return INVALIDPARM;
       }
 else if ((token == "LOOKLIST") || (token == "ESPERARLISTA") )
       {
         scriptCommand->commandId = LOOKLIST;
         /* Get Tag Parms */
         rc = GetToken(&token,&lineBufPtr,&bufCount,PARM);
         if (rc == OPERATIONOK)
          {
           // Constante a checar
           size_t colcheteAbre = token.find_first_of("[");
           if (colcheteAbre != NPOS)
             scriptCommand->parm[0] = token.substr(0,colcheteAbre);
           else return INVALIDPARM;
           // Label a desviar
           size_t colcheteFecha = token.find_first_of("]");
           if (colcheteFecha != NPOS)
              scriptCommand->parm[1] = token.substr(colcheteAbre+1,colcheteFecha-colcheteAbre-1);
           else return INVALIDPARM;
           // Constante a checar (2)
           size_t colcheteAbre2 = token.find_last_of("[");
           if ((colcheteAbre2>colcheteFecha) && (colcheteAbre2 != NPOS))
             scriptCommand->parm[2] = token.substr(colcheteFecha+1,colcheteAbre2-colcheteFecha-1);
           else return INVALIDPARM;
           // Label a desviar
           size_t colcheteFecha2 = token.find_last_of("]");
           if ((colcheteFecha2>colcheteAbre2) && (colcheteFecha2 != NPOS))
              scriptCommand->parm[3] = token.substr(colcheteAbre2+1,colcheteFecha2-colcheteAbre2-1);
           else return INVALIDPARM;
          }
         else return INVALIDPARM;
       }
 else if ((token == "PROMPT") || (token == "DIALOGO") )
       {
        scriptCommand->commandId = PROMPT;
        /* Get Tag Parms */
        rc = GetToken(&token,&lineBufPtr,&bufCount,PARM);
        if ( rc == OPERATIONOK )
         {
          scriptCommand->parm[0] = token;
         }
        else return INVALIDPARM;
       }
 else if ((token == "MESSAGE") || (token == "<MENSAGEM") )
       {
        scriptCommand->commandId = MESSAGE;
        /* Get Tag Parms */
        rc = GetToken(&token,&lineBufPtr,&bufCount,PARM);
        if ( rc == OPERATIONOK )
         {
          scriptCommand->parm[0] = token;
         }
        else return INVALIDPARM;
       }
 else if ((token == "IF") || (token == "SE") )
       {
         scriptCommand->commandId = IF;
         /* Get Tag Parms */
         rc = GetToken(&token,&lineBufPtr,&bufCount,PARM);
         if ( rc == OPERATIONOK )
          {
           scriptCommand->parm[0] = token;
          }
         else return INVALIDPARM;
         /*  IF command is not yet implemented. */
       }
 else if ((token == "EXIT") || (token == "SAIR") )
        {
         scriptCommand->commandId = EXIT;
        }
 else return INVALIDSCRIPTTAG;

 /* Save Last Command Ok */
 lastCommand = *scriptCommand;

 return OPERATIONOK;
}

string
ALFFScript::GetCommandDescription(ScriptCommand* comando)
{
 char command[64];
 switch (comando->commandId)
  {
    case             IF: strcpy(command,"IF");
                         break;
	 case        CONNECT: strcpy(command,"CONNECT");
                         break;
    case CONNECT_TEC3270: strcpy(command,"CONNECT_TEC3270");
                         break;
    case    CONNECTPORT: strcpy(command,"CONNECTPORT");
                         break;
    case       TERMINAL: strcpy(command,"TERMINAL");
                         break;
    case          TITLE: strcpy(command,"TITLE");
                         break;
    case           PORT: strcpy(command,"PORT");
                         break;
    case      GOTOLABEL: strcpy(command,"GOTOLABEL");
                         break;
    case   MOVECURSOR: strcpy(command,"MOVECURSOR");
                         break;
    case           TYPE: strcpy(command,"TYPE");
                         break;
    case            KEY: strcpy(command,"KEY");
                         break;
    case       PASSWORD: strcpy(command,"PASSWORD");
                         break;
    case        LOOKFOR: strcpy(command,"LOOKFOR");
                         break;
    case       LOOKLIST: strcpy(command,"LOOKLIST");
                         break;
    case          LABEL: strcpy(command,"LABEL");
                         break;
    case         PROMPT: strcpy(command,"PROMPT");
                         break;
    case           EXIT: strcpy(command,"EXIT");
                         break;
                default: strcpy(command,"UNKNOW");
                         break;
  }
  return string(command);
}

void
ALFFScript::Close()
{
 if (fileOpened)
  {
   close();
   fileOpened = false;
   endOfFile = true;  // File isn't opened
   bufCount = 0;
  }
}

int
ALFFScript::GetKeyCommand(char* key)
{
 if (!strcmp(key,"TAB"))
   return IDS_NEXTFIELD;
 else if (!strcmp(key,"ENTER"))
   return IDS_ENTERCOMMAND;
 else if (!strcmp(key,"CLEAR"))
   return IDS_CLEAR;
 else if (!strcmp(key,"PA1"))
   return IDS_PA1;
 else if (!strcmp(key,"PA2"))
   return IDS_PA2;
 else if (!strcmp(key,"PA3"))
   return IDS_PA3;
 else if (!strcmp(key,"PF1"))
   return IDS_PF1;
 else if (!strcmp(key,"PF2"))
   return IDS_PF2;
 else if (!strcmp(key,"PF3"))
   return IDS_PF3;
 else if (!strcmp(key,"PF4"))
   return IDS_PF4;
 else if (!strcmp(key,"PF5"))
   return IDS_PF5;
 else if (!strcmp(key,"PF6"))
   return IDS_PF6;
 else if (!strcmp(key,"PF7"))
   return IDS_PF7;
 else if (!strcmp(key,"PF8"))
   return IDS_PF8;
 else if (!strcmp(key,"PF9"))
   return IDS_PF9;
 else if (!strcmp(key,"PF10"))
   return IDS_PF10;
 else if (!strcmp(key,"PF11"))
   return IDS_PF11;
 else if (!strcmp(key,"PF12"))
   return IDS_PF12;
 else if (!strcmp(key,"PF13"))
   return IDS_PF13;
 else if (!strcmp(key,"PF14"))
   return IDS_PF14;
 else if (!strcmp(key,"PF15"))
   return IDS_PF15;
 else if (!strcmp(key,"PF16"))
   return IDS_PF16;
 else if (!strcmp(key,"PF17"))
   return IDS_PF17;
 else if (!strcmp(key,"PF18"))
   return IDS_PF18;
 else if (!strcmp(key,"PF19"))
   return IDS_PF19;
 else if (!strcmp(key,"PF20"))
   return IDS_PF20;
 else if (!strcmp(key,"PF21"))
   return IDS_PF21;
 else if (!strcmp(key,"PF22"))
   return IDS_PF22;
 else if (!strcmp(key,"PF23"))
   return IDS_PF23;
 else if (!strcmp(key,"PF24"))
   return IDS_PF24;
 else return INVALIDPARM;
}

bool
ALFFScript::SetNextCommandSame()
{
 maxSameCommand -= 1;
 if (maxSameCommand > 0)
  {
   nextCommandSame = true;
   return true;
  }
 else return false;
}
