/*
TM16xx.h - Library for TM1637, TM1638 and similar chips.

Copyright (C) 2011 Ricardo Batista <rjbatista at gmail dot com>
Modified by Maxint R&D. See https://github.com/maxint-rd/

This program is free software: you can redistribute it and/or modify
it under the terms of the version 3 GNU General Public License as
published by the Free Software Foundation.

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/>.
*/

#ifndef TM16XX_h
#define TM16XX_h

#if !defined(__max)
// MMOLE 180325: min, max are no macro in ESP core 2.3.9 libraries, see https://github.com/esp8266/Arduino/issues/398
// MMOLE 211229: Redefining min/max has issues in newer ESP cores with certain wifi libraries.
//               See definition as function template below.
#define __min(a,b) ((a)<(b)?(a):(b))
#define __max(a,b) ((a)>(b)?(a):(b))
#endif

#if !defined(_BV)
// MMOLE 220909: _BV is not defined in Raspberry Pi Pico RP2040 core v2.4.0 or v2.5.2 by Earle F. Philhower
// TM16xx uses it in some classes to determine button presses (TM1628/30/37/38/38Anode
// Note that _BV works on int values (16-bit in Arduino). For that reason using bit() would be better.
// This is how _BV is defined in the AVR libraries used within Arduino:
#define _BV( x )( 1<<(x))
#endif

#define TM16XX_CMD_DATA_AUTO 0x40
#define TM16XX_CMD_DATA_READ 0x42			// command to read data used on two wire interfaces of TM1637
#define TM16XX_CMD_DATA_FIXED 0x44
#define TM16XX_CMD_DISPLAY 0x80
#define TM16XX_CMD_ADDRESS 0xC0

// Common display modes for selecting different GRID x SEGMENT configuration
// Used by e.g. TM1618, TM1620, TM1628, TM1630, TM1668 
// (note: TM1652 combined display control and uses a single bit to designate 5x8 or 6x8)
#define TM16XX_CMD_MODE_4GRID 0x00
#define TM16XX_CMD_MODE_5GRID 0x01    // TM1630: 4x8 - 5x7
#define TM16XX_CMD_MODE_6GRID 0x02    // TM1620: 4x10 - 6x8
#define TM16XX_CMD_MODE_7GRID 0x03    // TM1628, TM1668: 4x13 - 7x10, TM1624: 4x14 - 7x11, TM1618: 4x8 - 7x5

#include "TM16xxFonts.h"
#include <driver/gpio.h>

class TM16xx
{
  public:
    /**
     * Instantiate a TM16xx module specifying data, clock and strobe pins (no strobe on some modules),
     * the maximum number of displays supported by the chip (as provided by derived chip specific class), 
     * the number of digits used to display numbers or text, 
     * display state and the starting intensity (0-7).
     */
    TM16xx(gpio_num_t dataPin, gpio_num_t clockPin, gpio_num_t strobePin, uint8_t maxDisplays, uint8_t nDigitsUsed, bool activateDisplay=true,	uint8_t intensity=7);

    /** Set the display (segments and LEDs) active or off and intensity (range from 0-7). */
    virtual void setupDisplay(bool active, uint8_t intensity);

    /** Clear the display */
		virtual void clearDisplay();

    /** Set segments of the display */
	  virtual void setSegments(uint8_t segments, uint8_t position);
	  virtual void setSegments16(uint16_t segments, uint8_t position);   // some modules support more than 8 segments
	  
	  // Basic display functions. For additional display features use the TM16xxDisplay class
    /** Set a single display at pos (starting at 0) to a digit (left to right) */
    virtual void setDisplayDigit(uint8_t digit, uint8_t pos=0, bool dot=false, const uint8_t numberFont[] = TM16XX_NUMBER_FONT);
		/** Set the display to a decimal number */
	  virtual void setDisplayToDecNumber(int nNumber, uint8_t bDots=0);
		/** Clear  a single display at pos (starting at 0, left to right) */ 
    virtual void clearDisplayDigit(uint8_t pos, bool dot=false);
    /** Set the display to the values (left to right) */
    virtual void setDisplay(const uint8_t values[], uint8_t size=8);

    /** Set the display to the string (defaults to built in font) */
		virtual void setDisplayToString(const char* string, const uint32_t dots=0, const uint8_t pos=0, const uint8_t font[] = TM16XX_FONT_DEFAULT);
		virtual void sendChar(uint8_t pos, uint8_t data, bool dot); // made public to allow calling from TM16xxDisplay
		virtual uint8_t getNumDigits(); // added as public menthod to allow calling from TM16xxDisplay
		virtual void sendAsciiChar(uint8_t pos, char c, bool dot); // made public to allow calling from TM16xxDisplay

		// Key-scanning functions
		// Note: not all TM16xx chips support key-scanning and sizes are different per chip
		// Up to 32 key states are supported, but specific chips may support less keys or less combinations
		// The chip specific derived class method will return a 32-bit value representing the state of each key, containing 0 if no key is pressed
		virtual uint32_t getButtons();  // return state of up to 32 keys.

  protected:
//		virtual void sendChar(byte pos, byte data, bool dot);
	  virtual void bitDelay();
    virtual void start();
    virtual void stop();
    virtual void send(uint8_t data);
		virtual void sendCommand(uint8_t led);
    virtual void sendData(uint8_t add, uint8_t data);
    virtual uint8_t receive();

#if !defined(max)
// MMOLE 211229: use c++ function templates to implement our own min/max, as redefining them wont work in newer ESP cores when using certain wifi libraries
// NOTE: min, max are no macro in ESP core 2.3.9 libraries, see https://github.com/esp8266/Arduino/issues/398
// See also
//   https://www.cplusplus.com/doc/oldtutorial/templates/
//   https://www.alltestanswers.com/c-templates-for-the-two-functions-minimum-and-maximum/
//   https://www.learncpp.com/cpp-tutorial/function-templates-with-multiple-template-types/
// MMOLE 220814: Arduino IDE 1.8.12 for LGT328P required "#if !defined(max)" to prevent compilation errors
template <typename T, typename U>
auto max(T x, U y) -> decltype(x>y ? x : y)
{
  return x>y ? x : y;
}
template <typename T, typename U>
auto min(T x, U y) -> decltype(x>y ? x : y)
{
  return x<y ? x : y;
}
#endif  // !defined(max)

    uint8_t _maxDisplays=2;		// maximum number of digits (grids), chip-dependent
    uint8_t _maxSegments=8;		// maximum number of segments per display, chip-dependent

    uint8_t digits;		// number of digits in the display, module dependent
    gpio_num_t dataPin;
    gpio_num_t clockPin;
    gpio_num_t strobePin;
};

#endif
