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

    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

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


#include <libpigpio.h>
#include <pigpin.h>
#include <pignode.h>

#include <libpigwindows.h>
#include <libpiglinux.h>


namespace pigpio
{

    pigCore& get_instance()
    {
        #if defined(unix) || defined(__unix__) || defined(__unix)
        static pigLinux _Instance;  // guaranteed to be destroyed.
        #endif //Q_OS_LINUX
        #ifdef _WIN32
        static pigWindows _Instance; //guaranteed to be destroyed.
        #endif // _WIN32
        return _Instance;
    }

    // bcm config
    int set_io_mode( bcm_t bcm, io_t mode )
    {
        return get_instance().set_io_mode( bcm, mode );
    }


    int get_io_mode( bcm_t bcm )
    {
        return get_instance().get_io_mode( bcm );
    }


    int set_pud_mode( bcm_t bcm, pud_t pud )
    {
        return get_instance().set_pud_mode( bcm, pud );
    }


    int set_glitch_filter(bcm_t bcm, uint32_t glitch )
    {
        return get_instance().set_glitch_filter( bcm, glitch );
    }


    int set_noise_filter(bcm_t bcm, unsigned steady, unsigned active )
    {
        return get_instance().set_noise_filter( bcm, steady, active );
    }


    // bcm operations
    int get_level( bcm_t bcm )
    {
        return get_instance().get_level( bcm );
    }


    int set_level( bcm_t bcm , level_t level )
    {
        return get_instance().set_level( bcm, level );
    }


    int trigger_level( bcm_t bcm,  level_t level, uint32_t pulseLen )
    {
        return get_instance().trigger_level( bcm, pulseLen, level );
    }


    int wait_for_edge( bcm_t bcm, edge_t edge, double timeout )
    {
        return get_instance().wait_for_edge( bcm, edge, timeout );
    }

    // reality interface
    int set_callback( bcm_t bcm, pigCall* fn, edge_t edge )
    {
        return get_instance().set_callback( bcm, fn, edge );
    }


    int cancel_callback( int cbId )
    {
        return get_instance().cancel_callback( 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
    int i2c_open( pigI2C& i2c )
    {
        return get_instance().i2c_open( i2c );
    }

    int i2c_close( pigI2C& i2c )
    {
        return get_instance().i2c_close( i2c );
    }

    /*

    int i2c_write_quick( pigI2C& i2c, unsigned bit )
    {
        return get_instance().
    }

    int i2c_write_byte( pigI2C& i2c, unsigned bVal )
    {
        return get_instance().
    }

    int i2c_write_byte_data( pigI2C& i2c, unsigned reg, unsigned val )
    {
        return get_instance().
    }

    int i2c_write_word_data( pigI2C& i2c, unsigned reg, unsigned val )
    {
        return get_instance().
    }

    int i2c_write_block_data( pigI2C& i2c, unsigned reg, char* buf, unsigned count  )
    {
        return get_instance().
    }

    int i2c_write_i2c_block_data( pigI2C& i2c, unsigned reg, char* buf, unsigned count )
    {
        return get_instance().
    }
    */

    int i2c_write_device( pigI2C& i2c, char* buf, unsigned count )
    {
        return get_instance().i2c_write_device( i2c,  buf, count );
    }

    /*

    int i2c_read_byte( pigI2C& i2c )
    {
        return get_instance().
    }

    int i2c_read_byte_data( pigI2C& i2c, unsigned reg )
    {
        return get_instance().
    }

    int i2c_read_word_data( pigI2C& i2c, unsigned reg )
    {
        return get_instance().
    }

    int i2c_read_block_data( pigI2C& i2c, unsigned reg, char* buf )
    {
        return get_instance().
    }

    int i2c_read_i2c_block_data( pigI2C& i2c, unsigned reg, char* buf, unsigned count )
    {
        return get_instance().
    }

    int i2c_read_device( pigI2C& i2c, char* buf, unsigned count )
    {
        return get_instance().
    }


    int i2c_process_call( pigI2C& i2c, unsigned i2c_reg, unsigned wVal)
    {
        return get_instance().
    }

    int i2c_block_process_call( pigI2C& i2c, unsigned i2c_reg, char* buf, unsigned count )
    {
        get_instance().i2c_block_process_call();
    }

    int i2c_zip( pigI2C& i2c, char *inBuf, unsigned inLen, char *outBuf, unsigned outLen )
    {
        get_instance().i2c_zip( i2c, inBuf, inLen, outBuf, outLen );
    }
    */

    // advanced
    int hardware_PWM( bcm_t bcm, unsigned PWMfreq, uint32_t PWMduty )
    {
        return get_instance().hardware_PWM( bcm, PWMfreq, PWMduty );
    }


    int hardware_clock( bcm_t bcm, unsigned clkfreq )
    {
        return get_instance().hardware_clock( bcm, clkfreq );
    }

    uint32_t get_current_tick()
    {
        return get_instance().get_current_tick();
    }

    void show_error( const std::string& msg )
    {
        std::cout << "error: " << msg << std::endl;
    }


    void show_hint( const std::string& msg )
    {
        std::cout << "hint: " << msg << std::endl;
    }


    ///
    /// timers
    ///

    void delay_seconds( uint32_t tval )
    {
        std::this_thread::sleep_for(std::chrono::seconds( tval ) );
    }


    void delay_millis( uint32_t tval )
    {
        std::this_thread::sleep_for(std::chrono::milliseconds( tval ) );
    }


    void delay_micros( uint32_t tval )
    {
        std::this_thread::sleep_for(std::chrono::microseconds( tval ) );
    }


    void wait_loop()
    {
        while( true )
           delay_seconds( 10 );
    }


    // void trigger( int pi, unsigned gpio, unsigned level, uint32_t tick, void *user )
    void trigger( int, unsigned  bcm, unsigned level, uint32_t, void *user )
    {   
        /// Need a static callback to link with C.
        pigCall* receiver = static_cast<pigCall*>( user );
        if( receiver )
             receiver->trigger( (bcm_t) bcm, level ); /// Call the get_instance callback.
        else
            std::cout <<  "-- ONPULSE FAIL" << (uint32_t) bcm << std::endl;
    }


    void greet()
    {
        std::cout << "lib:\t" << pigpio::Version  << std::endl;
        std::cout << "gpio:\t"    <<  get_instance().get_pigpio_version() << std::endl;
        std::cout << "hw rev:\t"  <<  get_instance().get_hardware_revision() << std::endl;
        std::cout << "deamon:\t"  <<  get_instance().pigpiod_if_version() << std::endl;
        std::cout << "\n" << std::endl;
    }


    int show_error( int error )
    {
        return error;
    }


} // namespace pigpio
