/*
 * LegoPowerFunctionsTest: LEGO Power Functions Tests
 * Copyright (c) 2016, 2017 Philipp Henkel
 */

#include <IRremote.h>
#include "ir_Lego_PF_BitStreamEncoder.h"

void runBitStreamEncoderTests();

void setup() {
    pinMode(LED_BUILTIN, OUTPUT);

    Serial.begin(115200);
#if defined(__AVR_ATmega32U4__)
    while (!Serial)
        ; //delay for Leonardo, but this loops forever for Maple Serial
#endif
#if defined(SERIAL_USB) || defined(SERIAL_PORT_USBVIRTUAL)
    delay(2000); // To be able to connect Serial monitor after reset and before first printout
#endif
    // Just to know which program is running on my Arduino
    Serial.println(F("START " __FILE__ " from " __DATE__));
    delay(1000); // wait for reset triggered by serial connection
    runBitStreamEncoderTests();
}

void loop() {
}

void runBitStreamEncoderTests() {
    Serial.println();
    Serial.println("BitStreamEncoder Tests");
    static LegoPfBitStreamEncoder bitStreamEncoder;
    testStartBit(bitStreamEncoder);
    testLowBit(bitStreamEncoder);
    testHighBit(bitStreamEncoder);
    testMessageBitCount(bitStreamEncoder);
    testMessageBitCountRepeat(bitStreamEncoder);
    testMessage407(bitStreamEncoder);
    testMessage407Repeated(bitStreamEncoder);
    testGetChannelId1(bitStreamEncoder);
    testGetChannelId2(bitStreamEncoder);
    testGetChannelId3(bitStreamEncoder);
    testGetChannelId4(bitStreamEncoder);
    testGetMessageLengthAllHigh(bitStreamEncoder);
    testGetMessageLengthAllLow(bitStreamEncoder);
}

void logTestResult(bool testPassed) {
    if (testPassed) {
        Serial.println("OK");
    } else {
        Serial.println("FAIL  ############");
    }
}

void testStartBit(LegoPfBitStreamEncoder& bitStreamEncoder) {
    Serial.print("  testStartBit                    ");
    bitStreamEncoder.reset(0, false);
    int startMark = bitStreamEncoder.getMarkDuration();
    int startPause = bitStreamEncoder.getPauseDuration();
    logTestResult(startMark == 158 && startPause == 1184 - 158);
}

void testLowBit(LegoPfBitStreamEncoder& bitStreamEncoder) {
    Serial.print("  testLowBit                      ");
    bitStreamEncoder.reset(0, false);
    bitStreamEncoder.next();
    int lowMark = bitStreamEncoder.getMarkDuration();
    int lowPause = bitStreamEncoder.getPauseDuration();
    logTestResult(lowMark == 158 && lowPause == 421 - 158);
}

void testHighBit(LegoPfBitStreamEncoder& bitStreamEncoder) {
    Serial.print("  testHighBit                     ");
    bitStreamEncoder.reset(0xFFFF, false);
    bitStreamEncoder.next();
    int highMark = bitStreamEncoder.getMarkDuration();
    int highPause = bitStreamEncoder.getPauseDuration();
    logTestResult(highMark == 158 && highPause == 711 - 158);
}

void testMessageBitCount(LegoPfBitStreamEncoder& bitStreamEncoder) {
    Serial.print("  testMessageBitCount             ");
    bitStreamEncoder.reset(0xFFFF, false);
    int bitCount = 1;
    while (bitStreamEncoder.next()) {
        bitCount++;
    }
    logTestResult(bitCount == 18);
}

boolean check(LegoPfBitStreamEncoder& bitStreamEncoder, unsigned long markDuration, unsigned long pauseDuration) {
    bool result = true;
    result = result && bitStreamEncoder.getMarkDuration() == markDuration;
    result = result && bitStreamEncoder.getPauseDuration() == pauseDuration;
    return result;
}

boolean checkNext(LegoPfBitStreamEncoder& bitStreamEncoder, unsigned long markDuration, unsigned long pauseDuration) {
    bool result = bitStreamEncoder.next();
    result = result && check(bitStreamEncoder, markDuration, pauseDuration);
    return result;
}

boolean checkDataBitsOfMessage407(LegoPfBitStreamEncoder& bitStreamEncoder) {
    bool result = true;
    result = result && checkNext(bitStreamEncoder, 158, 263);
    result = result && checkNext(bitStreamEncoder, 158, 263);
    result = result && checkNext(bitStreamEncoder, 158, 263);
    result = result && checkNext(bitStreamEncoder, 158, 263);
    result = result && checkNext(bitStreamEncoder, 158, 263);
    result = result && checkNext(bitStreamEncoder, 158, 263);
    result = result && checkNext(bitStreamEncoder, 158, 263);
    result = result && checkNext(bitStreamEncoder, 158, 553);
    result = result && checkNext(bitStreamEncoder, 158, 553);
    result = result && checkNext(bitStreamEncoder, 158, 263);
    result = result && checkNext(bitStreamEncoder, 158, 263);
    result = result && checkNext(bitStreamEncoder, 158, 553);
    result = result && checkNext(bitStreamEncoder, 158, 263);
    result = result && checkNext(bitStreamEncoder, 158, 553);
    result = result && checkNext(bitStreamEncoder, 158, 553);
    result = result && checkNext(bitStreamEncoder, 158, 553);
    return result;
}

void testMessage407(LegoPfBitStreamEncoder& bitStreamEncoder) {
    Serial.print("  testMessage407                  ");
    bitStreamEncoder.reset(407, false);
    bool result = true;
    result = result && check(bitStreamEncoder, 158, 1026);
    result = result && checkDataBitsOfMessage407(bitStreamEncoder);
    result = result && checkNext(bitStreamEncoder, 158, 1026);
    result = result && !bitStreamEncoder.next();
    logTestResult(result);
}

void testMessage407Repeated(LegoPfBitStreamEncoder& bitStreamEncoder) {
    Serial.print("  testMessage407Repeated          ");
    bitStreamEncoder.reset(407, true);
    bool result = true;
    result = result && check(bitStreamEncoder, 158, 1026);
    result = result && checkDataBitsOfMessage407(bitStreamEncoder);
    result = result && checkNext(bitStreamEncoder, 158, 1026L + 5L * 16000L - 10844L);
    result = result && checkNext(bitStreamEncoder, 158, 1026);
    result = result && checkDataBitsOfMessage407(bitStreamEncoder);
    result = result && checkNext(bitStreamEncoder, 158, 1026L + 5L * 16000L - 10844L);
    result = result && checkNext(bitStreamEncoder, 158, 1026);
    result = result && checkDataBitsOfMessage407(bitStreamEncoder);
    result = result && checkNext(bitStreamEncoder, 158, 1026L + 8L * 16000L - 10844L);
    result = result && checkNext(bitStreamEncoder, 158, 1026);
    result = result && checkDataBitsOfMessage407(bitStreamEncoder);
    result = result && checkNext(bitStreamEncoder, 158, 1026L + 8L * 16000L - 10844L);
    result = result && checkNext(bitStreamEncoder, 158, 1026);
    result = result && checkDataBitsOfMessage407(bitStreamEncoder);
    result = result && checkNext(bitStreamEncoder, 158, 1026);
    result = result && !bitStreamEncoder.next();
    logTestResult(result);
}

void testMessageBitCountRepeat(LegoPfBitStreamEncoder& bitStreamEncoder) {
    Serial.print("  testMessageBitCountRepeat       ");
    bitStreamEncoder.reset(0xFFFF, true);
    int bitCount = 1;
    while (bitStreamEncoder.next()) {
        bitCount++;
    }
    logTestResult(bitCount == 5 * 18);
}

void testGetChannelId1(LegoPfBitStreamEncoder& bitStreamEncoder) {
    Serial.print("  testGetChannelId1               ");
    bitStreamEncoder.reset(407, false);
    logTestResult(bitStreamEncoder.getChannelId() == 1);
}

void testGetChannelId2(LegoPfBitStreamEncoder& bitStreamEncoder) {
    Serial.print("  testGetChannelId2               ");
    bitStreamEncoder.reset(4502, false);
    logTestResult(bitStreamEncoder.getChannelId() == 2);
}

void testGetChannelId3(LegoPfBitStreamEncoder& bitStreamEncoder) {
    Serial.print("  testGetChannelId3               ");
    bitStreamEncoder.reset(8597, false);
    logTestResult(bitStreamEncoder.getChannelId() == 3);
}

void testGetChannelId4(LegoPfBitStreamEncoder& bitStreamEncoder) {
    Serial.print("  testGetChannelId4               ");
    bitStreamEncoder.reset(12692, false);
    logTestResult(bitStreamEncoder.getChannelId() == 4);
}

void testGetMessageLengthAllHigh(LegoPfBitStreamEncoder& bitStreamEncoder) {
    Serial.print("  testGetMessageLengthAllHigh     ");
    bitStreamEncoder.reset(0xFFFF, false);
    logTestResult(bitStreamEncoder.getMessageLength() == 13744);
}

void testGetMessageLengthAllLow(LegoPfBitStreamEncoder& bitStreamEncoder) {
    Serial.print("  testGetMessageLengthAllLow      ");
    bitStreamEncoder.reset(0x0, false);
    logTestResult(bitStreamEncoder.getMessageLength() == 9104);
}
