/*****************************************************************************

    source::worx libPiGPio
    Copyright © 2022 c.holzheuer
    chris@sourceworx.org

    This program 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 2 of the License, or
    (at your option) any later version.

    Uses:

    pigpiod_if2.h
    by joan2937, pigpio @ abyz.me.uk
    https://abyz.me.uk/rpi/pigpio

    sigslot.h
    by 2017 Pierre-Antoine Lacaze
    https://github.com/palacaze/sigslot

***************************************************************************/


#ifndef LIBPIGPIO_H
#define LIBPIGPIO_H

#if defined(_MSC_VER) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
#  define Q_DECL_EXPORT __declspec(dllexport)
#  define Q_DECL_IMPORT __declspec(dllimport)
#else
#  define Q_DECL_EXPORT     __attribute__((visibility("default")))
#  define Q_DECL_IMPORT     __attribute__((visibility("default")))
#endif

#if defined(LIBPIGPIO_LIBRARY)
#  define LIBPIGPIO_EXPORT Q_DECL_EXPORT
#else
#  define LIBPIGPIO_EXPORT Q_DECL_IMPORT
#endif

#include <iostream>
#include <sstream>
#include <iomanip>

#include <cstdint>
#include <string>
#include <signal.hpp>

struct pigI2C;
class pigCall;


namespace pigpio
{

    class pigCore;

    [[maybe_unused]] const int    GlitchDef  = 500;
    [[maybe_unused]] const double TimeoutDef = 0.2;

    enum class edge_t : uint32_t
    {
        rising   = 0,
        falling  = 1,
        either   = 2,
        edge_off = 99
    };

    enum class io_t : uint32_t
    {
        input   = 0,
        output  = 1,
        io_off  = 99
    };

    enum class bcm_t : uint32_t
    {
        /// left side, pin 1,3 ... 39

        bcm02   =  2,
        bcm03   =  3,
        bcm04   =  4,

        bcm17   = 17,
        bcm27   = 27,
        bcm22   = 22,

        bcm10   = 10,
        bcm09   =  9,
        bcm11   = 11,

        bcm05   =  5,
        bcm06   =  6,
        bcm13   = 13,

        bcm19   = 19,
        bcm26   = 26,

        /// right side, pin 2,4 .. 40

        bcm14   = 14,
        bcm15   = 15,
        bcm18   = 18,

        bcm23   = 23,
        bcm24   = 24,

        bcm25   = 25,
        bcm08   =  8,
        bcm07   =  7,

        bcm12   = 12,

        bcm16   = 16,
        bcm20   = 20,
        bcm21   = 21,

        bcm_off = 99

    };

    enum class pud_t : uint32_t
    {
        pull_off  = 0,
        pull_down = 1,
        pull_up   = 2,        
    };


    using level_t = uint32_t;

    ///
    /// i2c
    ///

    using i2c_bus_t  = uint32_t; // uint8_t ??
    using i2c_addr_t = uint32_t;
    using i2c_flag_t = uint32_t;

    ///  sugar
    [[maybe_unused]] constexpr static edge_t rising  = edge_t::rising;
    [[maybe_unused]] constexpr static edge_t falling = edge_t::falling;
    [[maybe_unused]] constexpr static edge_t either  = edge_t::either;

    ///---

    [[maybe_unused]] constexpr static io_t input  = io_t::input;
    [[maybe_unused]] constexpr static io_t output = io_t::output;

    ///---

    [[maybe_unused]] constexpr static pud_t pull_off  = pud_t::pull_off;
    [[maybe_unused]] constexpr static pud_t pull_down = pud_t::pull_down;
    [[maybe_unused]] constexpr static pud_t pull_up   = pud_t::pull_up;

    ///---

    /// left side, pin 1,3 ... 39
    [[maybe_unused]] constexpr static bcm_t bcm02   = bcm_t::bcm02;
    [[maybe_unused]] constexpr static bcm_t bcm03   = bcm_t::bcm03;
    [[maybe_unused]] constexpr static bcm_t bcm04   = bcm_t::bcm04;

    [[maybe_unused]] constexpr static bcm_t bcm17   = bcm_t::bcm17;
    [[maybe_unused]] constexpr static bcm_t bcm27   = bcm_t::bcm27;
    [[maybe_unused]] constexpr static bcm_t bcm22   = bcm_t::bcm22;

    [[maybe_unused]] constexpr static bcm_t bcm10   = bcm_t::bcm10;
    [[maybe_unused]] constexpr static bcm_t bcm09   = bcm_t::bcm09;
    [[maybe_unused]] constexpr static bcm_t bcm11   = bcm_t::bcm11;

    [[maybe_unused]] constexpr static bcm_t bcm05   = bcm_t::bcm05;
    [[maybe_unused]] constexpr static bcm_t bcm06   = bcm_t::bcm06;
    [[maybe_unused]] constexpr static bcm_t bcm13   = bcm_t::bcm13;

    [[maybe_unused]] constexpr static bcm_t bcm19   = bcm_t::bcm19;
    [[maybe_unused]] constexpr static bcm_t bcm26   = bcm_t::bcm26;

    /// right side, pin 2,4 .. 40
    [[maybe_unused]] constexpr static bcm_t bcm14   = bcm_t::bcm14;
    [[maybe_unused]] constexpr static bcm_t bcm15   = bcm_t::bcm15;
    [[maybe_unused]] constexpr static bcm_t bcm18   = bcm_t::bcm18;

    [[maybe_unused]] constexpr static bcm_t bcm23   = bcm_t::bcm23;
    [[maybe_unused]] constexpr static bcm_t bcm24   = bcm_t::bcm24;

    [[maybe_unused]] constexpr static bcm_t bcm25   = bcm_t::bcm25;
    [[maybe_unused]] constexpr static bcm_t bcm08   = bcm_t::bcm08;
    [[maybe_unused]] constexpr static bcm_t bcm07   = bcm_t::bcm07;

    [[maybe_unused]] constexpr static bcm_t bcm12   = bcm_t::bcm12;

    [[maybe_unused]] constexpr static bcm_t bcm16   = bcm_t::bcm16;
    [[maybe_unused]] constexpr static bcm_t bcm20   = bcm_t::bcm20;
    [[maybe_unused]] constexpr static bcm_t bcm21   = bcm_t::bcm21;

    /// more sugar

    /// left side, pin 1,3 ... 39
    [[maybe_unused]] constexpr static bcm_t pin03   = bcm_t::bcm02;
    [[maybe_unused]] constexpr static bcm_t pin05   = bcm_t::bcm03;
    [[maybe_unused]] constexpr static bcm_t pin07   = bcm_t::bcm04;

    [[maybe_unused]] constexpr static bcm_t pin11   = bcm_t::bcm17;
    [[maybe_unused]] constexpr static bcm_t pin13   = bcm_t::bcm27;
    [[maybe_unused]] constexpr static bcm_t pin15   = bcm_t::bcm22;

    [[maybe_unused]] constexpr static bcm_t pin19   = bcm_t::bcm10;
    [[maybe_unused]] constexpr static bcm_t pin21   = bcm_t::bcm09;
    [[maybe_unused]] constexpr static bcm_t pin23   = bcm_t::bcm11;

    [[maybe_unused]] constexpr static bcm_t pin29   = bcm_t::bcm05;
    [[maybe_unused]] constexpr static bcm_t pin31   = bcm_t::bcm06;
    [[maybe_unused]] constexpr static bcm_t pin33   = bcm_t::bcm13;

    [[maybe_unused]] constexpr static bcm_t pin35   = bcm_t::bcm19;
    [[maybe_unused]] constexpr static bcm_t pin37   = bcm_t::bcm26;

    /// right side, pin 2,4 .. 40
    [[maybe_unused]] constexpr static bcm_t pin08   = bcm_t::bcm14;
    [[maybe_unused]] constexpr static bcm_t pin10   = bcm_t::bcm15;
    [[maybe_unused]] constexpr static bcm_t pin12   = bcm_t::bcm18;

    [[maybe_unused]] constexpr static bcm_t pin16   = bcm_t::bcm23;
    [[maybe_unused]] constexpr static bcm_t pin18   = bcm_t::bcm24;

    [[maybe_unused]] constexpr static bcm_t pin22   = bcm_t::bcm25;
    [[maybe_unused]] constexpr static bcm_t pin24   = bcm_t::bcm08;
    [[maybe_unused]] constexpr static bcm_t pin26   = bcm_t::bcm07;

    [[maybe_unused]] constexpr static bcm_t pin32   = bcm_t::bcm12;

    [[maybe_unused]] constexpr static bcm_t pin36   = bcm_t::bcm16;
    [[maybe_unused]] constexpr static bcm_t pin38   = bcm_t::bcm20;
    [[maybe_unused]] constexpr static bcm_t pin40   = bcm_t::bcm21;

    /*

      GPIO       pin  pin    GPIO
      3V3         1    2      5V
      2 (SDA)     3    4      5V
      3 (SCL)     5    6      0V
      4           7    8      14 (TXD)
      0V          9   10      15 (RXD)
      17 (ce1)   11   12      18 (ce0)
      27         13   14      0V
      22         15   16      23
      3V3        17   18      24
      10 (MOSI)  19   20      0V
      9 (MISO)   21   22      25
      11 (SCLK)  23   24      8 (CE0)
      0V         25   26      7 (CE1)
                 .......
      0 (ID_SD)  27   28      1 (ID_SC)
      5          29   30      0V
      6          31   32      12
      13         33   34      0V
      19 (miso)  35   36      16 (ce2)
      26         37   38      20 (mosi)
      0V         39   40      21 (sclk)


    */

    // fragwürdig
    [[maybe_unused]] constexpr static level_t low   = 0;
    [[maybe_unused]] constexpr static level_t high  = 1;

    [[maybe_unused]] constexpr static level_t off = 0;
    [[maybe_unused]] constexpr static level_t on  = 1;


    //map<bcm> -> pin
    // op<

    [[maybe_unused]] constexpr static uint32_t TimeOut  = 2;
    [[maybe_unused]] constexpr static const char* Version  = "libPiGPIO 0.1.27 / 16.09.2022 2022 chris@sourceworx.org";


    LIBPIGPIO_EXPORT pigCore& get_instance();

    LIBPIGPIO_EXPORT int init( const std::string& host ="" , const std::string& port="" );

    // pin config
    LIBPIGPIO_EXPORT int get_io_mode( bcm_t bcm );
    LIBPIGPIO_EXPORT int set_io_mode( bcm_t bcm, io_t mode );
    LIBPIGPIO_EXPORT int set_pud_mode( bcm_t bcm, pud_t pud );
    LIBPIGPIO_EXPORT int set_glitch_filter( bcm_t bcm, uint32_t glitch = GlitchDef );
    LIBPIGPIO_EXPORT int set_noise_filter( bcm_t bcm, uint32_t steady, uint32_t active ) ;

    // pin operations
    LIBPIGPIO_EXPORT int get_level( bcm_t bcm );
    LIBPIGPIO_EXPORT int set_level( bcm_t bcm, level_t level ) ;
    LIBPIGPIO_EXPORT int trigger_level( bcm_t bcm, level_t level, uint32_t pulseLen );
    LIBPIGPIO_EXPORT int wait_for_edge( bcm_t bcm, edge_t edge, double timeout = TimeoutDef );

    // reality interface
    LIBPIGPIO_EXPORT int set_callback( bcm_t bcm, pigCall* fn, edge_t edge );
    LIBPIGPIO_EXPORT int cancel_callback( int cbId );

    // software PWM
    /*
    set_PWM_dutycycle	Start/stop PWM pulses on a GPIO
    set_PWM_frequency	Configure PWM frequency for a GPIO
    set_PWM_range	Configure PWM range for a GPIO
    get_PWM_dutycycle	Get the PWM dutycycle in use on a GPIO
    get_PWM_frequency	Get configured PWM frequency for a GPIO
    get_PWM_range	Get configured PWM range for a GPIO
    get_PWM_real_range	Get underlying PWM range for a GPIO
    */

    // i2c
    LIBPIGPIO_EXPORT int i2c_open( pigI2C& i2c );
    LIBPIGPIO_EXPORT int i2c_close( pigI2C& i2c );
    /*
    LIBPIGPIO_EXPORT int i2c_write_quick( pigI2C& i2c, uint32_t bit );
    LIBPIGPIO_EXPORT int i2c_write_byte( pigI2C& i2c, uint32_t bVal );
    LIBPIGPIO_EXPORT int i2c_write_byte_data( pigI2C& i2c, uint32_t reg, uint32_t val );
    LIBPIGPIO_EXPORT int i2c_write_word_data( pigI2C& i2c, uint32_t reg, uint32_t val );
    LIBPIGPIO_EXPORT int i2c_write_block_data( pigI2C& i2c, uint32_t reg, char* buf, uint32_t count  );
    LIBPIGPIO_EXPORT int i2c_write_i2c_block_data( pigI2C& i2c, uint32_t reg, char* buf, uint32_t count );
    */
    LIBPIGPIO_EXPORT int i2c_write_device( pigI2C& i2c, char* buf, uint32_t count );
    /*
    LIBPIGPIO_EXPORT int i2c_read_byte( pigI2C& i2c );
    LIBPIGPIO_EXPORT int i2c_read_byte_data( pigI2C& i2c, uint32_t reg );
    LIBPIGPIO_EXPORT int i2c_read_word_data( pigI2C& i2c, uint32_t reg );
    LIBPIGPIO_EXPORT int i2c_read_block_data( pigI2C& i2c, uint32_t reg, char* buf );
    LIBPIGPIO_EXPORT int i2c_read_i2c_block_data( pigI2C& i2c, uint32_t reg, char* buf, uint32_t count );
    LIBPIGPIO_EXPORT int i2c_read_device( pigI2C& i2c, char* buf, uint32_t count );

    LIBPIGPIO_EXPORT int i2c_process_call( pigI2C& i2c, uint32_t i2c_reg, uint32_t wVal);
    LIBPIGPIO_EXPORT int i2c_block_process_call( pigI2C& i2c, uint32_t i2c_reg, char* buf, uint32_t count );
    LIBPIGPIO_EXPORT int i2c_zip( pigI2C& i2c, char *inBuf, uint32_t inLen, char *outBuf, uint32_t outLen );
    */

    // advanced
    LIBPIGPIO_EXPORT int hardware_PWM( bcm_t bcm, uint32_t PWMfreq, uint32_t PWMduty ) ;
    LIBPIGPIO_EXPORT int hardware_clock( bcm_t bcm, uint32_t clkfreq );
    LIBPIGPIO_EXPORT uint32_t get_current_tick();

    // finally
    //LIBPIGPIO_EXPORT void trigger( int pi, bcm_t pin, level_t level, uint32_t tick, void *user );
    LIBPIGPIO_EXPORT void trigger( int pi, unsigned  bcm, unsigned level, uint32_t tick, void *user );
    LIBPIGPIO_EXPORT void greet();
    LIBPIGPIO_EXPORT void wait_loop();

    // timer
    LIBPIGPIO_EXPORT void delay_seconds( uint32_t );
    LIBPIGPIO_EXPORT void delay_millis( uint32_t );
    LIBPIGPIO_EXPORT void delay_micros( uint32_t );

    // util
    template< typename T >
    LIBPIGPIO_EXPORT std::string val2str( T value, int fixed=2  )
    {
        std::ostringstream sout;
        sout << std::fixed << std::setprecision( fixed );
        sout  << value;
        return sout.str();
    }

} // namespace

using signalBool  = sigslot::signal<bool>;
using signalInt   = sigslot::signal<int>;
using signalFloat = sigslot::signal<double>;


#endif // LIBPIGPIO_H
