#ifndef VCASDO_FSM_H
#define VCASDO_FSM_H

#ifndef __RTL__
#include <sys/time.h>
#else /*__RTL__*/
#include <time.h>
#endif /*__RTL__*/

#include "can.h"
#include "ul_dbuff.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef unsigned char byte;
typedef unsigned short word;

struct vcasdo_fsm_t;
struct canmsg_t;

typedef void (vcasdo_fsm_state_fnc_t)(struct vcasdo_fsm_t *fsm, const struct canmsg_t *in_msg);

enum {sdofsmMsgRefuse = -1, sdofsmMsgEat, sdofsmMsgEatError};
enum {sdofsmIdle = 0, sdofsmRun, sdofsmDone, sdofsmError, sdofsmAbort};
enum {sdofsmErrNone = 0, sdofsmErrTimeout, sdofsmErrFsmExists};
enum {sdofsmDoneUpload = 0, sdofsmDoneDownload};

/**
 * struct vcasdo_fsm_t - structure representing SDO FSM
 * @srvcli_cob_id:  SDO server-client COB_ID (default is 0x580 + node), port on which master listen
 * @clisrv_cob_id:  SDO client-server COB_ID (default is 0x600 + node), port on which slave listen
 * @node:           CANopen node number
 * @index:          index of communicated object
 * @subindex:       subindex of communicated object
 * @is_server:      type of FSM client or server (Master or Slave) (internal use)
 * @is_uploader:      processing upload/download in state sdofsmRun, sdofsmDone
 * @state:          state of SDO (%sdofsmIdle = 0, %sdofsmRun, %sdofsmDone, %sdofsmError, %sdofsmAbort)
 * @err_no:         error number in state %sdofsmError.
 * @data:           uploaded/downloaded bytes (see %ul_dbuff.h)
 * @out_msg:        if vcasdo_taste_msg() generates answer, it is stored in the %out_msg
 * @statefnc:       pointer to the state function (internal use)
 * @last_activity:  time of last FSM activity (internal use)
 * @bytes_to_load:  number of stil not uploaded SDO data bytes (internal use)
 * @toggle_bit:     (internal use)
 * @rx_cob_id:      COBID on which fsm listen
 * @tx_cob_id:      COBID on which fsm talk to CAN
*/
typedef struct vcasdo_fsm_t {
    unsigned srvcli_cob_id;
    unsigned clisrv_cob_id;
    unsigned node;
    unsigned index, subindex;
    
    struct timeval last_activity; 
    
    int bytes_to_load; 
    unsigned char toggle_bit;
    
    char is_server; /* client/server */ 
    char is_uploader; /* upload/download */
    int state;
    vcasdo_fsm_state_fnc_t *statefnc;

    int err_no;
    
    ul_dbuff_t data;
    canmsg_t out_msg; 
} vcasdo_fsm_t;

void vcasdo_init_fsm(vcasdo_fsm_t *fsm, unsigned srvcli_port, unsigned clisrv_port, unsigned node); 

void vcasdo_destroy_fsm(vcasdo_fsm_t *fsm);

int vcasdo_fsm_upload(vcasdo_fsm_t *fsm, int node, unsigned index, byte subindex, unsigned srvcli_cobid, unsigned clisrv_cobid);
int vcasdo_fsm_download(vcasdo_fsm_t *fsm, ul_dbuff_t *data, int node, unsigned index, byte subindex, unsigned srvcli_cobid, unsigned clisrv_cobid);

/**
 * vcasdo_fsm_upload1 - starts SDO upload using parameters set by previous calling vcasdo_init_fsm()
 * @fsm:    FSM to work with
 */
static inline int vcasdo_fsm_upload1(vcasdo_fsm_t *fsm) {
    return vcasdo_fsm_upload(fsm, fsm->node, fsm->index, fsm->subindex, fsm->srvcli_cob_id, fsm->clisrv_cob_id);
}
/**
 * vcasdo_fsm_download1 - starts SDO download using parameters set by previous calling vcasdo_init_fsm()
 * @fsm:    FSM to work with
 * @data:   pointer to &ul_dbuff_t structure where downloaded data will be stored
 */
static inline int vcasdo_fsm_download1(vcasdo_fsm_t *fsm, ul_dbuff_t *data) {
    return vcasdo_fsm_download(fsm, data, fsm->node, fsm->index, fsm->subindex, fsm->srvcli_cob_id, fsm->clisrv_cob_id);
}

/**
 * vcasdo_read_multiplexor - reads index and subindex from multiplexor part of CANopen mesage
 * @mult:  pointer to the multiplexor part of CANopen mesage
 * @index: pointer to place to store read index
 * @subindex: pointer to place to store read subindex
 */
void vcasdo_read_multiplexor(const byte *mult, unsigned *index, unsigned *subindex);

void vcasdo_fsm_run(vcasdo_fsm_t *fsm);
void vcasdo_fsm_idle(vcasdo_fsm_t *fsm);
void vcasdo_fsm_abort(vcasdo_fsm_t *fsm, uint32_t abort_code);

int vcasdo_fsm_taste_msg(vcasdo_fsm_t *fsm, const canmsg_t *msg);

const char* vcasdo_error_msg(int err_no);
            
#ifdef __cplusplus
} /* extern "C"*/
#endif

#endif
