/*
 *  TinyIRReceiver.h
 *
 *
 *  Copyright (C) 2021-2022  Armin Joachimsmeyer
 *  armin.joachimsmeyer@gmail.com
 *
 *  This file is part of IRMP https://github.com/IRMP-org/IRMP.
 *  This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
 *
 *  TinyIRReceiver is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program. If not, see <http://www.gnu.org/licenses/gpl.html>.
 *
 */

#ifndef _TINY_IR_RECEIVER_H
#define _TINY_IR_RECEIVER_H

#include <Arduino.h>

//#define DISABLE_NEC_SPECIAL_REPEAT_SUPPORT    // Activating this disables detection of full NEC frame repeats. Saves 40 bytes program memory.

#include "LongUnion.h"

/** \addtogroup TinyReceiver Minimal receiver for NEC protocol
 * @{
 */

/*
 * This function is called if a complete command was received and must be implemented by the including file (user code)
 */
void handleReceivedTinyIRData(uint16_t aAddress, uint8_t aCommand, bool isRepeat);

// LSB first, 1 start bit + 16 bit address + 8 bit data + 8 bit inverted data + 1 stop bit.
// see: https://www.sbprojects.net/knowledge/ir/nec.php

#define NEC_ADDRESS_BITS        16 // 16 bit address or 8 bit address and 8 bit inverted address
#define NEC_COMMAND_BITS        16 // Command and inverted command

#define NEC_BITS                (NEC_ADDRESS_BITS + NEC_COMMAND_BITS)
#define NEC_UNIT                560

#define NEC_HEADER_MARK         (16 * NEC_UNIT) // 9000
#define NEC_HEADER_SPACE        (8 * NEC_UNIT)  // 4500

#define NEC_BIT_MARK            NEC_UNIT
#define NEC_ONE_SPACE           (3 * NEC_UNIT)  // 1690
#define NEC_ZERO_SPACE          NEC_UNIT

#define NEC_REPEAT_HEADER_SPACE (4 * NEC_UNIT)  // 2250
#define NEC_REPEAT_PERIOD       110000 // Commands are repeated every 110 ms (measured from start to start) for as long as the key on the remote control is held down.
#define NEC_MINIMAL_DURATION    49900 // NEC_HEADER_MARK + NEC_HEADER_SPACE + 32 * 2 * NEC_UNIT + NEC_UNIT // 2.5 because we assume more zeros than ones
#define NEC_MAXIMUM_REPEAT_SPACE (NEC_REPEAT_PERIOD - NEC_MINIMAL_DURATION + 5) // 65 ms

/*
 * Macros for comparing timing values
 */
#define lowerValue25Percent(aDuration)   (aDuration - (aDuration / 4))
#define upperValue25Percent(aDuration)   (aDuration + (aDuration / 4))
#define lowerValue50Percent(aDuration)   (aDuration / 2) // (aDuration - (aDuration / 2))
#define upperValue50Percent(aDuration)   (aDuration + (aDuration / 2))

/*
 * The states for the state machine
 */
#define IR_RECEIVER_STATE_WAITING_FOR_START_MARK        0
#define IR_RECEIVER_STATE_WAITING_FOR_START_SPACE       1
#define IR_RECEIVER_STATE_WAITING_FOR_FIRST_DATA_MARK   2
#define IR_RECEIVER_STATE_WAITING_FOR_DATA_SPACE        3
#define IR_RECEIVER_STATE_WAITING_FOR_DATA_MARK         4
#define IR_RECEIVER_STATE_WAITING_FOR_STOP_MARK         5
/**
 * Control and data variables of the state machine for TinyReceiver
 */
struct TinyIRReceiverStruct {
    /*
     * State machine
     */
    uint32_t LastChangeMicros;      ///< Microseconds of last Pin Change Interrupt.
    uint8_t IRReceiverState;        ///< The state of the state machine.
    uint8_t IRRawDataBitCounter;    ///< How many bits are currently contained in raw data.
    /*
     * Data
     */
    uint32_t IRRawDataMask;         ///< The corresponding bit mask for IRRawDataBitCounter.
    LongUnion IRRawData;            ///< The current raw data. LongUnion helps with decoding of address and command.
    bool IRRepeatFrameDetected;     ///< A "standard" NEC repeat frame was detected.
#if !defined(DISABLE_NEC_SPECIAL_REPEAT_SUPPORT)
    bool IRRepeatDistanceDetected;  ///< A small gap between two frames is detected -> assume a "non standard" repeat.
#endif
};

/**
 * Can be used by the callback to transfer received data to main loop for further processing.
 * E.g. with volatile struct TinyIRReceiverCallbackDataStruct sCallbackData;
 */
struct TinyIRReceiverCallbackDataStruct {
    uint16_t Address;
    uint8_t Command;
    bool isRepeat;
    bool justWritten;   ///< Is set true if new data is available. Used by the main loop, to avoid multiple evaluations of the same IR frame.
};

bool initPCIInterruptForTinyReceiver();
bool enablePCIInterruptForTinyReceiver();
void disablePCIInterruptForTinyReceiver();
bool isTinyReceiverIdle();

/** @}*/

#endif // _TINY_IR_RECEIVER_H
