
// SPDX-License-Identifier: CC-BY-NC-SA-4.0
//
// Copyright (C) 2026 Bit by Bit Signal Processing LLC (https://bxbsp.com)
//
// This work is placed under the "Creative Commons Attribution
// NonCommercial ShareAlike 4.0 International" license, known
// by the shortened acronym "CC-BY-NC-SA-4.0".
//
// This work 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.
//
// A CC-BY-NC-SA-4.0 license allows you to use this work for
// noncommercial purposes so long as attribution is made to the
// original author.  Modified versions of this work may be distributed,
// but only under the same license.  For further details, see the
// Creative Commons License "CC-BY-NC-SA-4.0".
//
// You should have received a copy of the CC-BY-NC-SA-4.0 license
// along with this work. If not, see
// <https://creativecommons.org/licenses/by-nc-sa/4.0/>.
//

#ifndef INPUT_EVENT_HANDLER_HH
#define INPUT_EVENT_HANDLER_HH

#include <stdlib.h>
#include <linux/input.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <poll.h>
#include "my_event.hh"
#include "runnable.hh"
#include "synchronized_data.hh"
#include "touch_state.hh"
#include "my_event_queue.hh"

class input_event_handler : runnable
{
  int maxx;
  int maxy;
  int current_listener;
  
  const static int MAX_LISTENERS = 100;

  int num_listeners;
  class input_event_listener* listeners[MAX_LISTENERS];

  void scan_for_input_devices();
  
 public:

  synchronized_data<my_event_queue> out;
  class display* disp;

  input_event_handler(int maxx, int maxy, display* disp);
  ~input_event_handler();

  void set_verbose();

  virtual void run();
  
  my_event wait_for_event();  // Will sleep until some event happens
  my_event check_for_event(); // Returns immediately; event may be EVENT_NONE
};



enum motion_state
  {
    MOTION_STATE_NONE,     // Not handling motion, waiting a touch
    MOTION_STATE_INIT,     // Got a touch, waiting 100ms to make sure another finger doesn't come down
    MOTION_STATE_HOLDING,  // Have decided how many fingers, but no significant motion yet.  Send hold time events, flush minor motion.
    MOTION_STATE_MOVING,   // Got a stable touch, moved away from it, processing move events
    MOTION_STATE_ERROR,    // A finger was released on a multitouch event; sent a release to client, awaiting one from device, flushing all motion
  };

enum key_state
  {
    KEY_STATE_NONE,    // Awaiting a press
    KEY_STATE_PRESSED  // Awaiting a release, sending hold time events, flushing all other key presses
  };


//
// One listener per input device.  The input_event_handler brings them all together
// into a single event stream for the application.
//
class input_event_listener : runnable
{
  input_event_handler*  ieh;

  touch_state           touch_state_handler;

  const static int      wait_for_fingers_us  = 100000;
  const static int      motion_to_break_hold = 10;

  int                   fd;

  int                   touchscreen_to_screen_x_offset;
  int                   touchscreen_to_screen_x_numerator;
  int                   touchscreen_to_screen_x_denominator;

  int                   touchscreen_to_screen_y_offset;
  int                   touchscreen_to_screen_y_numerator;
  int                   touchscreen_to_screen_y_denominator;

  enum motion_state     old_motion_touch_state;
  enum motion_state     motion_touch_state;
  long long             motion_event_start_us;
  my_event              motion_starting_position;
  
  bool                  touchscreen;
  bool                  mouse;
  my_event              me;
  my_event              me_at_touch;

  enum key_state        key_press_state;
  bool                  key_press_event;       // got a key press
  bool                  key_release_event;     // got a key release
  long long             key_event_start_us;
  int                   key_press_count;
  
  int                   current_slot;
  
  const static int      MAX_SLOTS = 32;
  int                   touch_for_each_slot[MAX_SLOTS];
  int                   tracking_id_for_each_slot[MAX_SLOTS];

  int                   num_touches;
  
  static const char *ev_type[EV_CNT];
  static const char *ev_code_syn[SYN_CNT];
  static const char *ev_code_abs[ABS_CNT];
  static const char *ev_code_rel[REL_CNT];
  static const char *ev_code_key[KEY_CNT];
  static const char *ev_code_msc[MSC_CNT];

  void init();
  void handle_touchscreen_motion(input_event& ie, long long t_us);
  void handle_mouse_motion(input_event& ie, long long t_us);
  
public:

  char* filename;

  bool verbose;

  void print_event(struct input_event& ie);
  void send_event(my_event& me);
  
  input_event_listener(const char* devname, int screen_width, int screen_height, input_event_handler* ieh);
  ~input_event_listener();

  virtual void run();
  
  my_event& get_event();
};



#endif
