/*
 * Copyright (C) 2020-2021 Aloysius Indrayanto
 *
 * This file is part of the eCxx library, see LICENSE file for the license details.
 */


#ifndef ECXX_PLATFORM_NODEMCU_CORE_LIB_H
#define ECXX_PLATFORM_NODEMCU_CORE_LIB_H


#include <ets_sys.h>

#include <eCxx/eCxx_Internal.h>
#include <eCxx/Platform/Common/Application.h>
#include <eCxx/Platform/NodeMCU/GPIOUtil.h>

#include "ESP8266Undocumented.h"


//
// Macros
//

// GPIO mode
#define INPUT             0x00
#define INPUT_PULLUP      0x02
#define INPUT_PULLDOWN_16 0x04
#define OUTPUT            0x01
#define OUTPUT_OPEN_DRAIN 0x03
#define WAKEUP_PULLUP     0x05
#define WAKEUP_PULLDOWN   0x07
#define SPECIAL           0xF8
#define FUNCTION_0        0x08
#define FUNCTION_1        0x18
#define FUNCTION_2        0x28
#define FUNCTION_3        0x38
#define FUNCTION_4        0x48

// Interrupt mode
#define RISING  0x01
#define FALLING 0x02
#define CHANGE  0x03


//
// Timer-related constants
//

// Timer dividers
enum TIM_DIV_ENUM {
    TIM_DIV1   = 0, //  80   MHz (80   ticks/uS -   104857.588 uS maximum)
    TIM_DIV16  = 1, //   5   MHz ( 5   ticks/uS -  1677721.400 uS maximum)
    TIM_DIV256 = 3  // 312.5 khz ( 3.2 uS/tick  - 26843542.400 uS maximum)
};

// Timer interrupt types
#define TIM_EDGE  0
#define TIM_LEVEL 1

// Timer reload values
#define TIM_SINGLE 0 // On interrupt routine a new value msut be written to start the timer again
#define TIM_LOOP   1 // On interrupt the counter will start with the same value again


namespace eCxx {

namespace NodeMCU {


//
// Timer-related data types
//
typedef Application::VoidFuncPtr timercallback;


//
// Interrupt-related data types
//
using InterruptMode  = Application::InterruptMode;

using VoidFuncPtr    = Application::VoidFuncPtr;
using VoidFuncPtrArg = Application::VoidFuncPtrArg;


struct InterruptInfo {
    uint8_t  pin   = 0;
    uint8_t  value = 0;
    uint32_t micro = 0;
};


using FunctionalInterruptCallback          = eCxx::FunctionalVoidCallback;
using FunctionalScheduledInterruptCallback = eCxx::function<void(InterruptInfo)>;


struct FunctionInfo {
    FunctionalInterruptCallback          reqFunction          = nullptr;
    FunctionalScheduledInterruptCallback reqScheduledFunction = nullptr;
};


struct ArgStructure {
    InterruptInfo* interruptInfo = nullptr;
    FunctionInfo*  functionInfo  = nullptr;
};


//
// Function prototypes
//

// "CRC32.cpp"
extern uint32_t crc32(const void* data, size_t length, uint32_t crc = 0xFFFFFFFF);

// "Sqrt32.cpp"
extern uint32_t isqrt32(uint32_t n);

// "Random.cpp"
extern uint8_t* random(uint8_t* resultArray, const size_t outputSizeBytes);
extern uint32_t random();

// "Timer01.cpp"
extern void ICACHE_RAM_ATTR timer0InitISR();
extern void ICACHE_RAM_ATTR timer0AttachInterrupt(timercallback userFunc);
extern void ICACHE_RAM_ATTR timer0DetachInterrupt();

extern void ICACHE_RAM_ATTR timer1InitISR();
extern void ICACHE_RAM_ATTR timer1AttachInterrupt(timercallback userFunc);
extern void ICACHE_RAM_ATTR timer1DetachInterrupt();
extern void ICACHE_RAM_ATTR timer1Enable(uint8_t divider, uint8_t intType, uint8_t reload);
extern void ICACHE_RAM_ATTR timer1Disable();
extern void ICACHE_RAM_ATTR timer1Write(uint32_t ticks);

// "Timing.cpp"
extern void updateCPUFrequency(uint8_t freq);

extern void delay(unsigned long ms);
extern void ICACHE_RAM_ATTR delayMicroseconds(uint32_t us);

extern unsigned long ICACHE_RAM_ATTR millis();
extern unsigned long ICACHE_RAM_ATTR micros();
extern uint64_t ICACHE_RAM_ATTR micros64();

extern void initSystemAndTimer();

// "DigitalIO.cpp"
extern void ICACHE_RAM_ATTR pinMode(uint8_t pin, uint8_t mode);

extern void ICACHE_RAM_ATTR digitalWrite(uint8_t pin, bool bit);
extern bool ICACHE_RAM_ATTR digitalRead(uint8_t pin);

// "GPIOInterrupt.cpp"
extern bool attachInterrupt(uint8_t pin, FunctionalInterruptCallback intRoutine, InterruptMode mode);
extern bool attachScheduledInterrupt(uint8_t pin, FunctionalScheduledInterruptCallback scheduledIntRoutine, InterruptMode mode);

extern bool attachInterrupt(uint8_t pin, VoidFuncPtr userFunc, InterruptMode mode);
extern bool attachInterruptArg(uint8_t pin, VoidFuncPtrArg userFunc, void* arg, InterruptMode mode);
extern bool ICACHE_RAM_ATTR detachInterrupt(uint8_t pin);

// "Waveform.cpp"
extern void setWaveformTimer1Callback(uint32_t (*cb)());

extern bool startWaveformClockCycles(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles);
extern bool startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS);
extern bool ICACHE_RAM_ATTR stopWaveform(uint8_t pin);

extern void setPWMFrequency(uint32_t freq);
extern bool startPWM(int8_t pin, uint32_t val, uint32_t range);
extern bool ICACHE_RAM_ATTR stopPWM(int8_t pin);


} // namespace NodeMCU

} // namespace eCxx


//
// For compatibility with the Arduino library part
//
extern "C" void delay(unsigned long ms);


#endif
