
#ifdef  CANDRV

#include <candriver.h>
#include <Arduino.h>
#include <main.h>
#include <utils.h>

#if defined(ARDUINO_ARCH_STM32)
#include <STM32_CAN.h>
STM32_CAN STMCan( CAN1, CAN_PINS::DEF, RX_SIZE_64, TX_SIZE_16 );
#endif

#if defined(ARDUINO_ARCH_ESP32)
#include <CAN.h>
#endif
#if defined(__SAM3X8E__)
#include <due_can.h>
#endif

#include <config.h>
//#include <systemconfigdata.h>
extern systemConfig sysConf;
extern canStream CANConfStream; 
extern aJsonObject * root;
extern volatile int8_t configLocked;
extern bool configLoaded;


/**
 * @brief Prints the data contained in a CAN frame.
 * 
 * @param frame Pointer to the datagram_t structure containing the CAN frame.
 * @param len Length of the data to print.
 */
void printFrame(datagram_t * frame, uint8_t len ) {

   debugSerial.print(" Data:");
   for (int count = 0; count < len; count++) {
       debugSerial.print(frame->data[count], HEX);
       debugSerial.print(" ");
   }
   debugSerial.println();
}


/**
 * @brief Sends the uptime metric to the CAN bus.
 * 
 * @param ut Uptime value to send.
 * @return true if the message was sent successfully, false otherwise.
 */
bool canDriver::upTime(uint32_t ut)
{
  if (!controllerId) return false;  
 canid_t id;
 datagram_t packet;

 id.reserve=0;
 id.status=1;
 id.payloadType=payloadType::metric;  
 id.deviceId=controllerId;
 id.subjId=metricType::UpTime;


 packet.metric1=ut;

 debugSerial<<("CAN: UpTime")<<endl;   
 return write (id.id, &packet, 4);
}

/**
 * @brief Sends the salt metric to the CAN bus.
 * 
 * @param salt Salt value to send.
 * @return true if the message was sent successfully, false otherwise.
 */
bool canDriver::salt(uint32_t salt)
{
 if (!controllerId) return false;  
 canid_t id;
 datagram_t packet;

 id.reserve=0;
 id.status=1;
 id.payloadType=payloadType::metric;  
 id.deviceId=controllerId;
 id.subjId=metricType::Salt;



 packet.metric1=salt;

 debugSerial<<("CAN: Salt")<<endl;   
 return write (id.id, &packet, 4);
}

/**
 * @brief Looks up the MAC address and sends it over the CAN bus.
 * 
 * @return true if the MAC address was sent successfully, false otherwise.
 */
bool canDriver::lookupMAC()
{
   // return 0;
    canid_t id;
 datagram_t packet;
 bool       res;

 id.reserve=0;
 id.status=0;
 id.payloadType=payloadType::lookupMAC;  
 id.deviceId=0;
 id.subjId=0; //CRC?



 memcpy(packet.mac,sysConf.mac,6);

 debugSerial<<("CAN: Lookup MAC")<<endl;   
  res=write (id.id, &packet, 6);
 if (res) state=canState::MACLookup; 
    else  state=canState::Error; 
    responseTimer=millisNZ();
 return res;
}

/**
 * @brief Requests a frame from the specified device on the CAN bus.
 * 
 * @param devId Device ID to request the frame from.
 * @param _payloadType Type of payload to request.
 * @param seqNo Sequence number for the request.
 * @return true if the request was successful, false otherwise.
 */
bool canDriver::requestFrame(uint8_t devId, payloadType _payloadType, uint16_t seqNo )
{
    canid_t id;
 datagram_t packet;
 bool       res;


 id.reserve=0;
 id.status=0;
 id.payloadType=_payloadType;  
 id.deviceId=devId;
 id.subjId=seqNo; 


packet.metric1 =0;
 //memcpy(packet.mac,sysConf.mac,6);
//delay (100);
 debugSerial<<("CAN: Request frame ")<<_payloadType<<F(" for id ")<<devId<<F(" seq:")<<seqNo<<endl;   
  res=write (id.id,&packet,1);
 if (res) state=canState::FrameRequested; 
    else  state=canState::Error; 
    responseTimer=millisNZ();
 return res;
}

/**
 * @brief Sends the remote ID of a device identified by its MAC address.
 * 
 * @param mac MAC address of the device.
 * @return true if the remote ID was sent successfully, false otherwise.
 */
bool canDriver::sendRemoteID(macAddress mac)
{
 canid_t id;
 //datagram_t packet;
 bool       res=false;
 id.subjId=0; 
 id.deviceId=getIdByMac(mac); //Retrieved controllerID
 if (!id.deviceId) return false;
 aJsonObject * config=getConfbyID(id.deviceId);
 if (config) id.subjId=getCRC(config);   //CRC16 of remote config

 id.reserve=0;
 id.status=1; //response
 id.payloadType=payloadType::lookupMAC;  




 debugSerial<<("CAN: Send remote ID")<<endl;   
 res = write (id.id,(datagram_t*)mac,6);
 if (res) state=canState::Idle; 
    else  state=canState::Error; 
//    responseTimer=millisNZ(); ????????
return res;
}


/**
 * @brief Initializes the CAN driver and sets up the CAN bus.
 * 
 * @return true if initialization was successful, false otherwise.
 */
bool canDriver::begin()
            { 
            if (root)
            {
                canConfigObj =  aJson.getObjectItem(root, "can");
                if (canConfigObj) 
                    {
                    canRemoteConfigObj=  aJson.getObjectItem(canConfigObj, "conf");
                    controllerId = getMyId(); 
                    }
            confCRC=getCRC(root);        
            }
            

                #ifndef NOIP    
                if (!canConfigObj) return false;    
                #endif     

                            if (!ready) // not reInitialization
                            {
                            ready=false;    
                            //STM32  
                            #if defined(ARDUINO_ARCH_STM32)
                            //Can= new STM32_CAN( CAN1, ALT );
                            //if (!Can) return false;
                            STMCan.begin(); //with retransmission
                            STMCan.setBaudRate(50000);
                            // STMCan.setFilter( 0, 0x153, 0x1FFFFFFF );
                            #endif

                            //ESP
                            #if defined(ARDUINO_ARCH_ESP32)
                            CAN.setPins(GPIO_NUM_35,GPIO_NUM_5);//(rx, tx); 
                            // start the CAN bus at 500 kbps
                            if (!CAN.begin(50000)) {return false;  }
                            #endif    

                            #if defined(__SAM3X8E__)
                            Can0.begin(CAN_BPS_50K);

                            int filter;
                            //extended
                            for (filter = 0; filter < 3; filter++) {
                                Can0.setRXFilter(filter, 0, 0, true);
                                //Can1.setRXFilter(filter, 0, 0, true);
                            }  
                            #endif
                            ready=true;
                            }
            debugSerial<<"CAN: initialized. addr="<<controllerId<<endl;
            return true;
             }

/**
 * @brief Reads a frame from the CAN bus.
 * 
 * @return Length of the received frame, or -1 if no frame was received.
 */
int canDriver::readFrame()
{
 if (!ready) return -1;
                //STM32 
               #if defined(ARDUINO_ARCH_STM32)
               if (STMCan.read(CAN_RX_msg)) 
               {
                if (CAN_RX_msg.len>8) CAN_RX_msg.len=8;
                memcpy(RXpacket.data, CAN_RX_msg.buf,CAN_RX_msg.len);
                RXlen = CAN_RX_msg.len;
                RXid.id  = CAN_RX_msg.id;
                return RXlen;
               }
               #endif

               #if defined(ARDUINO_ARCH_ESP32) 
            // try to parse packet
            int packetSize = CAN.parsePacket();

            if (packetSize ){//|| CAN.packetId() != -1) {
                // received a packet
                debugSerialPort.print("CAN: Received ");

                if (CAN.packetExtended()) {
                debugSerialPort.print("extended ");
                }

                if (CAN.packetRtr()) {
                // Remote transmission request, packet contains no data
                debugSerialPort.print("RTR ");
                }

                debugSerialPort.print("packet with id 0x");
                debugSerialPort.print(CAN.packetId(), HEX);

                if (CAN.packetRtr()) {
                debugSerialPort.print(" and requested length ");
                debugSerialPort.println(CAN.packetDlc());
                } else {
                debugSerialPort.print(" and length ");
                //debugSerialPort.println(packetSize);
                debugSerialPort.println(CAN.packetDlc());

                
                // only print packet data for non-RTR packets
                RXlen=0;
                while (CAN.available()) {
                    RXpacket.data[RXlen++]=CAN.read();
                    if (RXlen>=8) break;
                }
                RXid.id = CAN.packetId();

                debugSerialPort.println();
                return RXlen;
                }
               }
                #endif


                //DUE
                #if defined(__SAM3X8E__)
                CAN_FRAME CAN_RX_msg;
                if (Can0.available() > 0) {
                                            
                                                Can0.read(CAN_RX_msg); 
                                                if (CAN_RX_msg.length>8) CAN_RX_msg.length=8;
                                                memcpy(RXpacket.data, CAN_RX_msg.data.bytes,CAN_RX_msg.length);
                                                RXlen = CAN_RX_msg.length;
                                                RXid.id  = CAN_RX_msg.id;
                                                return RXlen;
                                            } 

                #endif

        return -1;
}

/**
 * @brief Polls the CAN bus for incoming frames and processes them.
 */
void canDriver::Poll()
            {  

if (readFrame()>=0) processPacket( RXid, &RXpacket, RXlen);
//State machine

switch (state)
{
    case canState::MACLookup:
   // case canState::FrameRequested:

    if (isTimeOver(responseTimer,millis(),1000UL)) 
                            {
                            responseTimer=millisNZ();
                            state=canState::Error;
                            errorSerial<<"CAN: lookup Timeout"<<endl;
                            }
    break;

    case canState::Error:
        if (configLoaded) state=canState::ConfigLoaded;
           else 
                if (isTimeOver(responseTimer,millis(),10000UL)) 
                     lookupMAC();
    break;

    case canState::ReadConfig:
    {
    //Blocking read config
    if (configLocked) return; // only in safe moments
    configLocked++;

    infoSerial<<F("CAN: Requesting Config")<<endl;

    CANConfStream.open(controllerId,payloadType::configFrame,'r');

    if (CANConfStream.peek() == '{') {
                debugSerial<<F("CAN: JSON detected")<<endl;
                cleanConf(1);
                aJsonStream as = aJsonStream(&CANConfStream);
                root = aJson.parse(&as);
                CANConfStream.close();
                if (!root) {
                    errorSerial<<F("CAN: config load failed")<<endl;
                    sysConf.setETAG("");
                //    sysConfStream.close();
                    configLocked--;
                    state = canState::Error;
                    return;
                }
                infoSerial<<F("CAN: config Loaded")<<endl;
                configLocked--;
                cmdFunctionSave(0,NULL);
                if (applyConfig()) ;
      //                  debugSerial.print(aJson.print(root,false));
                sysConf.loadETAG();
                state = canState::Idle;
                return ;
            } 
            CANConfStream.close();   
            infoSerial<<F("CAN: Config not loaded")<<endl; 
            state = canState::Error;
            configLocked--;
    }        
    break;

//    case canState::Idle:

}
}

/**
 * @brief Processes a received CAN packet.
 * 
 * @param id Identifier of the CAN packet.
 * @param packet Pointer to the received datagram_t structure.
 * @param len Length of the received packet.
 * @param rtr Indicates if the packet is a remote transmission request.
 * @return true if the packet was processed successfully, false otherwise.
 */
bool canDriver::processPacket(canid_t id, datagram_t *packet, uint8_t len, bool rtr)
{

traceSerial.print("CAN: Rcvd ");
traceSerial.print(len);
traceSerial.print(" bytes id:");
traceSerial.println(id.id,HEX);

//if (id.deviceId && (id.deviceId != controllerId) && !id.status) return false;

debugSerial<<"CAN: " ;
if (id.status) debugSerial.print("Resp "); else debugSerial.print("Req ");
debugSerial<<"(devId:"<< id.deviceId <<" ";
switch ((id.payloadType)){
    case payloadType::itemCommand:
    debugSerial.print("itemCmd");
    debugSerial<<" itemId:"<< id.itemId;
    if (id.subItemId!=NO_SUBITEM) debugSerial << " subItem:"<<id.subItemId;
    
    if (id.deviceId && (id.deviceId != controllerId) && !id.status) //ignore requests with NON ZERO devId where another CANID
                        {
                           debugSerial.println(")");
                           return false; 
                        }    

    break;
    case payloadType::lookupMAC:
    debugSerial.print("lookupMAC");
    if (id.status && (memcmp(packet->mac, sysConf.mac,6)))  //Ignore responses for another controller
                        {
                           debugSerial.println(")");
                           return false; 
                        }
    break;
    case payloadType::configFrame:
    debugSerial.print("configFrame #");
    debugSerial<< id.subjId;
    if (id.status && (id.deviceId != controllerId)) //Ignore responses on config request not for me
                        {
                           debugSerial.println(")");
                           return false; 
                        }

    break;
    case payloadType::OTAFrame:
    debugSerial.print("OTAFrame #");
      debugSerial<< id.subjId;
    break;
    case payloadType::auth:
    debugSerial.print("auth #");
    debugSerial<< id.subjId;
    break;
    case payloadType::metric:
    debugSerial.print("metric #");
    debugSerial<< id.subjId;
    break;
    case payloadType::sysCmd:
    debugSerial.print("sysCmd");
     if (id.deviceId != controllerId) //Ignore commands not for me
                        {
                           debugSerial.println(")");
                           return false; 
                        }

    break;
    case payloadType::rawPinCtrl:
    debugSerial.print("rawPinCtrl");
     if (id.deviceId != controllerId) //Ignore commands not for me
                        {
                           debugSerial.println(")");
                           return false; 
                        }    
}
debugSerial<< ") ";

if (len) printFrame(packet,len);
if (id.status){
    //Responces
    //@ any state
    switch (id.payloadType)
    {
        case payloadType::itemCommand:
        {
        if (len!=8) return false;    
        aJsonObject  *confObj = getConfbyID(id.deviceId);
        if (confObj)
            {
                debugSerial<<F("CAN: status received for dev ")<<id.deviceId<<endl;
                //char * itemNName = findItemName(id.itemId);
                aJsonObject  *itemsObj = aJson.getObjectItem(confObj,"items");
                if (!itemsObj) return false;
                Item it(id.itemId, id.subjId, itemsObj);
                if (it.isValid())
                    {
                        itemCmd ic;
                        uint16_t flags = 0;
                        ic.cmd = packet->cmd;
                        ic.param = packet->param;

                        debugSerial<<F("CAN: item ")<<it.itemArr->name<<" ";
                        ic.debugOut();
                        
                        if (ic.isValue()) flags |= FLAG_PARAMETERS;
                        if (ic.getSuffix()==S_DELAYED) flags |= FLAG_SEND_DELAYED;
                           else if (ic.isCommand()) flags |= FLAG_COMMAND;

                        ic.saveItem(&it,flags);
                        it.SendStatusImmediate(ic,flags | FLAG_NOT_SEND_CAN, it.getSubItemStrById(id.subItemId));
                        return true;
                    }

            }

        }    
        break;
        
    }

    switch (state)
    {
        case canState::MACLookup:
        if ((id.payloadType == payloadType::lookupMAC))
           {
                if (root && (id.subjId == confCRC)) ///?
                    {
                        infoSerial << (F("Valid config already onboard")) << endl;
                        state = canState::Idle;
                    }
                else 
                    {
                        infoSerial<<"\nCAN: Got Controller addr: "<<id.deviceId<<endl;
                        state = canState::ReadConfig;
                        controllerId=id.deviceId;
                    }
           }
        return true; 

        case canState::FrameRequested:
        if ((id.payloadType == payloadType::configFrame) && (id.deviceId == controllerId))
          {
                errorSerial<<F("CAN: Config received when not expected")<<endl;
          }

        break;
        case canState::Idle:

        break;

        case canState::Error:
        return false;
    }
}   
else //Requests
    {
        /*
            switch (id.payloadType)
    {
            case payloadType::itemCommand:
            break;

            case payloadType::sysCmd:
            break;
        
    }*/
        if ((id.payloadType == payloadType::itemCommand) && (len ==8))
            {
                Item it(id.itemId,id.subItemId);
                if (!it.isValid()) return false;

                itemCmd ic;
                ic.cmd = packet->cmd;
                ic.param = packet->param;   
                         //debugSerial<<F("CAN: itemCmd: ");
                        //ic.debugOut();
                return it.Ctrl(ic,it.getSubItemStrById(id.subItemId));
            }
        else if ((id.payloadType == payloadType::lookupMAC) && (len>=6))
            {             
               return sendRemoteID(packet->mac);
               //debugSerial<<"ID requested"<<endl;
            }
        else if ((id.payloadType == payloadType::configFrame) && (id.subjId == 0xFFFF))
            {  
                debugSerial<<F("CAN: Requested conf for dev#")<<id.deviceId<<endl;
                aJsonObject * remoteConfObj = getConfbyID(id.deviceId);
                if (remoteConfObj)
                {
                        infoSerial<<F("CAN: Sending conf for dev#")<<id.deviceId<<endl;
                        CANConfStream.open(id.deviceId,payloadType::configFrame,'w');
                        aJsonStream outStream = aJsonStream(&CANConfStream);
                        aJson.print(remoteConfObj, &outStream); 
                        CANConfStream.close();
                }

               return 1;
               //debugSerial<<"ID requested"<<endl;
            }

    }
return false;
}

/**
 * @brief Retrieves the device ID of this CAN driver.
 * 
 * @return The device ID, or 0 if not set.
 */
uint8_t canDriver::getMyId()
{
if (!canConfigObj) return 0;
aJsonObject * addrObj = aJson.getObjectItem(canConfigObj, "addr");
if (addrObj && (addrObj->type == aJson_Int)) return addrObj->valueint;
return 0;
}

/**
 * @brief Retrieves the configuration object for a device by its ID.
 * 
 * @param devId Device ID to look up.
 * @return Pointer to the configuration object, or NULL if not found.
 */
aJsonObject * canDriver::getConfbyID(uint8_t devId)
{
if (!canConfigObj) return NULL;
if (!canRemoteConfigObj || canRemoteConfigObj->type != aJson_Object) return NULL;
aJsonObject * remoteConfObj=canRemoteConfigObj->child; 
while (remoteConfObj)
    {  
        aJsonObject * remoteCanObj =  aJson.getObjectItem(remoteConfObj, "can");
        if (remoteCanObj)
        {
        aJsonObject * addrObj = aJson.getObjectItem(remoteCanObj, "addr");
        if (addrObj && (addrObj->type == aJson_Int) && (addrObj->valueint == devId)) return remoteConfObj;
        }
        remoteConfObj=remoteConfObj->next;
    }
return NULL;    
}

/**
 * @brief Finds a configuration object by device name.
 * 
 * @param devName Name of the device to look for.
 * @param devAddr Pointer to store the device address if found.
 * @return Pointer to the configuration object, or NULL if not found.
 */
aJsonObject * canDriver::findConfbyName(char* devName, int * devAddr)
{
if (!canRemoteConfigObj || canRemoteConfigObj->type != aJson_Object || !devName ) return NULL;
aJsonObject * remoteConfObj=canRemoteConfigObj->child; 
while (remoteConfObj)
    {  
        aJsonObject * remoteCanObj =  aJson.getObjectItem(remoteConfObj, "can");
        if (remoteCanObj)
        {
        aJsonObject * nameObj = aJson.getObjectItem(remoteCanObj, "name");
        if (nameObj && (nameObj->type == aJson_String) && nameObj->valuestring && (strncasecmp(nameObj->valuestring,devName,strlen(nameObj->valuestring)) == 0)) 
         {   
            if (devAddr) 
            {
              aJsonObject * addrObj = aJson.getObjectItem(remoteCanObj, "addr");
              if (addrObj && (addrObj->type == aJson_Int)) *devAddr=addrObj->valueint;
            }
            return remoteConfObj;
         }    
        }
        remoteConfObj=remoteConfObj->next;
    }
return NULL;    
}

#if not defined (NOIP)   
extern PubSubClient mqttClient;

/**
 * @brief Subscribes to MQTT topics based on the CAN configuration.
 * 
 * @param root Pointer to the root topic string.
 * @param buflen Length of the buffer for the topic string.
 * @return true if subscription was successful, false otherwise.
 */
bool canDriver::subscribeTopics(char * root, size_t buflen)
{
 if (!root) return false;   
 if (!canRemoteConfigObj || canRemoteConfigObj->type != aJson_Object) return false;

 int rootLen = strlen(root);     

aJsonObject * remoteConfObj=canRemoteConfigObj->child; 
while (remoteConfObj)
    {  
        aJsonObject * remoteCanObj =  aJson.getObjectItem(remoteConfObj, "can");
        if (remoteCanObj)
        {
        aJsonObject * addrObj = aJson.getObjectItem(remoteCanObj, "name");
        if (addrObj && (addrObj->type == aJson_String) && addrObj->valuestring)
                {
                    strncpy(root+rootLen, addrObj->valuestring, buflen-rootLen-1);
                    strncat(root+rootLen, "/", buflen-rootLen-1);
                    strncat(root+rootLen, "#", buflen-rootLen-1);
                    debugSerial.print("CAN: subscribe ");
                    debugSerial.println(root);
                    mqttClient.subscribe(root);                
                }   
        }
        remoteConfObj=remoteConfObj->next;
    }
 //debugSerial<<"Subscribed"<<endl;
 //delay(100);   
 return true;   
}
#endif

/**
 * @brief Retrieves the device ID associated with a given MAC address.
 * 
 * @param mac MAC address to look up.
 * @return The device ID, or 0 if not found.
 */
uint8_t canDriver::getIdByMac(macAddress mac)
{
char macStr[19];
uint8_t strptr = 0;

if (!canRemoteConfigObj) return 0;
memset(macStr,0,sizeof(macStr));
for (byte i = 0; i < 6; i++)
        {
        SetBytes(&mac[i],1,&macStr[strptr]);
        strptr+=2;
        if (i < 5) macStr[strptr++]=':';
        }
debugSerial<<F("CAN: Searching devId for ")<<macStr<<endl;
aJsonObject * remoteConfObj =  aJson.getObjectItem(canRemoteConfigObj, macStr);

if (!remoteConfObj) return 0;

aJsonObject * remoteCanObj = aJson.getObjectItem(remoteConfObj, "can");
 if (!remoteCanObj) return 0;

aJsonObject * addrObj = aJson.getObjectItem(remoteCanObj, "addr");
 if (!addrObj) return 0;
if (addrObj && (addrObj->type == aJson_Int)) 
            {
            debugSerial<<F("CAN: find dev#")<< addrObj->valueint << endl;   
            return addrObj->valueint;
            }

return 0;
}

/**
 * @brief Sends a message over the CAN bus.
 * 
 * @param msg_id Identifier for the message.
 * @param buf Pointer to the datagram_t structure containing the message data.
 * @param size Size of the message data.
 * @return true if the message was sent successfully, false otherwise.
 */
bool canDriver::write(uint32_t msg_id, datagram_t * buf, uint8_t size)
            {   //
                if (!ready) {
                            errorSerial<<"CAN: not initialized"<<endl;
                            return false;
                            }
                bool res;
                if (size>8) size = 8;

                //STM32 
                #if defined(ARDUINO_ARCH_STM32)
                //if (!Can) return 0;
                if (buf) for(uint8_t i=0;i<size; i++) CAN_TX_msg.buf[i]=buf->data[i];
                CAN_TX_msg.id = msg_id;
                CAN_TX_msg.flags.extended = 1;  // To enable extended ID
                CAN_TX_msg.len=size;
                if (res=STMCan.write(CAN_TX_msg)) {traceSerial<<("CAN: Wrote ")<<size<<" bytes, id "<<_HEX(msg_id)<<endl;}
                    else debugSerial.println("CAN: Write error");
                return res;    
                #endif

                //ESP
                #if defined(ARDUINO_ARCH_ESP32)
                CAN.beginExtendedPacket(msg_id,size);
                CAN.write(buf->data,size);
                //for(uint8_t i=0;i<size; i++) CAN.write(buf[i]);
                if (res=CAN.endPacket()) {traceSerial<< ("CAN: Wrote ")<<size << " bytes, id "<<_HEX(msg_id)<<endl;}
                    else debugSerial.println("CAN: Write error");
                return res;
                #endif

                #if defined(__SAM3X8E__)
                CAN_FRAME CAN_TX_msg;
	            CAN_TX_msg.id = msg_id;
                CAN_TX_msg.extended = true;
                CAN_TX_msg.length=size;
                //outgoing.priority = 4; //0-15 lower is higher priority
                if (buf) for(uint8_t i=0;i<size; i++) CAN_TX_msg.data.bytes[i]=buf->data[i];
                res=Can0.sendFrame(CAN_TX_msg);
                if (res) {traceSerial<<F("CAN: Wrote ")<<size<<" bytes, id "<<_HEX(msg_id)<<endl;}
                                                    else errorSerial.println("CAN: Write error");
                return res;
                #endif
        }
    




/**
 * @brief Sends the status of a specified item.
 * 
 * @param itemNum Item number to send the status for.
 * @param cmd Command structure containing the status information.
 * @param subItem Sub-item identifier.
 * @return true if the status was sent successfully, false otherwise.
 */
bool canDriver::sendStatus(uint16_t itemNum, itemCmd cmd, int subItem)
{
 if (!itemNum || !controllerId) return false;
 return sendCommand(controllerId, itemNum, cmd, true, subItem);
}

/**
 * @brief Sends a command to a specified CAN device.
 * 
 * @param can Pointer to the configuration object for the device.
 * @param cmd Command structure containing the command information.
 * @param status Indicates if the command is a status update.
 * @return true if the command was sent successfully, false otherwise.
 */
bool canDriver::sendCommand(aJsonObject * can, itemCmd cmd, bool status)
{    
     if (can && (can->type == aJson_Array))
       {
        int subItem=NO_SUBITEM;
        aJsonObject * dev = aJson.getArrayItem(can,0);
        aJsonObject * it = aJson.getArrayItem(can,1);
        aJsonObject * sfx = aJson.getArrayItem(can,2);
        aJsonObject * subItemObj = aJson.getArrayItem(can,3);
        if (sfx)
            switch (sfx->type)
            {
             case  aJson_Int: cmd.setSuffix(sfx->valueint);
             break;
             case  aJson_String:
             int suffix=txt2subItem(sfx->valuestring);
             if (suffix) cmd.setSuffix(suffix);
            }

         if (subItemObj)
            switch (subItemObj->type)
            {
             case  aJson_Int: 
                   if (subItemObj->valueint>=0 && subItemObj->valueint<SUBITEM_IS_COMMAND) subItem=subItemObj->valueint;
             break;
             case  aJson_String:
             int suffix=txt2cmd(subItemObj->valuestring);
             if (suffix) subItem = suffix | SUBITEM_IS_COMMAND;
            }   

        if (dev && it && dev->type == aJson_Int && it->type == aJson_Int)
            return sendCommand(dev->valueint, it->valueint, cmd, status,subItem); 
       }  
      return false;  
}

/**
 * @brief Sends a command to a specified device by ID.
 * 
 * @param devID Device ID to send the command to.
 * @param itemID Item ID to send the command for.
 * @param cmd Command structure containing the command information.
 * @param status Indicates if the command is a status update.
 * @param subItemID Sub-item identifier.
 * @return true if the command was sent successfully, false otherwise.
 */
bool canDriver::sendCommand(uint8_t devID, uint16_t itemID,itemCmd cmd, bool status,int subItemID )
{
    canid_t id;
 datagram_t packet;
 bool       res;


 id.reserve=0;
 id.status=status?1:0;
 id.payloadType=payloadType::itemCommand;  
 id.deviceId=devID;
 id.itemId=itemID;
 id.subItemId=subItemID;
 
 packet.cmd = cmd.cmd;
 packet.param = cmd.param;
 
 debugSerial << ((status)?"CAN: Send Status":"CAN: Send Command");
 debugSerial<<F(" ->[")<<devID<<":"<<itemID<<"] "; 
 if (subItemID!=NO_SUBITEM)  debugSerial<<F("Subitem:")<<subItemID<<" "; 
 cmd.debugOut();

  res=write (id.id,&packet,8);

 //if (res) debugSerial<<F("CAN: sent ok")<<endl;
 //   else  debugSerial<<F("CAN: fail")<<endl;

 return res;
}



////////////////////////////// Steream //////////////////////////


    /**
     * @brief Sends data over the CAN stream.
     * 
     * @param len Length of the data to send.
     * @param _seqNo Sequence number for the data.
     * @return 1 if the data was sent successfully, 0 otherwise.
     */
    int canStream::send(uint8_t len, uint16_t _seqNo)
    {  
                    canid_t id;
                    datagram_t packet;
                    bool       res;
                    if (!driver) return 0;
                    id.reserve=0;
                    id.status=1;
                    id.payloadType=pType;  
                    id.deviceId=devId;
                    id.subjId=_seqNo; 

                    res=driver->write (id.id, &writeBuffer, len);
                    writePos=0;
                    if (res) 
                            {
                            //Await check?    
                            return 1; 
                            }
                       else return 0; 
    }               

    /**
     * @brief Checks the state of the CAN stream and processes any incoming data.
     * 
     * @return -1 on error, or the number of bytes available for reading.
     */
     int canStream::checkState() 
     {
      bool res = false;  
      if (!driver) return -1;
        switch (state)
        {
            case canState::StreamOpenedRead:
                       readPos = 0;
                       res= driver->requestFrame(devId,pType,seqNo); //Requesting frame; 
                       if (res)
                            state = canState::FrameRequested;    
                          else   
                            {
                            state = canState::Error;       
                            return -1;  
                            }    
            if (seqNo ==0xFFFF) seqNo =0;                          
            //continue 

            case canState::FrameRequested:    
            { 
              int c;

              uint32_t timer = millis();  
              do {
                    //debugSerial.print(".");
                    yield();
                    
                    if ((c=driver->readFrame()>0) && (driver->RXid.deviceId == devId) && (driver ->RXid.payloadType == pType) && driver->RXid.status)   
                           {
                            state = canState::FrameReceived; 
                            debugSerial<<F("CAN: Payload received ")<< c << "|" <<driver->RXlen<< " "<<driver->RXpacket.payload<<"#"<<driver->RXid.subjId<<endl;
                            seqNo=driver->RXid.subjId;
                            failedCount=0;
                            return driver->RXlen; 
                           }

                 } while((!isTimeOver(timer,millis(),1000UL)) );
                
                debugSerial<<F("CAN: RX data awaiting timeout #")<<failedCount<<endl;   
                if (failedCount>=3) 
                        {
                            state = canState::Error;       
                            return -1;  
                        }

                failedCount++;
                state = canState::StreamOpenedRead; 
            }    
            return -1;
            break;

            case canState::FrameReceived:
               return driver->RXlen; 
            break;    

            case canState::waitingConfirm:    
              if (driver->readFrame()>=0) 
                        {
                        if (
                            (driver->RXid.deviceId == devId)  &&
                            (driver->RXid.payloadType == pType) &&
                            (driver->RXid.status == 0)
                           )
                        {   
                        debugSerial<<F("CAN: frame confirmed #")<<driver->RXid.subjId<<endl;   
                        if (seqNo == driver->RXid.subjId)
                                {
                                 state = canState::StreamOpenedWrite;
                                 seqNo++;
                                }
                         else
                                {
                                    state = canState::Error;
                                    errorSerial<<F("CAN: wrong seqNum")<<endl;
                                    return -1;
                                }       

                        }
                        
                        return 0; 
                        }
              return driver->RXlen; 
            break;

            case canState::Idle:
               return -1;
            break;           
        }
     return -1;
     };



    // Stream methods
    /**
     * @brief Checks how many bytes are available for reading from the CAN stream.
     * 
     * @return Number of bytes available, or -1 on error.
     */
     int canStream::available() 
     {
        if (!driver) return -1;
        int avail = checkState();
        return avail;      
     };

    /**
     * @brief Reads a byte from the CAN stream.
     * 
     * @return The byte read, or -1 on error.
     */
     int canStream::read() 
     {  
        if (!driver) return -1;
        int avail = checkState();
        int ch;
       if (avail>=0) 
                {   
                 ch = driver->RXpacket.data[readPos++];
                 if (readPos>=8) state = canState::StreamOpenedRead;
                 return ch;
                }
           else return -1;
     };

    /**
     * @brief Peeks at the next byte in the CAN stream without removing it.
     * 
     * @return The next byte, or -1 on error.
     */
     int canStream::peek() 
     {
        if (!driver) return -1;
        int avail = checkState();
        int ch;
       if (avail>=0) 
                {   
                 ch = driver->RXpacket.data[readPos];
                 return ch;
                }
           else return -1;
     };



    /**
     * @brief Writes a byte to the CAN stream.
     * 
     * @param c The byte to write.
     * @return The number of bytes written, or -1 on error.
     */
      size_t canStream::write(uint8_t c) 
    { 
        //if ((state != canState::StreamOpenedWrite) || (state != canState::waitingConfirm)) return -1;

        uint32_t timer = millis();
        do 
         {
         checkState();
         yield();
         //debugSerial.print("*");
         if (isTimeOver(timer,millis(),1000UL))
            {
                 state = canState::Error;
                 errorSerial<<F("CAN: write timeout")<<endl;
                 return -1;
            }            

         }
        while (!availableForWrite() );

        writeBuffer.data[writePos++]=c;
        if (writePos>=8) 
            {
                bool res = send(8,seqNo);
                if (res) state = canState::waitingConfirm;
                    else state = canState::Error;
                return res;
            }
         return 1; };

        /**
         * @brief Flushes the CAN stream, sending any buffered data.
         */
           void canStream::flush() 
             { 
                send(writePos,seqNo);
             };


/**
 * @brief Checks if the CAN stream is available for writing.
 * 
 * @return 1 if available, 0 if waiting for confirmation.
 */
 int canStream::availableForWrite()
{
 switch (state)
 {
    case canState::waitingConfirm: return 0;
 }   
return 1;
}
#endif 
