/****************************************************************************/
/*                                                                          */
/*  FILE: command.c                                                         */
/*                                                                          */
/*  PURPOSE:                                                                */
/*      Provide functions that decode and execute comands sent to the       */
/*      Mk4d server.                                                        */
/*                                                                          */
/*  HOW TO ADD A NEW COMMAND TO THE Mk4d SERVER:                            */
/*                                                                          */
/*      1)  Write a function with the following calling sequence            */
/*          int cmd_nameofcommand(int sd, const char* args)                 */
/*                                                                          */
/*          "sd"    is an open socket to the client                         */
/*          "args"  is a string containing whatever was sent by the client  */
/*                  following the comand name.  Your function must parse    */
/*                  this string.  By convention strtok(args, MK4D_DELIM)    */
/*                  should be used to scan the args string, but you can     */
/*                  use whatever you want.                                  */
/*                                                                          */
/*          Return Value:   CMD_OK    if your function worked               */
/*                          CMD_FAIL  if your function detected an error    */
/*                                                                          */
/*          Your new comand function may or may not send data to the client */
/*          It all depends of the purpose of the function.                  */
/*                                                                          */
/*      2)  Add a line to the Command_Table.                                */
/*                                                                          */
/*      3)  Put the function some place where it will get compiled and      */
/*          linked by the Makefile.  Very small functions can go in this    */
/*          file but larger ones should be in thier own file with a header  */
/*          file #included here.  The rule is, if the function is complex   */
/*          enough that it could contain errors and will need to be         */
/*          debugged put it in it's own file.                               */
/*          See the file cmd_example.c and cmd_example.h for examples       */
/*          of what to do.  Don't forget to fix the Makefile                */
/*                                                                          */
/*      4)  Just do a Make and now all clients will have access to your     */
/*          new command.                                                    */
/*                                                                          */
/****************************************************************************/

/* Used by RCS and ident */
char commands_rcsid[] = 
    "$Id: commands.c,v 1.8 1998/09/29 06:44:33 chris Exp $";

#include <stdio.h>
#include <string.h>
#include <errno.h>

#include "MLog.h"
#include "server_lib.h"
#include "commands.h"
#include "tasks.h"

#include "cmd_example.h"
#include "cmd_expose.h"
#include "cmd_fits2image.h"
#include "cmd_focus.h"
#include "cmd_image2cdl.h"
#include "cmd_image2fits.h"
#include "cmd_park.h"
#include "cmd_point_at.h"
#include "cmd_status.h"
#include "cmd_set.h"
#include "cmd_track.h"
#include "cmd_unpark.h"


/* Prototypes for functions defined in this file*/
int cmd_null            (int sd, const char* args);
int cmd_version         (int sd, const char* args);
int cmd_help            (int sd, const char* args);
int cmd_login           (int sd, const char* args);
int cmd_logout          (int sd, const char* args);
int cmd_remove_all_tasks(int sd, const char* args);



CommandDesTyp   Command_Table[] =
{
    {"null",             "Does nothing",                  cmd_null,             0},
    {"version",          "Send server version number",    cmd_version,          0},
    {"help",             "Send a list of valid commands", cmd_help,             0},
    {"login",            "Request access to server",      cmd_login,            0},
    {"logout",           "Disconnect from server",        cmd_logout,           0},
  /*{"example_arguments","Parse (float dbl int string)",  cmd_example_arguments},*/
  /*{"example_task",     "Runs again after 15 sec",       cmd_example_task},     */
    {"remove_all_tasks", "Clears the task table",         cmd_remove_all_tasks, 1},
    
    {"expose",           "Collect an image to buffer",    cmd_expose,           1},
    {"fits2image",       "Reads FITS image to buffer",    cmd_fits2image,       1},
    {"focus",            "Move to specied focus setting", cmd_focus,            1},
    {"image2cdl",        "Display image in buffer",       cmd_image2cdl,        1},
    {"image2fits",       "Write image in buffer to FITS", cmd_image2fits,       1},
    {"park",             "Shutdown the system",           cmd_park,             1},
    {"point_at",         "Points telscope.",              cmd_point_at,         1},
    {"set",              "Set a global variable",         cmd_set,              1},
    {"status",           "Print system status",           cmd_status,           1},
    {"track",            "Start tracking in RA, at rate", cmd_track,            1},
    {"unpark",           "Make system ready for use",     cmd_unpark,           1},
    
    {"END_OF_TABLE",    "END_OF_TABLE",                   NULL}
};
 
 
int dispatch(int sd, char buffer[])
{
    int     cmd_index;
    int     res;
    char*   token;
    char    name[MK4D_CMDLEN];
    char    args[MK4D_CMDLEN];
    char    reply[MK4D_CMDLEN+16];
    void*   fp;
    
    token = strtok(buffer, MK4D_DELIM);
    
    if ( !token ) 
    {
        /* no token found in buffer, null command.  We still must   */
        /* send a reply.  We assume a null command can never fail   */
        /* So we send a null OK.                                    */
        res = sl_Send2Client(sd, "< OK>\n");
        if (res < 0)
        {
            /* failed to send replay to client */
            ml_printf(ML_MANDATORY, ML_ERROR,
                "ERROR could not send reply to client %s\n", reply); 
            ml_printf(ML_MANDATORY, ML_ERROR,
                "      %s:%d\n", __FILE__, __LINE__); 
        }
        return 0;
    }
    
    strncpy(name, token, sizeof(name));
    strncpy(args, &buffer[1+strlen(token)], sizeof(args));
    
    /* We found the first token which should be a command name  */
    /* now search the table.  Commands are not case sensitive   */
    cmd_index = 0;
    while (Command_Table[cmd_index].cmd_function)
    {
        if (0 == strncasecmp(token, Command_Table[cmd_index].name, MK4D_CMDLEN))
        {
            /* Found it.  Now run the command */
            fp = Command_Table[cmd_index].cmd_function;
            res = ((int (*)(int, const char*)) fp)(sd, args);
            
            if (res == CMD_OK)
            {
                /* The command worked */
                sprintf(reply, "<%s OK>\n", name);
            }
            else if (res == CMD_FAIL)
            {
                /* The command failed */
                sprintf(reply, "<%s FAIL>\n", name); 
            
            }
            else if (res == CMD_NOREPLY)
            {
                /* The client connection is closed.  Can't send reply */
                return 1;
            }
            else
            {
                /* This should never happen but don't quit. */
                ml_printf(ML_MANDATORY, ML_ERROR,
                    "INTERNAL ERROR %s:%d\n", __FILE__, __LINE__); 
                sprintf(reply, "<%s FAIL>\n", name); 
            } 
            
            /* Send the reply */
            res = sl_Send2Client(sd, reply);
            if (res < 0)
            {
                /* failed to send replay to client */
                ml_printf(ML_MANDATORY, ML_ERROR,
                    "ERROR could not send reply to client %s\n", reply); 
                ml_printf(ML_MANDATORY, ML_ERROR,
                    "      %s:%d\n", __FILE__, __LINE__);  
            }
            return 1;
        }
        cmd_index++;
    }
    
    /* command not found */
    sprintf(reply, "<%s INVALID>\n", name);
    res = sl_Send2Client(sd, reply);
    if (res < 0)
    {
        /* failed to send replay to client */
        ml_print(ML_MANDATORY, ML_ERROR,
            "ERROR could not send <invalid> to client %s\n"); 
        ml_printf(ML_MANDATORY, ML_ERROR,
            "      %s:%d\n", __FILE__, __LINE__);  
    }
    return -1; 
}   

int cmd_null(int sd, const char* args)
{
    /* do nothiung */
    
    return CMD_OK;
}  

int cmd_version(int sd, const char* args)
{
    int res;
    char    Version[16];
     
    snprintf(Version, sizeof(Version), "%s\n", VERS);  
    res = sl_Send2Client(sd, Version);
    if (res < 0)
    {
        /* failed to send replay to client */
        ml_print(ML_MANDATORY, ML_ERROR,
            "ERROR could not send data to client \n");
        ml_printf(ML_MANDATORY, ML_ERROR,
            "      %s:%d\n", __FILE__, __LINE__);  
        return CMD_FAIL; 
    }
    return CMD_OK;
}

int cmd_help(int sd, const char* args)
{
    int     i;
    int     res;
    char    buffer[32+128+8];
    
    i = 0;
    while (Command_Table[i].cmd_function)
    {
        /* Send requested data */
        sprintf(buffer, "%s\t%s\n", 
            Command_Table[i].name, Command_Table[i].comment);
        res = sl_Send2Client(sd, buffer);
        if (res < 0)
        {
            /* failed to send replay to client */
            ml_print(ML_MANDATORY, ML_ERROR,
                "ERROR could not send data to client \n");
            ml_printf(ML_MANDATORY, ML_ERROR,
                "      %s:%d\n", __FILE__, __LINE__);  
            return CMD_FAIL; 
        }
        i++;
    }    
    return CMD_OK; 
}


int cmd_login(int sd, const char* args)
{
    return CMD_OK; 
} 

int cmd_logout(int sd, const char* args)
{
    int res;
    
    
    /* Kill this client.  BUT first send reply.  This command is the    */
    /* exception to the rulles.  Command functions normally do not      */
    /* send replies.  Even worse is send the reply _before_ doing the   */
    /* requested action.  If this case we must break some rules.        */
    
    res = sl_Send2Client(sd, "<logout OK>\n");
    if (res < 0)
    {
        /* failed to send replay to client */
        ml_print(ML_MANDATORY, ML_ERROR,
            "ERROR could not send data to client \n");
        ml_printf(ML_MANDATORY, ML_ERROR,
            "      %s:%d\n", __FILE__, __LINE__);  
        return CMD_FAIL; 
    }
    
    /* If the socket has any tasks scheduled to run remove them now     */
    /* Then close socket.                                               */
    ct_RemoveClient(sd);
    
    return CMD_NOREPLY;
}   

int cmd_remove_all_tasks(int sd, const char* args)
{
    /* Clean out the task table */
    RemoveAllTasks();
    return CMD_OK;
}  
 
