//
// DCC_Decoder.h - Arduino library for NMRA DCC Decoding.
// Written by Kevin Snow, MynaBay.com, November, 2011. 
// Questions: dcc@mynabay.com
// Released into the public domain.
//

#ifndef __DCC_DECODER_H__
#define __DCC_DECODER_H__

#include "Arduino.h"

///////////////////////////////////////////////////////////////////////////////////////

#define kDCC_STOP_SPEED     0xFE
#define kDCC_ESTOP_SPEED    0xFF

    // Multifunction Decoders
#define kCV_PrimaryAddress            1
#define kCV_Vstart                    2
#define kCV_AccelerationRate          3
#define kCV_Deceleration Rate         4
#define kCV_ManufacturerVersionNo     7 
#define kCV_ManufacturedID            8
#define kCV_ExtendedAddress1          17
#define kCV_ExtendedAddress2          18
#define kCV_ConfigurationData1        29

    // Accessory Decoders
#define kCV_AddressLSB                1
#define kCV_AddressMSB                9


    // DCC_Decoder results/errors
#define kDCC_OK                       0
#define kDCC_OK_UNHANDLED             1
#define kDCC_OK_BOOT                  2
#define kDCC_OK_IDLE                  3
#define kDCC_OK_RESET                 4
#define kDCC_OK_RAW                   5
#define kDCC_OK_BASELINE              6 
#define kDCC_OK_BASIC_ACCESSORY       7 
#define kDCC_OK_EXTENDED_ACCESSORY    8
#define kDCC_OK_MAX                   99

#define kDCC_ERR_DETECTION_FAILED     100
#define kDCC_ERR_BASELINE_ADDR        101             
#define kDCC_ERR_BASELINE_INSTR       102         // Baseline packet instruction isn't 0x01DCSSSS
#define kDCC_ERR_MISSED_BITS          103
#define kDCC_ERR_NOT_0_OR_1           104
#define kDCC_ERR_INVALID_LENGTH       105
#define kDCC_ERR_MISSING_END_BIT      106

    // Min and max valid packet lengths
#define kPACKET_LEN_MIN               3
#define kPACKET_LEN_MAX               6

    // CV 1..256 are supported
#define kCV_MAX                       257

///////////////////////////////////////////////////////////////////////////////////////

typedef boolean (*RawPacket)(byte byteCount, byte* packetBytes);

typedef void (*IdleResetPacket)(byte byteCount, byte* packetBytes);

typedef void (*BaselineControlPacket)(int address, int speed, int direction);

typedef void (*BasicAccDecoderPacket)(int address, boolean activate, byte data);
typedef void (*ExtendedAccDecoderPacket)(int address, byte data);

typedef void (*DecodingEngineCompletion)(byte resultOfLastPacket);

///////////////////////////////////////////////////////////////////////////////////////

typedef void(*StateFunc)();

///////////////////////////////////////////////////////////////////////////////////////

class DCC_Decoder
{
public:
    DCC_Decoder();
    
        // Called from setup in Arduino Sketch. Set mfgID, mfgVers and interrupt. Call one SetupXXX
    void SetupDecoder(byte mfgID, byte mfgVers, byte interrupt);    // Used for Decoder
    void SetupMonitor(byte interrupt);                              // Used when building a monitor
    
        // All packets are sent to RawPacketHandler. Return true to stop dispatching to other handlers.
    void SetRawPacketHandler(RawPacket func);
    
        // S 9.2 defines two special packets. Idle and reset.
    void SetIdlePacketHandler(IdleResetPacket func);
    void SetResetPacketHandler(IdleResetPacket func);
    
        // Handler for S 9.2 baseline packets. Speed value will be 1-14, 1-28, kDCC_STOP_SPEED or kDCC_ESTOP_SPEED
    void SetBaselineControlPacketHandler(BaselineControlPacket func, boolean allPackets);
    
        // Handler for RP 9.2.1 Accessory Decoders.
    void SetBasicAccessoryDecoderPacketHandler(BasicAccDecoderPacket func, boolean allPackets);
    void SetExtendedAccessoryDecoderPacketHandler(ExtendedAccDecoderPacket func, boolean allPackets);
                
        // Read/Write CVs
    byte ReadCV(int cv);
    void WriteCV(int cv, byte data);
    
        // Helper function to read decoder address
    int Address();
    
        // Call at least once from mainloop. Not calling frequently enough and library will miss data bits!
    void loop();
    
        // Returns the packet data in string form.
    char* MakePacketString(char* buffer60Bytes, byte packetByteCount, byte* packet);
        
        // Returns the number of bits in last preamble 
    int LastPreambleBitCount();
    
        // Timing functions. These return MS since various packets
    unsigned long MillisecondsSinceLastValidPacket();
    unsigned long MillisecondsSinceLastPacketToThisDecoder();
    unsigned long MillisecondsSinceLastIdlePacket();
    unsigned long MillisecondsSinceLastResetPacket();
    
    
    //=======================   Debugging   =======================//    
        // Everytime the DCC Decoder engine starts looking for preamble bits this will be 
        // called with result of last packet. (Debugging)
    void SetDecodingEngineCompletionStatusHandler(DecodingEngineCompletion func);
        // Converts code passed into completionStatusHandler to human readable string.
    const prog_char* ResultString(byte resultCode);
    
    //======================= Library Internals =======================//
private:
        // State machine functions
    static void State_Boot();
    static void State_ReadPreamble();
    static void State_ReadPacket();
    static void State_Execute();
    static void State_Reset();
    
        // Function pointers for the library callbacks
    static RawPacket                func_RawPacket;
    static IdleResetPacket          func_IdlePacket;
    static IdleResetPacket          func_ResetPacket;
        
    static BasicAccDecoderPacket    func_BasicAccPacket;
    static boolean                  func_BasicAccPacket_All_Packets;
    static ExtendedAccDecoderPacket func_ExtdAccPacket;
    static boolean                  func_ExtdAccPacket_All_Packets;
    
    static BaselineControlPacket    func_BaselineControlPacket;
    static boolean                  func_BaselineControlPacket_All_Packets;
    
    static DecodingEngineCompletion func_DecodingEngineCompletion;
    
        // Current state function pointer
    static StateFunc                gState;                      // Current state function pointer
    
        // Timing data from last interrupt
    static unsigned int             gLastChaos;                  // Interrupt chaos count we processed
    
        // Preamble bit count
    static int                      gPreambleCount;              // Bit count for reading preamble
    
        // Reset reason 
    static byte                     gResetReason;                // Result code of last reason decoder was reset
    static boolean                  gHandledAsRawPacket;
    
        // Packet data
    static byte                     gPacket[kPACKET_LEN_MAX];    // The packet data.
    static byte                     gPacketIndex;                // Byte index to write to.
    static byte                     gPacketMask;                 // Bit index to write to. 0x80,0x40,0x20,...0x01
    static boolean                  gPacketEndedWith1;           // Set true if packet ended on 1. Spec requires that the 
                                                                 // packet end bit can count as a bit in next preamble. 
                                                                 // CV Storage
    static byte                     gCV[kCV_MAX];                // CV Storage (TODO - Storage in PROGMEM)
    
        // Packet arrival timing
    static unsigned long            gThisPacketMS;               // Milliseconds of this packet being parsed
    static boolean                  gLastPacketToThisAddress;    // Was last pack processed to this decoder's address?
    
    static unsigned long            gLastValidPacketMS;          // Milliseconds of last valid packet
    static unsigned long            gLastValidPacketToAddressMS; // Milliseconds of last valid packet to this decoder
    static unsigned long            gLastValidIdlePacketMS;      // Milliseconds of last valid idle packet
    static unsigned long            gLastValidResetPacketMS;     // Milliseconds of last valid reset packet

        //////////////////////////////////////////////////////
        // Interrupt Support
    static void StartInterrupt(byte interrupt);    
    static void DCC_Interrupt();
    static void ShiftInterruptAlignment();
    
    static unsigned long          gInterruptMicros;
    static byte                   gInterruptTimeIndex;
    static volatile unsigned int  gInterruptTime[2];
    static volatile unsigned int  gInterruptChaos;
};

///////////////////////////////////////////////////////////////////////////////////////

extern DCC_Decoder DCC;

///////////////////////////////////////////////////////////////////////////////////////

#endif
