/*
;    Project:       Open Vehicle Monitor System
;    Date:          14th March 2017
;
;    Changes:
;    1.0  Initial release
;
;    (C) 2011       Michael Stegen / Stegen Electronics
;    (C) 2011-2017  Mark Webb-Johnson
;    (C) 2011        Sonny Chen @ EPRO/DX
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in
; all copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
; THE SOFTWARE.
;
; Portions of this are based on the work of Thomas Barth, and licensed
; under MIT license.
; Copyright (c) 2017, Thomas Barth, barth-dev.de
; https://github.com/ThomasBarth/ESP32-CAN-Driver
*/

#ifndef __CAN_H__
#define __CAN_H__

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include <stdint.h>
#include <functional>
#include <list>
#include "pcp.h"
#include <esp_err.h>
#include "ovms_events.h"

////////////////////////////////////////////////////////////////////////
// Constant ESP_QUEUED to indicate a 'queued' response
// (rather than ESP_OK or ESP_FAIL, for example)
////////////////////////////////////////////////////////////////////////

#ifndef ESP_QUEUED
#define ESP_QUEUED           1    // frame has been queued for later processing
#endif

////////////////////////////////////////////////////////////////////////
// CAN BUS constants and objects
////////////////////////////////////////////////////////////////////////

#define CAN_MAXBUSES 5            // Limit of number of CAN buses supported

class canbus; // Forward definition

// CAN mode
typedef enum
  {
  CAN_MODE_OFF=0,
  CAN_MODE_LISTEN=1,
  CAN_MODE_ACTIVE=2
  } CAN_mode_t;

// CAN link speed (33.3kbps -> 1MHz)
typedef enum
  {
  CAN_SPEED_33KBPS=33,       // CAN Node runs at 33.333kBit/s
  CAN_SPEED_50KBPS=50,       // CAN Node runs at 50kBit/s
  CAN_SPEED_83KBPS=83,       // CAN Node runs at 83.333kBit/s
  CAN_SPEED_100KBPS=100,     // CAN Node runs at 100kBit/s
  CAN_SPEED_125KBPS=125,     // CAN Node runs at 125kBit/s
  CAN_SPEED_250KBPS=250,     // CAN Node runs at 250kBit/s
  CAN_SPEED_500KBPS=500,     // CAN Node runs at 500kBit/s
  CAN_SPEED_1000KBPS=1000    // CAN Node runs at 1000kBit/s
  } CAN_speed_t;

/* Map CAN_speed_t to a Bit/s value */
#define MAP_CAN_SPEED(s) \
    (((s) == CAN_SPEED_33KBPS || (s) == CAN_SPEED_83KBPS) ? ((((int)(s)) * 1000) + 333) : (((int)(s)) * 1000))

// CAN frame type (standard/extended)
typedef enum
  {
  CAN_frame_std=0,           // Standard frame, using 11 bit identifer
  CAN_frame_ext=1            // Extended frame, using 29 bit identifer
  } CAN_frame_format_t;

// CAN RTR
typedef enum
  {
  CAN_no_RTR=0,              // No RTR frame
  CAN_RTR=1                  // RTR frame
  } CAN_RTR_t;

// CAN Frame Information Record
typedef union
  {
  uint32_t U;                           // Unsigned access
  struct
    {
    uint8_t             DLC:4;          // [3:0] DLC, Data length container
    unsigned int        unknown_2:2;    // internal unknown
    CAN_RTR_t           RTR:1;          // [6:6] RTR, Remote Transmission Request
    CAN_frame_format_t  FF:1;           // [7:7] Frame Format, see# CAN_frame_format_t
    unsigned int        reserved_24:24; // internal Reserved
    } B;
  } CAN_FIR_t;


typedef struct CAN_frame_t CAN_frame_t;
typedef std::function<void(const CAN_frame_t*, bool)> CanFrameCallback;

// CAN Frame
// Note: Take care changing this structure, as it is a union with
// CAN_log_message_t and position of 'origin' is fixed.
struct CAN_frame_t
  {
  canbus*     origin;                   // Origin of the frame
  CanFrameCallback * callback;          // Frame-specific callback. Is called when this frame is successfully sent (or sending failed)
  CAN_FIR_t   FIR;                      // Frame information record
  uint32_t    MsgID;                    // Message ID
  union
    {
    uint8_t   u8[8];                    // Payload byte access
    uint32_t  u32[2];                   // Payload u32 access (Att: little endian!)
    uint64_t  u64;                      // Payload u64 access (Att: little endian!)
    } data;

  esp_err_t Write(canbus* bus=NULL, TickType_t maxqueuewait=0);  // bus: NULL=origin
  };

// CAN status
typedef struct
  {
  uint32_t interrupts;              // interrupts
  uint32_t packets_rx;              // frames reveiced
  uint32_t packets_tx;              // frames sent successfully
  uint32_t txbuf_delay;             // frames routed through TX queue
  uint16_t rxbuf_overflow;          // frames lost due to RX buffers full
  uint16_t txbuf_overflow;          // TX queue overflows
  uint32_t tx_fails;                // TX failures/aborts
  uint32_t error_flags;             // driver specific bitset
  uint16_t errors_rx;               // RX error counter
  uint16_t errors_tx;               // TX error counter
  uint16_t invalid_rx;              // RX invalid frame counter
  uint16_t watchdog_resets;         // Watchdog reset counter
  uint16_t error_resets;            // Error resolving reset counter
  } CAN_status_t;

// CAN error states
typedef enum
  {
  CAN_errorstate_none = 0,          // normal operation, no errors present
  CAN_errorstate_active,            // normal operation, some errors present (< 96 rx/tx errors)
  CAN_errorstate_warning,           // normal operation, many errors present (>= 96 rx/tx errors)
  CAN_errorstate_passive,           // passive mode, no tx retries until bus recovery (>= 128 rx/tx errors)
  CAN_errorstate_busoff             // bus-off mode, no tx/rx until bus recovery (> 255 rx/tx errors)
} CAN_errorstate_t;

extern const char* GetCanErrorStateName(CAN_errorstate_t error_state);

////////////////////////////////////////////////////////////////////////
// CAN messages queue
// This queue is between the CAN bus controller MyCAN and tasks that
// inject/receive CAN bus messages (such as MCP2515 and esp32can)
////////////////////////////////////////////////////////////////////////

// CAN message type
typedef enum
  {
  CAN_frame = 0,
  CAN_asyncinterrupthandler, // used for asynchronous handling of rx and other interrupts from MCP2515
  CAN_txcallback,
  CAN_txfailedcallback,
  CAN_logerror,
  CAN_logstatus
} CAN_queue_type_t;

// CAN message
typedef struct
  {
  CAN_queue_type_t type;
  union
    {
    CAN_frame_t frame;  // CAN_frame
    canbus* bus;
    } body;
  } CAN_queue_msg_t;

////////////////////////////////////////////////////////////////////////
// CAN Filtering (software based filter)
// The canfilter object encapsulates the filtering of CAN frames
////////////////////////////////////////////////////////////////////////

typedef struct
  {
  uint8_t bus;
  uint32_t id_from;
  uint32_t id_to;
  } CAN_filter_t;

typedef std::list<CAN_filter_t*> CAN_filter_list_t;

class canfilter
  {
  public:
    canfilter();
    virtual ~canfilter();

  public:
    void ClearFilters();
    void AddFilter(uint8_t bus=0, uint32_t id_from=0, uint32_t id_to=UINT32_MAX);
    void AddFilter(const char* filterstring);
    bool RemoveFilter(uint8_t bus=0, uint32_t id_from=0, uint32_t id_to=UINT32_MAX);

  public:
    bool IsFiltered(const CAN_frame_t* p_frame);
    bool IsFiltered(canbus* bus);
    std::string Info();

  protected:
    CAN_filter_list_t m_filters;
  };

////////////////////////////////////////////////////////////////////////
// CAN logging and tracing
// These structures are involved in formatting, logging and tracing of
// CAN messages (both frames and status/control messages)
////////////////////////////////////////////////////////////////////////

// Log entry types:
typedef enum
  {
  CAN_LogNone = 0,            // init placeholder
  CAN_LogFrame_RX,            // frame received
  CAN_LogFrame_TX,            // frame transmitted
  CAN_LogFrame_TX_Queue,      // frame delayed
  CAN_LogFrame_TX_Fail,       // frame transmission failure
  CAN_LogStatus_Error,        // canbus error status
  CAN_LogStatus_Statistics,   // canbus packet statistics
  CAN_LogInfo_Comment,        // general comment
  CAN_LogInfo_Config,         // logger setup info (type, file, filters, vehicle)
  CAN_LogInfo_Event,          // system event (i.e. vehicle started)
  } CAN_log_type_t;

// Log message:
typedef struct
  {
  CAN_log_type_t type;
  struct timeval timestamp;
  union
    {
    CAN_frame_t frame;
    struct
      {
      canbus* origin;
      union
        {
        CAN_status_t status;
        char* text;
        };
      };
    };
  } CAN_log_message_t;

extern const char* GetCanLogTypeName(CAN_log_type_t type);

////////////////////////////////////////////////////////////////////////
// canbus - the definition of a CAN bus
////////////////////////////////////////////////////////////////////////

class canlog;
class canplay;
class dbcfile;

class canbus : public pcp, public InternalRamAllocated
  {
  public:
    canbus(const char* name);
    virtual ~canbus();

  public:
    virtual esp_err_t Start(CAN_mode_t mode, CAN_speed_t speed);
    virtual esp_err_t Start(CAN_mode_t mode, CAN_speed_t speed, dbcfile *dbcfile);
    virtual esp_err_t Stop();
    virtual void ClearStatus();
    virtual esp_err_t ViewRegisters();
    virtual esp_err_t WriteReg( uint8_t reg, uint8_t value );

  public:
    void AttachDBC(dbcfile *dbcfile);
    bool AttachDBC(const char *name);
    void DetachDBC();
    dbcfile* GetDBC();

  public:
    virtual esp_err_t Write(const CAN_frame_t* p_frame, TickType_t maxqueuewait=0);
    virtual esp_err_t WriteExtended(uint32_t id, uint8_t length, uint8_t *data, TickType_t maxqueuewait=0);
    virtual esp_err_t WriteStandard(uint16_t id, uint8_t length, uint8_t *data, TickType_t maxqueuewait=0);
    virtual bool AsynchronousInterruptHandler(CAN_frame_t* frame, uint32_t* framesReceived);
    virtual void TxCallback(CAN_frame_t* frame, bool success);

  protected:
    virtual esp_err_t QueueWrite(const CAN_frame_t* p_frame, TickType_t maxqueuewait=0);
    void BusTicker10(std::string event, void* data);

  public:
    void LogFrame(CAN_log_type_t type, const CAN_frame_t* p_frame);
    void LogStatus(CAN_log_type_t type);
    void LogInfo(CAN_log_type_t type, const char* text);
    bool StatusChanged();
    CAN_errorstate_t GetErrorState();
    const char* GetErrorStateName();

  public:
    CAN_speed_t m_speed;
    CAN_mode_t m_mode;
    CAN_status_t m_status;
    CAN_frame_t m_tx_frame;       // saved copy of last TX frame to be used in txcallback
    uint32_t m_status_chksum;
    uint32_t m_watchdog_timer;
    uint32_t m_state;             // state bitset
    QueueHandle_t m_txqueue;
    int m_busnumber;

  protected:
    dbcfile *m_dbcfile;
  };

#define CAN_M_STATE_TX_BUF_OCCUPIED   BIT(0) // transmit buffer is in use

////////////////////////////////////////////////////////////////////////
// can - the CAN system controller
////////////////////////////////////////////////////////////////////////

typedef std::map<QueueHandle_t, bool> CanListenerMap_t;


class CanFrameCallbackEntry
  {
  public:
    CanFrameCallbackEntry(const char* caller, CanFrameCallback callback)
      {
      m_caller = caller;
      m_callback = callback;
      }
    ~CanFrameCallbackEntry() {}
  public:
    const char *m_caller;
    CanFrameCallback m_callback;
  };
typedef std::list<CanFrameCallbackEntry*> CanFrameCallbackList_t;

class can : public InternalRamAllocated
  {
  public:
     can();
     ~can();

  private:
    static void CAN_rxtask(void *pvParameters);

  public:
    void IncomingFrame(CAN_frame_t* p_frame);

  public:
    QueueHandle_t m_rxqueue;

  public:
    void RegisterListener(QueueHandle_t queue, bool txfeedback=false);
    void DeregisterListener(QueueHandle_t queue);
    void NotifyListeners(const CAN_frame_t* frame, bool tx);

  public:
    void RegisterCallback(const char* caller, CanFrameCallback callback, bool txfeedback=false);
    void DeregisterCallback(const char* caller);
    int ExecuteCallbacks(const CAN_frame_t* frame, bool tx, bool success);

  public:
    uint32_t AddLogger(canlog* logger, int filterc=0, const char* const* filterv=NULL);
    bool HasLogger();
    canlog* GetLogger(uint32_t id);
    bool RemoveLogger(uint32_t id);
    void RemoveLoggers();

  public:
    uint32_t AddPlayer(canplay* player, int filterc=0, const char* const* filterv=NULL);
    bool HasPlayer();
    canplay* GetPlayer(uint32_t id);
    bool RemovePlayer(uint32_t id);
    void RemovePlayers();

  public:
    void LogFrame(canbus* bus, CAN_log_type_t type, const CAN_frame_t* frame);
    void LogStatus(canbus* bus, CAN_log_type_t type, const CAN_status_t* status);
    void LogInfo(canbus* bus, CAN_log_type_t type, const char* text);

  public:
    canbus* GetBus(int busnumber);

  public:
    typedef std::map<uint32_t, canlog*> canlog_map_t;
    canlog_map_t m_loggermap;
    OvmsRecMutex m_loggermap_mutex;
    uint32_t m_logger_id;

  public:
    typedef std::map<uint32_t, canplay*> canplay_map_t;
    canplay_map_t m_playermap;
    OvmsMutex m_playermap_mutex;
    uint32_t m_player_id;

  private:
    canbus* m_buslist[CAN_MAXBUSES];
    CanListenerMap_t m_listeners;
    CanFrameCallbackList_t m_rxcallbacks;
    CanFrameCallbackList_t m_txcallbacks;
    TaskHandle_t m_rxtask;            // Task to handle reception
  };

extern can MyCan;

#endif //#ifndef __CAN_H__
