package com.knutejohnson.pi.jpigpio;

import java.io.*;
import java.net.*;
import java.nio.*;
import java.util.*;
import static java.util.stream.Collectors.*;

import static com.knutejohnson.pi.jpigpio.JPigpioConstants.*;

/**
 * JPigpio
 *
 * A Java wrapper written in all Java to access the pigpio library through its
 * socket interface.  See http://abyz.me.uk/rpi/pigpio/ for details of the
 * pigpio libary.  See http://knutejohnson.com/pi/jpigpio for details of this
 * library.
 *
 * @version 0.21.0 - 2021-02-11
 * @author  Knute Johnson
 */
public class JPigpio implements AutoCloseable {
    /** JPigpio version */
    public static final String VERSION = "0.21.0";

    /** Socket to connect to pigpio */
    private final Socket socket;

    /** OutputStream for socket */
    private final OutputStream os;

    /** InputStream for socket */
    private final InputStream is;

    /** List of commands that return 32 bit unsigned results */
    private final List<Integer> listOf32BitResults =
      Arrays.asList(10,11,16,17,26);

    /** Debug flag */
    private volatile boolean debugMode;

    /**
     * Create a new JPigpio object and connect it to the specified pigpio on
     * the specified port.
     *
     * @param   ip    hostname or ip address where pigpio is running
     * @param   port  port pigpio is listening on
     * @throws  UnknownHostException if the IP address of the host could not be
     *          determined
     * @throws  IOException if an I/O error occurs creating the socket or when
     *          creating the input or output streams
     */
    public JPigpio(String ip, int port)
     throws UnknownHostException,IOException {
        socket = new Socket(ip,port);
        os = socket.getOutputStream();
        is = socket.getInputStream();
    }

    /**
     * Create a new JPigpio object and connects it to the local machine on the
     * default port.
     *
     * @throws  UnknownHostException if the IP address of the host could not be
     *          determined
     * @throws  IOException if an I/O error occurs creating the socket or when
     *          creating the input or output streams
     */
    public JPigpio() throws UnknownHostException, IOException {
        this("::1",8888);
    }

    /**
     * Set the mode for the GPIO pin.
     *
     * @param   gpio    GPIO pin number
     * @param   mode    GPIO pin modes (PI_INPUT, PI_OUTPUT, PI_ALT0 - 7)
     * @return  0 for success, otherwise PI_BAD_GPIO, PI_BAD_MODE, or
     *           PI_NOT_PERMITTED 
     * @throws  IOException if an I/O error occurs
     */
    public int modeSet(int gpio, int mode) throws IOException {
        sendCmd(PI_CMD_MODES,gpio,mode,0);
        return readEcho();
    }

    /**
     * Get the mode for the GPIO pin.
     *
     * @param   gpio    GPIO pin number
     * @return  the GPIO mode or PI_BAD_GPIO
     * @throws  IOException if an I/O error occurs
     */
    public int modeGet(int gpio) throws IOException {
        sendCmd(PI_CMD_MODEG,gpio,0,0);
        return readEcho();
    }

    /**
     * Set the pull up/down for the specified GPIO pin.
     *
     * @param   gpio    GPIO pin number
     * @param   pud     one of PI_PUD_OFF, PI_PUD_DOWN, or PI_PUD_UP
     * @return  0 for success or PI_BAD_GPIO, PI_BAD_PUD, or PI_NOT_PERMITTED
     * @throws  IOException if an I/O error occurs
     */
    public int pullUpDown(int gpio, int pud) throws IOException {
        sendCmd(PI_CMD_PUD,gpio,pud,0);
        return readEcho();
    }

    /**
     * Read the level on the specified GPIO pin.
     *
     * @param   gpio    GPIO pin number
     * @return  gpio level or PI_BAD_GPIO
     * @throws  IOException if an I/O error occurs
     */
    public int read(int gpio) throws IOException {
        sendCmd(PI_CMD_READ,gpio,0,0);
        return readEcho();
    }

    /**
     * Write the level to the GPIO pin.
     *
     * @param   gpio    GPIO pin number
     * @param   level   0 or 1
     * @return  0 on success or PI_BAD_GPIO, PI_BAD_LEVEL, or PI_NOT_PERMITTED
     * @throws  IOException if an I/O error occurs
     */
    public int write(int gpio, int level) throws IOException {
        sendCmd(PI_CMD_WRITE,gpio,level,0);
        return readEcho();
    }

    /**
     * Start PWM (non-zero dutycycle) or stop (0) on the specified GPIO.
     *
     * @param   gpio        GPIO pin number
     * @param   dutycycle   pwm dutycycle
     * @return  0 on success or PI_BAD_USER_GPIO, PI_BAD_DUTYCYCLE, or
     *           PI_NOT_PERMITTED
     * @throws  IOException if an I/O error occurs
     * @see     <a href="http://abyz.me.uk/rpi/pigpio/pdif2.html#set_PWM_dutycycle">pigpiod documentation for setPWM_range</a>
     */
    public int setPWMValue(int gpio, int dutycycle) throws IOException {
        sendCmd(PI_CMD_PWM,gpio,dutycycle,0);
        return readEcho();
    }

    /**
     * Set PWM range.  If PWM is currently active on the GPIO its dutycycle
     * will be scaled to reflect the new range.
     *
     * @param   gpio    GPIO pin number
     * @param   range   pwm range (25-40000)
     * @return  0 on success or PI_BAD_USER_GPIO or PI_NOT_PWM_GPIO
     * @throws  IOException if an I/O occurs
     * @see     <a href="http://abyz.me.uk/rpi/pigpio/pdif2.html#set_PWM_range">pigpiod documentation for set_PWM_range</a>
     */
    public int setPWMRange(int gpio, int range) throws IOException {
        sendCmd(PI_CMD_PRS,gpio,range,0);
        return readEcho();
    }

    /**
     * Set PWM frequency.  Overrides servo commands on this GPIO.
     *
     * @param   gpio    GPIO pin number
     * @param   freq    pwm frequency
     * @return  Numerically closest frequency on success or PI_BAD_USER_GPIO,
     *           if PWM is currently active on the GPIO it will be switched off
     *          and then back on at the new frequency.  Each GPIO can be set
     *          to one of 18 different PWM frequencies.  The selectable
     *          frequencies depend on the sample rate which is set when the
     *          pigpio daemon is started.
     * @throws  IOException if an I/O error occurs
     * @see     <a href="http://abyz.me.uk/rpi/pigpio/pdif2.html#set_PWM_frequency">pigpiod documentation for set_PWM_frequency</a>
     */
    public int setPWMFreq(int gpio, int freq) throws IOException {
        sendCmd(PI_CMD_PFS,gpio,freq,0);
        return readEcho();
    }

    /**
     * Get PWM range.
     *
     * @param   gpio    GPIO pin number
     * @return  pwm range or PI_BAD_USER_GPIO
     * @throws  IOException if an I/O error occurs
     * @see     <a href="http://abyz.me.uk/rpi/pigpio/pdif2.html#get_PWM_dutycycle">pigpiod documentation for get_PWM_range</a>
     */
    public int getPWMRange(int gpio) throws IOException {
        sendCmd(PI_CMD_PRG,gpio,0,0);
        return readEcho();
    }

    /**
     * Get PWM frequency.
     *
     * @param   gpio    GPIO pin number
     * @return  pwm frequency or PI_BAD_USER_GPIO
     * @throws  IOException if an I/O error occurs
     * @see     <a href="http://abyz.me.uk/rpi/pigpio/pdif2.html#get_PWM_frequency">see pigpiod documenation</a>
     */
    public int getPWMFreq(int gpio) throws IOException {
        sendCmd(PI_CMD_PFG,gpio,0,0);
        return readEcho();
    }

    /**
     * Get PWM real range.
     *
     * @param   gpio    GPIO pin number
     * @return  pwm real range or PI_BAD_USER_GPIO
     * @throws  IOException if an I/O error occurs
     * @see     <a href="http://abyz.me.uk/rpi/pigpio/pdif2.html#get_PWM_real_range">see pigpiod documentation</a>
     */
    public int getPWMRealRange(int gpio) throws IOException {
        sendCmd(PI_CMD_PRRG,gpio,0,0);
        return readEcho();
    }

    /**
     * Get PWM value (dutycycle).
     *
     * @param   gpio    GPIO pin number
     * @return  dutycycle or PI_BAD_USER_GPIO or PI_NOT_PWM_GPIO
     * @see     <a href="http://abyz.me.uk/rpi/pigpio/pdif2.html#get_PWM_dutycycle">see pigpiod documentation</a>
     * @throws  IOException if an I/O error occurs
     */
    public int getPWMValue(int gpio) throws IOException {
        sendCmd(PI_CMD_GDC,gpio,0,0);
        return readEcho();
    }

    /**
     * Starts servo pulses on the specified GPIO in pulsewidth milliseconds.
     * Overrides PWM commands on this GPIO.
     *
     * @param   gpio    GPIO pin number
     * @param   pulsewidth  pulse width in milliseconds, values from 500 to
     *          2500 start the pulses with 0 stopping the pulses
     * @return  0 on success or PI_BAD_USER_GPIO, PI_BAD_PULSEWIDTH, or
     *           PI_NOT_PERMITTED
     * @throws  IOException if an I/O error occurs
     */
    public int setServoPulsewidth(int gpio, int pulsewidth) throws IOException {
        sendCmd(PI_CMD_SERVO,gpio,pulsewidth,0);
        return readEcho();
    }

    /**
     * Read the levels of bank 1 GPIO (0-31).
     *
     * @return  bitmask in int
     * @throws  IOException if an I/O error occurs
     */
    public int readBank1() throws IOException {
        sendCmd(PI_CMD_BR1,0,0,0);
        return readEcho();
    }

    /**
     * Read the levels of bank 2 GPIO (32-53).
     * 
     * @return  bitmask in lower order 21 bits corresponding to GPIO 32-53
     * @throws  IOException if an I/O error occurs
     */
    public int readBank2() throws IOException {
        sendCmd(PI_CMD_BR2,0,0,0);
        return readEcho();
    }

    /**
     * Clear bank 1 GPIO (0-31) if the corresponding bit is set.
     *
     * @param   bits    bitmask
     * @return  0 if success or PI_SOME_PERMITTED.  A status of
     *           PI_SOME_PERMITTED indicates the user is not allowed t write
     *           to one or more of the GPIO
     * @throws  IOException if an I/O error occurs
     */
    public int clearBank1(int bits) throws IOException {
        sendCmd(PI_CMD_BC1,bits,0,0);
        return readEcho();
    }

    /**
     * Clear bank 2 GPIO (32-53) if the corresponding bit is set.
     * Bit 0 corresponds to pin 32 and bit 21 to pin 53.
     *
     * @param   bits    bitmask in lower order 21 bits
     * @return  0 if success or PI_SOME_PERMITTED.  A status of
     *           PI_SOME_PERMITTED indicates the user is not allowed t write
     *           to one or more of the GPIO
     * @throws  IOException if an I/O error occurs
     */
    public int clearBank2(int bits) throws IOException {
        sendCmd(PI_CMD_BC2,bits,0,0);
        return readEcho();
    }

    /**
     * Set bank 1 GPIO pins (0-31) corresponding to bitmask.
     *
     * @param   bits    bitmask
     * @return  0 if success or PI_SOME_PERMITTED.  A status of
     *           PI_SOME_PERMITTED indicates the user is not allowed t write
     *           to one or more of the GPIO
     * @throws  IOException if an I/O error occurs
     */
    public int setBank1(int bits) throws IOException {
        sendCmd(PI_CMD_BS1,bits,0,0);
        return readEcho();
    }

    /**
     * Set bank 2 GPIO pins (32-53) corresponding to bitmask.
     * Bit 0 corresponds to pin 32 and bit 21 to pin 53.
     *
     * @param   bits    bitmask in lower order 21 bits
     * @return  0 if success or PI_SOME_PERMITTED.  A status of
     *           PI_SOME_PERMITTED indicates the user is not allowed t write
     *           to one or more of the GPIO
     * @throws  IOException if an I/O error occurs
     */
    public int setBank2(int bits) throws IOException {
        sendCmd(PI_CMD_BS2,bits,0,0);
        return readEcho();
    }

    /**
     * Gets the servo pulse width on the specified GPIO pin.
     *
     * @param   gpio    GPIO pin number
     * @return  pulse width in milliseconds or PI_BAD_USER o rPI_NOT_SERVO_GPIO
     * @throws  IOException if an I/O error occurs
     */
    public int getServoPulseWidth(int gpio) throws IOException {
        sendCmd(PI_CMD_GPW,gpio,0,0);
        return readEcho();
    }

    /**
     * Sets a glitch filter on a GPIO.  Level changes are not reported unless
     * the level has been stable for at least 'steady' microseconds.
     *
     * @param   gpio    GPIO to filter (0 - 31)
     * @param   steady  time in microseconds level change must be steady
     * @return  0 on success or PI_BAD_USER_GPIO or PI_BAD_FILTER
     * @throws  IOException if an I/O error occurs
     * @see     <a href="http://abyz.me.uk/rpi/pigpio/pdif2.html#set_glitch_filter">pigpiod documentation for set_glitch_filter</a>
     */
    public int setGlitchFilter(int gpio,int steady) throws IOException {
        sendCmd(PI_CMD_FG,gpio,steady,0);
        return readEcho();
    }

    /**
     * Sets a noise filter on a GPIO.  Level changes are ignored until a level
     * which has been stable for 'steady' microseconds is detected.  Level
     * changes are reported for 'active' microseconds after which the process
     * repeats.
     *
     * @param   gpio    GPIO to filter (0 - 31)
     * @param   steady  time in microseconds level change must be steady
     * @param   active  period during which level changes are rported
     * @return  0 on success or PI_BAD_USER_GPIO or PI_BAD_FILTER
     * @throws  IOException if an I/O error occurs
     * @see     <a href="http://abyz.me.uk/rpi/pigpio/pdif2.html#set_noise_filter">pigpiod documentation for set_noise_filter</a>
     */
    public int setNoiseFilter(int gpio,int steady,int active)
     throws IOException {
        sendCmd(PI_CMD_FN,gpio,steady,4);
        sendExt(active);
        return readEcho();
    }

    /**
     * Returns the current tick count from the pigpio library.  The tick count
     * is stored in the pigpio library as an unsigned 32 bit number that will
     * roll over about 72 minutes after boot.
     *
     * @return  tick count
     * @throws  IOException if an I/O error occurs
     */
    public long tick() throws IOException {
        sendCmd(PI_CMD_TICK,0,0,0);
        return Integer.toUnsignedLong(readEcho());
    }

    /**
     * Measure an elapsed time period in ticks between two tick counts dealing
     * with the 32 bit rollover.
     *
     * @param   first   the tick count at the beginning of the period
     * @param   second  the tick count at the end of the period
     * @return  the elapsed time in ticks
     */
    public long tickDiff(long first, long second) {
        long diff = second - first;

        diff &= 0xffffffffL;

        return diff;
    }

    /**
     * Returns the hardware version.
     *
     * @return  the hardware version (display in hex for readability)
     * @throws  IOException if an I/O error occurs
     */
    public long hardwareVersion() throws IOException {
        sendCmd(PI_CMD_HWVER,0,0,0);
        return Integer.toUnsignedLong(readEcho());
    }

    /**
     * Returns the pigpio version.
     *
     * @return  the pigpio version
     * @throws  IOException if an I/O error occurs
     */
    public long pigpioVersion() throws IOException {
        sendCmd(PI_CMD_PIGPV,0,0,0);
        return Integer.toUnsignedLong(readEcho());
    }

    /**
     * Requests a free notification handle.
     *
     * @return  notification handle or PI_NO_HANDLE
     * @throws  IOException if an I/O error occurs
     */
    public int notifyOpen() throws IOException {
        sendCmd(PI_CMD_NO,0,0,0);
        return readEcho();
    }

    /**
     * Starts notifications on a previously opened handle.
     *
     * @param   handle  notification handle from notifyOpen
     * @param   bits    bit mask specifying which GPIO state change will be
     *                  sent notifications
     * @return  0 on success or PI_BAD_HANDLE
     * @throws  IOException if an I/O error occurs
     */
    public int notifyBegin(int handle, int bits) throws IOException {
        sendCmd(PI_CMD_NB,handle,bits,0);
        return readEcho();
    }

    /**
     * Pauses notifcations on a previously opened handle.
     *
     * @param   handle  notification handle
     * @return  0 on success or PI_BAD_HANDLE
     * @throws  IOException if an I/O error occurs
     */
    public int notifyPause(int handle) throws IOException {
        sendCmd(PI_CMD_NP,handle,0,0);
        return readEcho();
    }

    /**
     * Stops notifications on a previously opend handle and releases the handle.
     *
     * @param   handle  notification handle
     * @return  0 on success or PI_BAD_HANDLE
     * @throws  IOException if an I/O error occurs
     */
    public int notifyClose(int handle) throws IOException {
        sendCmd(PI_CMD_NC,handle,0,0);
        return readEcho();
    }

    /**
     * Sends a trigger pulse to specified GPIO of specified pulse length in
     * microseconds and level.
     *
     * @param   gpio        GPIO pin number
     * @param   pulselen    pulse length in microseconds
     * @param   level       level of the pulse
     * @return  0 on success or error code
     * @throws  IOException if an I/O error occurs
     */
    public int gpioTrigger(int gpio, int pulselen, int level)
     throws IOException {
        sendCmd(PI_CMD_TRIG,gpio,pulselen,4);
        sendExt(level);
        return readEcho();
    }

    /**
     * Starts a hardware clock at the specified frequency on the specified GPIO
     * pin.  Frequency of 0 stops the clock.
     *
     * @param   gpio    GPIO pin
     * @param   clkfreq clock frequency
     * @return  0 on success or PI_NOT_PERMITTED, PI_BAD_GPIO, PI_NOT_CHLK_GPIO,
     *           PI_BAD_HCLK_FREQ, or PI_BAD_HCLK_PASS
     * @throws  IOException if an I/O error occurs
     * @see     <a href="http://abyz.me.uk/rpi/pigpio/pdif2.html#hardware_clock">pipgiod documentation for hardware_clock</a>
     */
    public int hardwareClock(int gpio, int clkfreq) throws IOException {
        sendCmd(PI_CMD_HC,gpio,clkfreq,0);
        return readEcho();
    }

    /**
     * Starts hardware PWM on a GPIO at the specified frequency and dutycycle.
     *
     * @param   gpio    GPIO pin number
     * @param   freq    PWM frequency
     * @param   dutycycle   PWM dutycycle
     * @return  0 if success or PI_NOT_PERMITTED, PI_BAD_GPIO,PI_NOT_HPWM_GPIO,
     *           PI_BAD_HPWM_DUTY, PI_BAD_HPWM_FREQ, or PI_HPWM_ILLEGAL
     * @throws  IOException if an I/O error occurs
     * @see     <a href="http://abyz.me.uk/rpi/pigpio/pdif2.html#hardware_PWM">pigpiod documentation for hardware PWM</a>
     */
    public int hardwarePWM(int gpio,int freq, int dutycycle)
     throws IOException {
        sendCmd(PI_CMD_HP,gpio,freq,4);
        sendExt(dutycycle);
        return readEcho();
    }

    //-------------------------------------------------------------------------

    /**
     * Sends the specified command and parameters to the running pigpio library.
     *
     * @param   cmd pigpio command
     * @param   p1  first parameter
     * @param   p2  second parameter
     * @param   p3  third parameter
     * @throws  IOException if an I/O error occurs
     */
    protected void sendCmd(int cmd, int p1, int p2, int p3) throws IOException {
        ByteBuffer outBuf = ByteBuffer.allocate(16);
        outBuf.order(ByteOrder.LITTLE_ENDIAN);
        outBuf.putInt(cmd);
        outBuf.putInt(p1);
        outBuf.putInt(p2);
        outBuf.putInt(p3);

        os.write(outBuf.array());
        os.flush();
    }

    /**
     * Read the echo of a command and return the result field.  If the debug
     * mode is enabled on an error this method will pring the calling method
     * and the error message.
     *
     * @return  result field
     * @throws  IOException if an I/O error occurs
     */
    protected int readEcho() throws IOException {
        int n = 0;
        byte[] resBuf = new byte[16];

        while ((n += is.read(resBuf,n,16-n)) < 16) ;

        ByteBuffer inBuf = ByteBuffer.wrap(resBuf);
        inBuf.order(ByteOrder.LITTLE_ENDIAN);

        int cmd = inBuf.getInt(0);
        int res = inBuf.getInt(12);

        if (debugMode && res < 0 && !listOf32BitResults.contains(cmd)) {
            // get the calling method name
            StackWalker walker = StackWalker.getInstance();
            Optional<String> methodName = walker.walk(frames -> frames.
             limit(2).
             reduce((first,second) -> second).
             map(StackWalker.StackFrame::getMethodName));
            String funcName = methodName.isPresent() ? methodName.get() : "";

            System.out.printf("Error in %s: %s\n",funcName,
             JPigpio.errorToString(res));
        }

        return res;
    }

    /**
     * Send an integer (32 bit) extension to the running pigpio library.
     *
     * @param   ext integer extension
     * @throws  IOException if an I/O error occurs
     */
    public void sendExt(int ext) throws IOException {
        ByteBuffer outBuf = ByteBuffer.allocate(4);
        outBuf.order(ByteOrder.LITTLE_ENDIAN);
        outBuf.putInt(ext);

        os.write(outBuf.array());
        os.flush();
    }

    /**
     * Send a command extension to the running pigpio library.
     *
     * @param   buf byte array to send
     * @throws  IOException if an I/O error occurs
     */
    public void sendExt(byte[] buf) throws IOException {
        os.write(buf);
        os.flush();
    }

    /**
     * Send a command extension to the running pigpio library.
     *
     * @param   buf     byte array to send
     * @param   offset  into the byte array
     * @param   length  number of bytes to send
     * @throws  IOException if an I/O error occurs
     */
    public void sendExt(byte[] buf, int offset, int length) 
     throws IOException {
        os.write(buf,offset,length);
        os.flush();
    }

    /**
     * readReport reads the 12 byte packet from the specified input stream when
     * a notify has been sent.
     *
     * @param   is  InputStream to read report from
     * @return  a GPIOReport object with the report data
     * @throws  IOException if an I/O error occurs
     */
    public GPIOReport readReport(InputStream is) throws IOException {
        int n = 0;
        byte[] resBuf = new byte[12];
        while ((n += is.read(resBuf,n,12-n)) < 12) ;

        ByteBuffer inBuf = ByteBuffer.wrap(resBuf);
        inBuf.order(ByteOrder.LITTLE_ENDIAN);

        return new GPIOReport(
         Short.toUnsignedInt(inBuf.getShort()),
         Short.toUnsignedInt(inBuf.getShort()),
         Integer.toUnsignedLong(inBuf.getInt()),
         Integer.toUnsignedLong(inBuf.getInt()));
    }

    /**
     * Sets the debug mode.
     *
     * @param   state   true enables debug mode
     */
    public void setDebugMode(boolean state) {
        debugMode = state;
    }

    /**
     * Get debug mode.
     *
     * @return  true if debug mode is enabled otherwise false
     */
    public boolean getDebugMode() {
        return debugMode;
    }

    /**
     * Disconnects the socket from the running pigpio libaray.
     *
     * @throws  IOException if an I/O error occurs
     */
    public void disconnect() throws IOException {
        socket.close();
    }

    /**
     * Specified by AutoCloseable, disconnects from the running pigpio library.
     *
     * @throws  IOException if an I/O error occurs
     */
    @Override public void close() throws IOException {
        disconnect();
    }

    /**
     * Get the JPigpio version.
     *
     * @return  JPigpio version string
     */
    public static String getVersion() {
        return VERSION;
    }

    /**
     * Returns a string error message from an error number.
     *
     * @param   errno   pigpio error number
     * @return  error message string
     */
    public static String errorToString(int errno) {
        switch(errno) {
            case PI_NO_ERROR: return "no error";
            case PI_INIT_FAILED: return "gpioInitialise failed";
            case PI_BAD_USER_GPIO: return "GPIO not 0-31";
            case PI_BAD_GPIO: return "GPIO not 0-53";
            case PI_BAD_MODE: return "mode not 0-7";
            case PI_BAD_LEVEL: return "level not 0-1";
            case PI_BAD_PUD: return "pud not 0-2";
            case PI_BAD_PULSEWIDTH: return "pulsewidth not 0 or 500-2500";
            case PI_BAD_DUTYCYCLE: return "dutycycle outside set range";
            case PI_BAD_TIMER: return "timer not 0-9";
            case PI_BAD_MS: return "ms not 10-60000";
            case PI_BAD_TIMETYPE: return "timetype not 0-1";
            case PI_BAD_SECONDS: return "seconds < 0";
            case PI_BAD_MICROS: return "micros not 0-999999";
            case PI_TIMER_FAILED: return "gpioSetTimerFunc failed";
            case PI_BAD_WDOG_TIMEOUT: return "timeout not 0-60000";
            case PI_NO_ALERT_FUNC: return "DEPRECATED";
            case PI_BAD_CLK_PERIPH: return "clock peripheral not 0-1";
            case PI_BAD_CLK_SOURCE: return "DEPRECATED";
            case PI_BAD_CLK_MICROS: return "clock micros not 1, 2, 4, 5, 8, or 10";
            case PI_BAD_BUF_MILLIS: return "buf millis not 100-10000";
            case PI_BAD_DUTYRANGE: return "dutycycle range not 25-40000";
            case PI_BAD_SIGNUM: return "signum not 0-63";
            case PI_BAD_PATHNAME: return "can't open pathname";
            case PI_NO_HANDLE: return "no handle available";
            case PI_BAD_HANDLE: return "unknown handle";
            case PI_BAD_IF_FLAGS: return "ifFlags > 4";
            case PI_BAD_CHANNEL: return "DMA channel not 0-15";
//          case PI_BAD_PRIM_CHANNEL: return "DMA primary channel not 0-15";
            case PI_BAD_SOCKET_PORT: return "socket port not 1024-32000";
            case PI_BAD_FIFO_COMMAND: return "unrecognized fifo command";
            case PI_BAD_SECO_CHANNEL: return "DMA secondary channel not 0-15";
            case PI_NOT_INITIALISED: return "function called before gpioInitialise";
            case PI_INITIALISED: return "function called after gpioInitialise";
            case PI_BAD_WAVE_MODE: return "waveform mode not 0-3";
            case PI_BAD_CFG_INTERNAL: return "bad parameter in gpioCfgInternals call";
            case PI_BAD_WAVE_BAUD: return "baud rate not 50-250K(RX)/50-1M(TX)";
            case PI_TOO_MANY_PULSES: return "waveform has too many pulses";
            case PI_TOO_MANY_CHARS: return "waveform has too many chars";
            case PI_NOT_SERIAL_GPIO: return "no bit bang serial read on GPIO";
            case PI_BAD_SERIAL_STRUC: return "bad (null) serial structure parameter";
            case PI_BAD_SERIAL_BUF: return "bad (null) serial buf parameter";
            case PI_NOT_PERMITTED: return "GPIO operation not permitted";
            case PI_SOME_PERMITTED: return "one or more GPIO not permitted";
            case PI_BAD_WVSC_COMMND: return "bad WVSC subcommand";
            case PI_BAD_WVSM_COMMND: return "bad WVSM subcommand";
            case PI_BAD_WVSP_COMMND: return "bad WVSP subcommand";
            case PI_BAD_PULSELEN: return "trigger pulse length not 1-100";
            case PI_BAD_SCRIPT: return "invalid script";
            case PI_BAD_SCRIPT_ID: return "unknown script id";
            case PI_BAD_SER_OFFSET: return "add serial data offset > 30 minutes";
            case PI_GPIO_IN_USE: return "GPIO already in use";
            case PI_BAD_SERIAL_COUNT: return "must read at least a byte at a time";
            case PI_BAD_PARAM_NUM: return "script parameter id not 0-9";
            case PI_DUP_TAG: return "script has duplicate tag";
            case PI_TOO_MANY_TAGS: return "script has too many tags";
            case PI_BAD_SCRIPT_CMD: return "illegal script command";
            case PI_BAD_VAR_NUM: return "script variable id not 0-149";
            case PI_NO_SCRIPT_ROOM: return "no more room for scripts";
            case PI_NO_MEMORY: return "can't allocate temporary memory";
            case PI_SOCK_READ_FAILED: return "socket read failed";
            case PI_SOCK_WRIT_FAILED: return "socket write failed";
            case PI_TOO_MANY_PARAM: return "too many script parameters (> 10)";
//            case PI_NOT_HALTED: return "DEPRECATED";
            case PI_SCRIPT_NOT_READY: return "script initialising";
            case PI_BAD_TAG: return "script has unresolved tag";
            case PI_BAD_MICS_DELAY: return "bad MICS delay (too large)";
            case PI_BAD_MILS_DELAY: return "bad MILS delay (too large)";
            case PI_BAD_WAVE_ID: return "non existent wave id";
            case PI_TOO_MANY_CBS: return "No more CBs for waveform";
            case PI_TOO_MANY_OOL: return "No more OOL for waveform";
            case PI_EMPTY_WAVEFORM: return "attempt to create an empty waveform";
            case PI_NO_WAVEFORM_ID: return "no more waveforms";
            case PI_I2C_OPEN_FAILED: return "can't open I2C device";
            case PI_SER_OPEN_FAILED: return "can't open serial device";
            case PI_SPI_OPEN_FAILED: return "can't open SPI device";
            case PI_BAD_I2C_BUS: return "bad I2C bus";
            case PI_BAD_I2C_ADDR: return "bad I2C address";
            case PI_BAD_SPI_CHANNEL: return "bad SPI channel";
            case PI_BAD_FLAGS: return "bad i2c/spi/ser open flags";
            case PI_BAD_SPI_SPEED: return "bad SPI speed";
            case PI_BAD_SER_DEVICE: return "bad serial device name";
            case PI_BAD_SER_SPEED: return "bad serial baud rate";
            case PI_BAD_PARAM: return "bad i2c/spi/ser parameter";
            case PI_I2C_WRITE_FAILED: return "i2c write failed";
            case PI_I2C_READ_FAILED: return "i2c read failed";
            case PI_BAD_SPI_COUNT: return "bad SPI count";
            case PI_SER_WRITE_FAILED: return "ser write failed";
            case PI_SER_READ_FAILED: return "ser read failed";
            case PI_SER_READ_NO_DATA: return "ser read no data available";
            case PI_UNKNOWN_COMMAND: return "unknown command";
            case PI_SPI_XFER_FAILED: return "spi xfer/read/write failed";
            case PI_BAD_POINTER: return "bad (NULL) pointer";
            case PI_NO_AUX_SPI: return "no auxiliary SPI on Pi A or B";
            case PI_NOT_PWM_GPIO: return "GPIO is not in use for PWM";
            case PI_NOT_SERVO_GPIO: return "GPIO is not in use for servo pulses";
            case PI_NOT_HCLK_GPIO: return "GPIO has no hardware clock";
            case PI_NOT_HPWM_GPIO: return "GPIO has no hardware PWM";
            case PI_BAD_HPWM_FREQ: return "invalid hardware PWM frequency";
            case PI_BAD_HPWM_DUTY: return "hardware PWM dutycycle not 0-1M";
            case PI_BAD_HCLK_FREQ: return "invalid hardware clock frequency";
            case PI_BAD_HCLK_PASS: return "need password to use hardware clock 1";
            case PI_HPWM_ILLEGAL: return "illegal, PWM in use for main clock";
            case PI_BAD_DATABITS: return "serial data bits not 1-32";
            case PI_BAD_STOPBITS: return "serial (half) stop bits not 2-8";
            case PI_MSG_TOOBIG: return "socket/pipe message too big";
            case PI_BAD_MALLOC_MODE: return "bad memory allocation mode";
            case PI_TOO_MANY_SEGS: return "too many I2C transaction segments";
            case PI_BAD_I2C_SEG: return "an I2C transaction segment failed";
            case PI_BAD_SMBUS_CMD: return "SMBus command not supported by driver";
            case PI_NOT_I2C_GPIO: return "no bit bang I2C in progress on GPIO";
            case PI_BAD_I2C_WLEN: return "bad I2C write length";
            case PI_BAD_I2C_RLEN: return "bad I2C read length";
            case PI_BAD_I2C_CMD: return "bad I2C command";
            case PI_BAD_I2C_BAUD: return "bad I2C baud rate, not 50-500k";
            case PI_CHAIN_LOOP_CNT: return "bad chain loop count";
            case PI_BAD_CHAIN_LOOP: return "empty chain loop";
            case PI_CHAIN_COUNTER: return "too many chain counters";
            case PI_BAD_CHAIN_CMD: return "bad chain command";
            case PI_BAD_CHAIN_DELAY: return "bad chain delay micros";
            case PI_CHAIN_NESTING: return "chain counters nested too deeply";
            case PI_CHAIN_TOO_BIG: return "chain is too long";
            case PI_DEPRECATED: return "deprecated function removed";
            case PI_BAD_SER_INVERT: return "bit bang serial invert not 0 or 1";
            case PI_BAD_EDGE: return "bad ISR edge value, not 0-2";
            case PI_BAD_ISR_INIT: return "bad ISR initialisation";
            case PI_BAD_FOREVER: return "loop forever must be last command";
            case PI_BAD_FILTER: return "bad filter parameter";
            case PI_BAD_PAD: return "bad pad number";
            case PI_BAD_STRENGTH: return "bad pad drive strength";
            case PI_FIL_OPEN_FAILED: return "file open failed";
            case PI_BAD_FILE_MODE: return "bad file mode";
            case PI_BAD_FILE_FLAG: return "bad file flag";
            case PI_BAD_FILE_READ: return "bad file read";
            case PI_BAD_FILE_WRITE: return "bad file write";
            case PI_FILE_NOT_ROPEN: return "file not open for read";
            case PI_FILE_NOT_WOPEN: return "file not open for write";
            case PI_BAD_FILE_SEEK: return "bad file seek";
            case PI_NO_FILE_MATCH: return "no files match pattern";
            case PI_NO_FILE_ACCESS: return "no permission to access file";
            case PI_FILE_IS_A_DIR: return "file is a directory";
            case PI_BAD_SHELL_STATUS: return "bad shell return status";
            case PI_BAD_SCRIPT_NAME: return "bad script name";
            case PI_BAD_SPI_BAUD: return "bad SPI baud rate, not 50-500k";
            case PI_NOT_SPI_GPIO: return "no bit bang SPI in progress on GPIO";
            case PI_BAD_EVENT_ID: return "bad event id";
            case PI_CMD_INTERRUPTED: return "Used by Python";
            case PI_NOT_ON_BCM2711: return "not available on BCM2711";
            case PI_ONLY_ON_BCM2711: return "only available on BCM2711";

            case pigif_bad_send: return "bad send";
            case pigif_bad_recv: return "bad receive";
            case pigif_bad_getaddrinfo: return "bad getaddrinfo";
            case pigif_bad_connect: return "bad connect";
            case pigif_bad_socket: return "bad socket";
            case pigif_bad_noib: return "noib";
            case pigif_duplicate_callback: return "duplicate callback";
            case pigif_bad_malloc: return "bad malloc";
            case pigif_bad_callback: return "bad callback";
            case pigif_notify_failed: return "notify failed";
            case pigif_callback_not_found: return "callback not found";
            case pigif_unconnected_pi: return "unconnect to pi";
            case pigif_too_many_pis: return "too many pis";

            default: return "unknown error";
        }
    }

    /**
     * Get the JPigpio license.
     *
     * @return  license string
     */
    public String getLicense() {
        String license = "";

        try (InputStream is =
         getClass().getResourceAsStream("jpigpio_license.txt")) {
            if (is != null) {
                try (BufferedReader br =
                 new BufferedReader(new InputStreamReader(is))) {
                     license = br.lines().collect(joining("\n"));
                }
            } else {
                license = "License not found";
            }
        } catch (IOException ioe) {
            license =
             String.format("Error reading license: %s\n",ioe.toString());
        }

        return license;
    }
}

package com.knutejohnson.pi.jpigpio;

/**
 * Class to hold all the constants used by JPigpio.  This class should be
 * imported statically to any program using JPigpio to use them without their
 * full class name.
 *
 * @version 2020-12-26
 * @author  Knute Johnson
 */
public class JPigpioConstants {
    public static final int GPIO1 = 1;
    public static final int GPIO2 = 2;
    public static final int GPIO3 = 3;
    public static final int GPIO4 = 4;
    public static final int GPIO5 = 5;
    public static final int GPIO6 = 6;
    public static final int GPIO7 = 7;
    public static final int GPIO8 = 8;
    public static final int GPIO9 = 9;
    public static final int GPIO10 = 10;
    public static final int GPIO11 = 11;
    public static final int GPIO12 = 12;
    public static final int GPIO13 = 13;
    public static final int GPIO14 = 14;
    public static final int GPIO15 = 15;
    public static final int GPIO16 = 16;
    public static final int GPIO17 = 17;
    public static final int GPIO18 = 18;
    public static final int GPIO19 = 19;
    public static final int GPIO20 = 20;
    public static final int GPIO21 = 21;
    public static final int GPIO22 = 22;
    public static final int GPIO23 = 23;
    public static final int GPIO24 = 24;
    public static final int GPIO25 = 25;
    public static final int GPIO26 = 26;
    public static final int GPIO27 = 27;

    // gpio pin states
    public static final int PI_ON = 1;
    public static final int PI_OFF = 0;

    public static final int PI_SET = 1;
    public static final int PI_CLEAR = 0;

    public static final int PI_HIGH = 1;
    public static final int PI_LOW = 0;

    // pi commands
    public static final int PI_CMD_MODES = 0;
    public static final int PI_CMD_MODEG = 1;
    public static final int PI_CMD_PUD   = 2;
    public static final int PI_CMD_READ  = 3;
    public static final int PI_CMD_WRITE = 4;
    public static final int PI_CMD_PWM   = 5;
    public static final int PI_CMD_PRS   = 6;
    public static final int PI_CMD_PFS   = 7;
    public static final int PI_CMD_SERVO = 8;
    public static final int PI_CMD_WDOG  = 9;
    public static final int PI_CMD_BR1   = 10;
    public static final int PI_CMD_BR2   = 11;
    public static final int PI_CMD_BC1   = 12;
    public static final int PI_CMD_BC2   = 13;
    public static final int PI_CMD_BS1   = 14;
    public static final int PI_CMD_BS2   = 15;
    public static final int PI_CMD_TICK  = 16;
    public static final int PI_CMD_HWVER = 17;
    public static final int PI_CMD_NO    = 18;
    public static final int PI_CMD_NB    = 19;
    public static final int PI_CMD_NP    = 20;
    public static final int PI_CMD_NC    = 21;
    public static final int PI_CMD_PRG   = 22;
    public static final int PI_CMD_PFG   = 23;
    public static final int PI_CMD_PRRG  = 24;
    public static final int PI_CMD_HELP  = 25;
    public static final int PI_CMD_PIGPV = 26;
    public static final int PI_CMD_WVCLR = 27;
    public static final int PI_CMD_WVAG  = 28;
    public static final int PI_CMD_WVAS  = 29;
    public static final int PI_CMD_WVGO  = 30;
    public static final int PI_CMD_WVGOR = 31;
    public static final int PI_CMD_WVBSY = 32;
    public static final int PI_CMD_WVHLT = 33;
    public static final int PI_CMD_WVSM  = 34;
    public static final int PI_CMD_WVSP  = 35;
    public static final int PI_CMD_WVSC  = 36;
    public static final int PI_CMD_TRIG  = 37;
    public static final int PI_CMD_PROC  = 38;
    public static final int PI_CMD_PROCD = 39;
    public static final int PI_CMD_PROCR = 40;
    public static final int PI_CMD_PROCS = 41;
    public static final int PI_CMD_SLRO  = 42;
    public static final int PI_CMD_SLR   = 43;
    public static final int PI_CMD_SLRC  = 44;
    public static final int PI_CMD_PROCP = 45;
    public static final int PI_CMD_MICS  = 46;
    public static final int PI_CMD_MILS  = 47;
    public static final int PI_CMD_PARSE = 48;
    public static final int PI_CMD_WVCRE = 49;
    public static final int PI_CMD_WVDEL = 50;
    public static final int PI_CMD_WVTX  = 51;
    public static final int PI_CMD_WVTXR = 52;
    public static final int PI_CMD_WVNEW = 53;
    public static final int PI_CMD_I2CO  = 54;
    public static final int PI_CMD_I2CC  = 55;
    public static final int PI_CMD_I2CRD = 56;
    public static final int PI_CMD_I2CWD = 57;
    public static final int PI_CMD_I2CWQ = 58;
    public static final int PI_CMD_I2CRS = 59;
    public static final int PI_CMD_I2CWS = 60;
    public static final int PI_CMD_I2CRB = 61;
    public static final int PI_CMD_I2CWB = 62;
    public static final int PI_CMD_I2CRW = 63;
    public static final int PI_CMD_I2CWW = 64;
    public static final int PI_CMD_I2CRK = 65;
    public static final int PI_CMD_I2CWK = 66;
    public static final int PI_CMD_I2CRI = 67;
    public static final int PI_CMD_I2CWI = 68;
    public static final int PI_CMD_I2CPC = 69;
    public static final int PI_CMD_I2CPK = 70;
    public static final int PI_CMD_SPIO  = 71;
    public static final int PI_CMD_SPIC  = 72;
    public static final int PI_CMD_SPIR  = 73;
    public static final int PI_CMD_SPIW  = 74;
    public static final int PI_CMD_SPIX  = 75;
    public static final int PI_CMD_SERO  = 76;
    public static final int PI_CMD_SERC  = 77;
    public static final int PI_CMD_SERRB = 78;
    public static final int PI_CMD_SERWB = 79;
    public static final int PI_CMD_SERR  = 80;
    public static final int PI_CMD_SERW  = 81;
    public static final int PI_CMD_SERDA = 82;
    public static final int PI_CMD_GDC   = 83;
    public static final int PI_CMD_GPW   = 84;
    public static final int PI_CMD_HC    = 85;
    public static final int PI_CMD_HP    = 86;
    public static final int PI_CMD_CF1   = 87;
    public static final int PI_CMD_CF2   = 88;
    public static final int PI_CMD_BI2CC = 89;
    public static final int PI_CMD_BI2CO = 90;
    public static final int PI_CMD_BI2CZ = 91;
    public static final int PI_CMD_I2CZ  = 92;
    public static final int PI_CMD_WVCHA = 93;
    public static final int PI_CMD_SLRI  = 94;
    public static final int PI_CMD_CGI   = 95;
    public static final int PI_CMD_CSI   = 96;
    public static final int PI_CMD_FG    = 97;
    public static final int PI_CMD_FN    = 98;
    public static final int PI_CMD_NOIB  = 99;
    public static final int PI_CMD_WVTXM = 100;
    public static final int PI_CMD_WVTAT = 101;
    public static final int PI_CMD_PADS  = 102;
    public static final int PI_CMD_PADG  = 103;
    public static final int PI_CMD_FO    = 104;
    public static final int PI_CMD_FC    = 105;
    public static final int PI_CMD_FR    = 106;
    public static final int PI_CMD_FW    = 107;
    public static final int PI_CMD_FS    = 108;
    public static final int PI_CMD_FL    = 109;
    public static final int PI_CMD_SHELL = 110;
    public static final int PI_CMD_BSPIC = 111;
    public static final int PI_CMD_BSPIO = 112;
    public static final int PI_CMD_BSPIX = 113;
    public static final int PI_CMD_BSCX  = 114;
    public static final int PI_CMD_EVM   = 115;
    public static final int PI_CMD_EVT   = 116;
    public static final int PI_CMD_PROCU = 117;

    // GPIO modes
    public static final int PI_INPUT = 0;
    public static final int PI_OUTPUT = 1;
    public static final int PI_ALT0 = 4;
    public static final int PI_ALT1 = 5;
    public static final int PI_ALT2 = 6;
    public static final int PI_ALT3 = 7;
    public static final int PI_ALT4 = 3;
    public static final int PI_ALT5 = 2;

    // GPIO pull up/down
    public static final int PI_PUD_OFF = 0;
    public static final int PI_PUD_DOWN = 1;
    public static final int PI_PUD_UP = 2;

    // error codes
    public static final int PI_NO_ERROR         = 0;
    public static final int PI_INIT_FAILED      = -1;
    public static final int PI_BAD_USER_GPIO    = -2;
    public static final int PI_BAD_GPIO         = -3;
    public static final int PI_BAD_MODE         = -4;
    public static final int PI_BAD_LEVEL        = -5;
    public static final int PI_BAD_PUD          = -6;
    public static final int PI_BAD_PULSEWIDTH   = -7;
    public static final int PI_BAD_DUTYCYCLE    = -8;
    public static final int PI_BAD_TIMER        = -9;
    public static final int PI_BAD_MS           = -10;
    public static final int PI_BAD_TIMETYPE     = -11;
    public static final int PI_BAD_SECONDS      = -12;
    public static final int PI_BAD_MICROS       = -13;
    public static final int PI_TIMER_FAILED     = -14;
    public static final int PI_BAD_WDOG_TIMEOUT = -15;
    public static final int PI_NO_ALERT_FUNC    = -16;
    public static final int PI_BAD_CLK_PERIPH   = -17;
    public static final int PI_BAD_CLK_SOURCE   = -18;
    public static final int PI_BAD_CLK_MICROS   = -19;
    public static final int PI_BAD_BUF_MILLIS   = -20;
    public static final int PI_BAD_DUTYRANGE    = -21;
    public static final int PI_BAD_SIGNUM       = -22;
    public static final int PI_BAD_PATHNAME     = -23;
    public static final int PI_NO_HANDLE        = -24;
    public static final int PI_BAD_HANDLE       = -25;
    public static final int PI_BAD_IF_FLAGS     = -26;
    public static final int PI_BAD_CHANNEL      = -27;
//    public static final int PI_BAD_PRIM_CHANNEL = -27;
    public static final int PI_BAD_SOCKET_PORT  = -28;
    public static final int PI_BAD_FIFO_COMMAND = -29;
    public static final int PI_BAD_SECO_CHANNEL = -30;
    public static final int PI_NOT_INITIALISED  = -31;
    public static final int PI_INITIALISED      = -32;
    public static final int PI_BAD_WAVE_MODE    = -33;
    public static final int PI_BAD_CFG_INTERNAL = -34;
    public static final int PI_BAD_WAVE_BAUD    = -35;
    public static final int PI_TOO_MANY_PULSES  = -36;
    public static final int PI_TOO_MANY_CHARS   = -37;
    public static final int PI_NOT_SERIAL_GPIO  = -38;
    public static final int PI_BAD_SERIAL_STRUC = -39;
    public static final int PI_BAD_SERIAL_BUF   = -40;
    public static final int PI_NOT_PERMITTED    = -41;
    public static final int PI_SOME_PERMITTED   = -42;
    public static final int PI_BAD_WVSC_COMMND  = -43;
    public static final int PI_BAD_WVSM_COMMND  = -44;
    public static final int PI_BAD_WVSP_COMMND  = -45;
    public static final int PI_BAD_PULSELEN     = -46;
    public static final int PI_BAD_SCRIPT       = -47;
    public static final int PI_BAD_SCRIPT_ID    = -48;
    public static final int PI_BAD_SER_OFFSET   = -49;
    public static final int PI_GPIO_IN_USE      = -50;
    public static final int PI_BAD_SERIAL_COUNT = -51;
    public static final int PI_BAD_PARAM_NUM    = -52;
    public static final int PI_DUP_TAG          = -53;
    public static final int PI_TOO_MANY_TAGS    = -54;
    public static final int PI_BAD_SCRIPT_CMD   = -55;
    public static final int PI_BAD_VAR_NUM      = -56;
    public static final int PI_NO_SCRIPT_ROOM   = -57;
    public static final int PI_NO_MEMORY        = -58;
    public static final int PI_SOCK_READ_FAILED = -59;
    public static final int PI_SOCK_WRIT_FAILED = -60;
    public static final int PI_TOO_MANY_PARAM   = -61;
//    public static final int PI_NOT_HALTED       = -62;
    public static final int PI_SCRIPT_NOT_READY = -62;
    public static final int PI_BAD_TAG          = -63;
    public static final int PI_BAD_MICS_DELAY   = -64;
    public static final int PI_BAD_MILS_DELAY   = -65;
    public static final int PI_BAD_WAVE_ID      = -66;
    public static final int PI_TOO_MANY_CBS     = -67;
    public static final int PI_TOO_MANY_OOL     = -68;
    public static final int PI_EMPTY_WAVEFORM   = -69;
    public static final int PI_NO_WAVEFORM_ID   = -70;
    public static final int PI_I2C_OPEN_FAILED  = -71;
    public static final int PI_SER_OPEN_FAILED  = -72;
    public static final int PI_SPI_OPEN_FAILED  = -73;
    public static final int PI_BAD_I2C_BUS      = -74;
    public static final int PI_BAD_I2C_ADDR     = -75;
    public static final int PI_BAD_SPI_CHANNEL  = -76;
    public static final int PI_BAD_FLAGS        = -77;
    public static final int PI_BAD_SPI_SPEED    = -78;
    public static final int PI_BAD_SER_DEVICE   = -79;
    public static final int PI_BAD_SER_SPEED    = -80;
    public static final int PI_BAD_PARAM        = -81;
    public static final int PI_I2C_WRITE_FAILED = -82;
    public static final int PI_I2C_READ_FAILED  = -83;
    public static final int PI_BAD_SPI_COUNT    = -84;
    public static final int PI_SER_WRITE_FAILED = -85;
    public static final int PI_SER_READ_FAILED  = -86;
    public static final int PI_SER_READ_NO_DATA = -87;
    public static final int PI_UNKNOWN_COMMAND  = -88;
    public static final int PI_SPI_XFER_FAILED  = -89;
    public static final int PI_BAD_POINTER      = -90;
    public static final int PI_NO_AUX_SPI       = -91;
    public static final int PI_NOT_PWM_GPIO     = -92;
    public static final int PI_NOT_SERVO_GPIO   = -93;
    public static final int PI_NOT_HCLK_GPIO    = -94;
    public static final int PI_NOT_HPWM_GPIO    = -95;
    public static final int PI_BAD_HPWM_FREQ    = -96;
    public static final int PI_BAD_HPWM_DUTY    = -97;
    public static final int PI_BAD_HCLK_FREQ    = -98;
    public static final int PI_BAD_HCLK_PASS    = -99;
    public static final int PI_HPWM_ILLEGAL     = -100;
    public static final int PI_BAD_DATABITS     = -101;
    public static final int PI_BAD_STOPBITS     = -102;
    public static final int PI_MSG_TOOBIG       = -103;
    public static final int PI_BAD_MALLOC_MODE  = -104;
    public static final int PI_TOO_MANY_SEGS    = -105;
    public static final int PI_BAD_I2C_SEG      = -106;
    public static final int PI_BAD_SMBUS_CMD    = -107;
    public static final int PI_NOT_I2C_GPIO     = -108;
    public static final int PI_BAD_I2C_WLEN     = -109;
    public static final int PI_BAD_I2C_RLEN     = -110;
    public static final int PI_BAD_I2C_CMD      = -111;
    public static final int PI_BAD_I2C_BAUD     = -112;
    public static final int PI_CHAIN_LOOP_CNT   = -113;
    public static final int PI_BAD_CHAIN_LOOP   = -114;
    public static final int PI_CHAIN_COUNTER    = -115;
    public static final int PI_BAD_CHAIN_CMD    = -116;
    public static final int PI_BAD_CHAIN_DELAY  = -117;
    public static final int PI_CHAIN_NESTING    = -118;
    public static final int PI_CHAIN_TOO_BIG    = -119;
    public static final int PI_DEPRECATED       = -120;
    public static final int PI_BAD_SER_INVERT   = -121;
    public static final int PI_BAD_EDGE         = -122;
    public static final int PI_BAD_ISR_INIT     = -123;
    public static final int PI_BAD_FOREVER      = -124;
    public static final int PI_BAD_FILTER       = -125;
    public static final int PI_BAD_PAD          = -126;
    public static final int PI_BAD_STRENGTH     = -127;
    public static final int PI_FIL_OPEN_FAILED  = -128;
    public static final int PI_BAD_FILE_MODE    = -129;
    public static final int PI_BAD_FILE_FLAG    = -130;
    public static final int PI_BAD_FILE_READ    = -131;
    public static final int PI_BAD_FILE_WRITE   = -132;
    public static final int PI_FILE_NOT_ROPEN   = -133;
    public static final int PI_FILE_NOT_WOPEN   = -134;
    public static final int PI_BAD_FILE_SEEK    = -135;
    public static final int PI_NO_FILE_MATCH    = -136;
    public static final int PI_NO_FILE_ACCESS   = -137;
    public static final int PI_FILE_IS_A_DIR    = -138;
    public static final int PI_BAD_SHELL_STATUS = -139;
    public static final int PI_BAD_SCRIPT_NAME  = -140;
    public static final int PI_BAD_SPI_BAUD     = -141;
    public static final int PI_NOT_SPI_GPIO     = -142;
    public static final int PI_BAD_EVENT_ID     = -143;
    public static final int PI_CMD_INTERRUPTED  = -144;
    public static final int PI_NOT_ON_BCM2711   = -145;
    public static final int PI_ONLY_ON_BCM2711  = -146;

    public static final int pigif_bad_send           = -2000;
    public static final int pigif_bad_recv           = -2001;
    public static final int pigif_bad_getaddrinfo    = -2002;
    public static final int pigif_bad_connect        = -2003;
    public static final int pigif_bad_socket         = -2004;
    public static final int pigif_bad_noib           = -2005;
    public static final int pigif_duplicate_callback = -2006;
    public static final int pigif_bad_malloc         = -2007;
    public static final int pigif_bad_callback       = -2008;
    public static final int pigif_notify_failed      = -2009;
    public static final int pigif_callback_not_found = -2010;
    public static final int pigif_unconnected_pi     = -2011;
    public static final int pigif_too_many_pis       = -2012;
}

package com.knutejohnson.pi.jpigpio;

/**
 * Class to hold data returned from a notify action.
 *
 * @version 2021-01-22
 * @author  Knute Johnson
 */
public class GPIOReport {
    /** Sequence number */
    public int seqno;

    /** Flags */
    public int flags;

    /** Time of the event in ticks/microseconds of boot */
    public long tick;

    /** Level of the GPIO pin that triggered the notification */
    public long level;

    /**
     * Create a new GPIOReport object.
     *
     * @param   seqno   sequence number
     * @param   flags   flags
     * @param   tick    tick count
     * @param   level   GPIO pin level
     */
    public GPIOReport(int seqno, int flags, long tick, long level) {
        this.seqno = seqno;
        this.flags = flags;
        this.tick = tick;
        this.level = level;
    }

    /**
     * Create a String representation of the GPIOReport
     *
     * @return  String containing fields of report
     */
    public String toString() {
        return String.format("seqno: %d - flags: %s - tick: %d - level: %s",
         seqno,Integer.toUnsignedString(flags,2),tick,
         Long.toUnsignedString(level,2));
    }
}