#include "IRremote.h"

//+=============================================================================
// Gets one undecoded level at a time from the raw buffer.
// The RC5/6 decoding is easier if the data is broken into time intervals.
// E.g. if the buffer has MARK for 2 time intervals and SPACE for 1,
// successive calls to getRClevel will return MARK, MARK, SPACE.
// offset and used are updated to keep track of the current position.
// t1 is the time interval for a single bit in microseconds.
// Returns -1 for error (measured time interval is not a multiple of t1).
//
#if (DECODE_RC5 || DECODE_RC6)
int getRClevel(decode_results *results, unsigned int *offset, int *used, int t1) {
    int width;
    int val;
    int correction;
    int avail;

    if (*offset >= results->rawlen) {
        return SPACE;  // After end of recorded buffer, assume SPACE.
    }
    width = results->rawbuf[*offset];
    val = ((*offset) % 2) ? MARK : SPACE;
    correction = (val == MARK) ? MARK_EXCESS_MICROS : - MARK_EXCESS_MICROS;

    if (MATCH(width, (t1) + correction)) {
        avail = 1;
    } else if (MATCH(width, (2 * t1) + correction)) {
        avail = 2;
    } else if (MATCH(width, (3 * t1) + correction)) {
        avail = 3;
    } else {
        return -1;
    }

    (*used)++;
    if (*used >= avail) {
        *used = 0;
        (*offset)++;
    }

    DBG_PRINTLN((val == MARK) ? "MARK" : "SPACE");

    return val;
}
#endif

//==============================================================================
// RRRR    CCCC  55555
// R   R  C      5
// RRRR   C      5555
// R  R   C          5
// R   R   CCCC  5555
//
// NB: First bit must be a one (start bit)
//
#define MIN_RC5_SAMPLES     11
#define RC5_T1             889
#define RC5_RPT_LENGTH   46000

//+=============================================================================
#if SEND_RC5
void IRsend::sendRC5(unsigned long data, int nbits) {
    // Set IR carrier frequency
    enableIROut(36);

    // Start
    mark(RC5_T1);
    space(RC5_T1);
    mark(RC5_T1);

    // Data - Biphase code MSB first
    for (unsigned long mask = 1UL << (nbits - 1); mask; mask >>= 1) {
        if (data & mask) {
            space(RC5_T1); // 1 is space, then mark
            mark(RC5_T1);
        } else {
            mark(RC5_T1);
            space(RC5_T1);
        }
    }

    space(0);  // Always end with the LED off
}

void IRsend::sendRC5ext(uint8_t addr, uint8_t cmd, boolean toggle) {
    // Set IR carrier frequency
    enableIROut(36);

    uint8_t addressBits = 5;
    uint8_t commandBits = 7;
//    unsigned long nbits = addressBits + commandBits;

    // Start
    mark(RC5_T1);

    // Bit #6 of the command part, but inverted!
    uint8_t cmdBit6 = (1UL << (commandBits - 1)) & cmd;
    if (cmdBit6) {
        // Inverted (1 -> 0 = mark-to-space)
        mark(RC5_T1);
        space(RC5_T1);
    } else {
        space(RC5_T1);
        mark(RC5_T1);
    }
    commandBits--;

    // Toggle bit
    static int toggleBit = 1;
    if (toggle) {
        if (toggleBit == 0) {
            toggleBit = 1;
        } else {
            toggleBit = 0;
        }
    }
    if (toggleBit) {
        space(RC5_T1);
        mark(RC5_T1);
    } else {
        mark(RC5_T1);
        space(RC5_T1);
    }

    // Address
    for (uint8_t mask = 1UL << (addressBits - 1); mask; mask >>= 1) {
        if (addr & mask) {
            space(RC5_T1); // 1 is space, then mark
            mark(RC5_T1);
        } else {
            mark(RC5_T1);
            space(RC5_T1);
        }
    }

    // Command
    for (uint8_t mask = 1UL << (commandBits - 1); mask; mask >>= 1) {
        if (cmd & mask) {
            space(RC5_T1); // 1 is space, then mark
            mark(RC5_T1);
        } else {
            mark(RC5_T1);
            space(RC5_T1);
        }
    }

    space(0);  // Always end with the LED off
}

#endif

//+=============================================================================
#if DECODE_RC5
bool IRrecv::decodeRC5() {
    int nbits;
    long data = 0;
    int used = 0;
    unsigned int offset = 1;  // Skip gap space

    if (results.rawlen < MIN_RC5_SAMPLES + 2) {
        return false;
    }

    // Get start bits
    if (getRClevel(&results, &offset, &used, RC5_T1) != MARK) {
        return false;
    }
    if (getRClevel(&results, &offset, &used, RC5_T1) != SPACE) {
        return false;
    }
    if (getRClevel(&results, &offset, &used, RC5_T1) != MARK) {
        return false;
    }

    /*
     * Get data bits - MSB first
     */
    for (nbits = 0; offset < results.rawlen; nbits++) {
        int levelA = getRClevel(&results, &offset, &used, RC5_T1);
        int levelB = getRClevel(&results, &offset, &used, RC5_T1);

        if ((levelA == SPACE) && (levelB == MARK)) {
            data = (data << 1) | 1;
        } else if ((levelA == MARK) && (levelB == SPACE)) {
            data = (data << 1) | 0;
        } else {
            return false;
        }
    }

    // Success
    results.bits = nbits;
    results.value = data;
    results.decode_type = RC5;
    return true;
}
bool IRrecv::decodeRC5(decode_results *aResults) {
    bool aReturnValue = decodeRC5();
    *aResults = results;
    return aReturnValue;
}
#endif

//+=============================================================================
// RRRR    CCCC   6666
// R   R  C      6
// RRRR   C      6666
// R  R   C      6   6
// R   R   CCCC   666
//
// NB : Caller needs to take care of flipping the toggle bit
//
#define MIN_RC6_SAMPLES      1
#define RC6_HEADER_MARK      2666
#define RC6_HEADER_SPACE      889
#define RC6_T1             444
#define RC6_RPT_LENGTH   46000

#if SEND_RC6
void IRsend::sendRC6(unsigned long data, int nbits) {
    // Set IR carrier frequency
    enableIROut(36);

    // Header
    mark(RC6_HEADER_MARK);
    space(RC6_HEADER_SPACE);

    // Start bit
    mark(RC6_T1);
    space(RC6_T1);

    // Data
    for (unsigned long i = 1, mask = 1UL << (nbits - 1); mask; i++, mask >>= 1) {
        // The fourth bit we send is a "double width trailer bit"
        int t = (i == 4) ? (RC6_T1 * 2) : (RC6_T1);
        if (data & mask) {
            mark(t);
            space(t);
        } else {
            space(t);
            mark(t);
        }
    }

    space(0);  // Always end with the LED off
}
#endif

//+=============================================================================
#if DECODE_RC6
bool IRrecv::decodeRC6() {
    int nbits;
    long data = 0;
    int used = 0;
    unsigned int offset = 1;  // Skip first space

    if (results.rawlen < MIN_RC6_SAMPLES) {
        return false;
    }

// Initial mark
    if (!MATCH_MARK(results.rawbuf[offset], RC6_HEADER_MARK)) {
        return false;
    }
    offset++;

    if (!MATCH_SPACE(results.rawbuf[offset], RC6_HEADER_SPACE)) {
        return false;
    }
    offset++;

// Get start bit (1)
    if (getRClevel(&results, &offset, &used, RC6_T1) != MARK) {
        return false;
    }
    if (getRClevel(&results, &offset, &used, RC6_T1) != SPACE) {
        return false;
    }

    for (nbits = 0; offset < results.rawlen; nbits++) {
        int levelA, levelB;  // Next two levels

        levelA = getRClevel(&results, &offset, &used, RC6_T1);
        if (nbits == 3) {
            // T bit is double wide; make sure second half matches
            if (levelA != getRClevel(&results, &offset, &used, RC6_T1)) {
                return false;
            }
        }

        levelB = getRClevel(&results, &offset, &used, RC6_T1);
        if (nbits == 3) {
            // T bit is double wide; make sure second half matches
            if (levelB != getRClevel(&results, &offset, &used, RC6_T1)) {
                return false;
            }
        }

        if ((levelA == MARK) && (levelB == SPACE)) {
            data = (data << 1) | 1;  // inverted compared to RC5
        } else if ((levelA == SPACE) && (levelB == MARK)) {
            data = (data << 1) | 0;
        } else {
            return false;            // Error
        }
    }

    // Success
    results.bits = nbits;
    results.value = data;
    results.decode_type = RC6;
    return true;
}
bool IRrecv::decodeRC6(decode_results *aResults) {
    bool aReturnValue = decodeRC6();
    *aResults = results;
    return aReturnValue;
}
#endif
