/**
 * Looking Glass
 * Copyright © 2017-2026 The Looking Glass Authors
 * https://looking-glass.io
 *
 * 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.
 *
 * 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, write to the Free Software Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include <stdbool.h>
#include <stdatomic.h>
#include <linux/input.h>

#include "dynamic/displayservers.h"
#include "dynamic/renderers.h"
#include "dynamic/audiodev.h"

#include "common/thread.h"
#include "common/types.h"
#include "common/locking.h"
#include "common/ringbuffer.h"
#include "common/event.h"
#include "common/ll.h"

#include "cimgui.h"
#include "app.h"
#include "interface/transport.h"
#include "transport_fallback.h"

enum MicDefaultState {
  MIC_DEFAULT_PROMPT,
  MIC_DEFAULT_ALLOW,
  MIC_DEFAULT_DENY
};
#define MIC_DEFAULT_MAX (MIC_DEFAULT_DENY + 1)

enum AudioResampler {
  AUDIO_RESAMPLER_AUTO,
  AUDIO_RESAMPLER_LIBSAMPLERATE,
  AUDIO_RESAMPLER_BACKEND
};

struct VideoSourceState
{
  atomic_uint_least64_t generation;
  atomic_uint_least64_t transitionSerial;
  atomic_bool           transitionPending;
  atomic_uint_least32_t width;
  atomic_uint_least32_t height;
  atomic_int            rotate;
  atomic_uint_least32_t appliedWidth;
  atomic_uint_least32_t appliedHeight;
  atomic_int            appliedRotate;
  atomic_bool           ready;
  bool                  swSurface;
  bool                  configurePending;

  bool     cursorStateValid;
  bool     cursorVisible;
  int      cursorX;
  int      cursorY;
  int      cursorHX;
  int      cursorHY;
  uint32_t cursorWhiteLevel;
};

struct AppState
{
  ImGuiIO        * io;
  ImGuiStyle     * style;
  struct ll      * overlays;
  char           * fontName;
  ImFont         * fontLarge;
  bool             overlayInput;
  ImGuiMouseCursor cursorLast;
  char           * imGuiIni;
  bool             modCtrl;
  bool             modShift;
  bool             modAlt;
  bool             modSuper;
  uint64_t         lastImGuiFrame;
  bool             renderImGuiTwice;
  bool             exclusiveEvdev;

  struct LG_DisplayServerOps * ds;
  bool                         dsInitialized;
  bool                         jitRender;

  struct VideoSourceState videoSource[LG_VIDEO_SOURCE_COUNT];
  atomic_int               videoSourceRequested;
  atomic_int               videoSourceApplied;
  atomic_uint_least64_t     videoSourceAppliedGeneration;
  atomic_bool              videoStatusActive;
  atomic_bool              videoStatusKnown;
  atomic_bool              videoFrameAvailable;
  atomic_bool              videoPointerAvailable;
  atomic_uint_least64_t     videoFrameEpoch;
  atomic_uint_least64_t     videoPointerEpoch;
  atomic_int                videoFrameReason;
  atomic_int                videoPointerReason;
  LG_Lock                   videoPayloadLock;
  LG_Lock                   videoSourceLock;
  LG_Lock                   videoSplashLock;
  bool                      videoGeometryDirty;

  atomic_bool fallbackEndpointMismatch;
  atomic_uint transportLost;
  atomic_bool forceRecovery;

  uint8_t guestUUID[16];
  bool    guestUUIDValid;
  LG_TransportGuestOS guestOS;

  atomic_bool lgHostConnected;

  atomic_bool          stopVideo;
  atomic_bool          stopVideoThreads;
  bool                 ignoreInput;
  bool                 escapeActive;
  uint64_t             escapeTime;
  int                  escapeAction;
  bool                 escapeKeys[KEY_MAX];
  bool                 escapeShiftKeys[KEY_MAX];
  atomic_uint          keyModifiers;
  bool                 escapeHelp;
  struct ll          * bindings;
  bool                 haveSrcSize;
  struct Point         windowPos;
  int                  windowW, windowH;
  int                  windowCX, windowCY;
  double               windowScale;
  double               fontScale;
  LG_RendererRotate    rotate;
  bool                 focused;
  struct Border        border;
  struct Point         srcSize;
  LG_RendererRect      dstRect;
  bool                 posInfoValid;
  bool                 alignToGuest;
  atomic_uint_least64_t shaderMousePosition;
  atomic_uint_least32_t shaderMouseState;

  LG_Renderer        * lgr;
  atomic_int           lgrResize;
  atomic_bool          fontDirty;
  LG_Lock              lgrLock;
  bool                 useDMA;

  LG_TransportInstance      transport;
  const LG_VideoOps       * videoOps;
  LG_TransportFeatureFlags  transportFeatures;

  LG_TransportFallback * fallback;

  LGThread            * cursorThread;
  LGThread            * frameThread;
  LGEvent             * frameEvent;
  atomic_bool           invalidateWindow;
  bool                  formatValid;
  uint64_t              frameTime;
  uint64_t              overlayFrameTime;
  uint64_t              lastRenderTime;
  bool                  lastRenderTimeValid;
  RingBuffer            renderTimings;
  RingBuffer            frameLatency;
  uint64_t              frameImportTime;
  uint64_t              frameImportWaitTime;

  atomic_uint_least64_t pendingCount;
  atomic_uint_least64_t renderCount, frameCount;
  _Atomic(float)        fps, ups;

  uint64_t resizeTimeout;
  bool     resizeDone;

  bool     autoIdleInhibitState;

  enum MicDefaultState micDefaultState;

  // Set to true when setHDRImageDesc() returns false, indicating
  // the display server could not apply the HDR image description.
  // Checked by the renderer to fall back to software tone-mapping.
  _Atomic(bool) hdrDescFailed;
};

struct AppParams
{
  bool                 autoResize;
  bool                 allowResize;
  bool                 keepAspect;
  bool                 forceAspect;
  bool                 dontUpscale;
  bool                 intUpscale;
  bool                 shrinkOnUpscale;
  bool                 borderless;
  bool                 fullscreen;
  bool                 maximize;
  bool                 minimizeOnFocusLoss;
  bool                 center;
  int                  x, y;
  unsigned int         w, h;
  bool                 setGuestRes;
  int                  fpsMin;
  LG_RendererRotate    winRotate;
  bool                 clipboardToVM;
  bool                 clipboardToLocal;
  bool                 scaleMouseInput;
  bool                 hideMouse;
  bool                 ignoreQuit;
  bool                 noScreensaver;
  bool                 autoScreensaver;
  bool                 captureOnFocus;
  bool                 grabKeyboard;
  bool                 grabKeyboardOnFocus;
  int                  escapeKey;
  bool                 ignoreWindowsKeys;
  bool                 releaseKeysOnFocusLoss;
  bool                 showAlerts;
  bool                 captureOnStart;
  bool                 quickSplash;
  bool                 overlayDim;
  bool                 alwaysShowCursor;
  uint64_t             helpMenuDelayUs;
  const char *         uiFont;
  int                  uiSize;
  bool                 jitRender;
  bool                 requestActivation;
  bool                 disableWaitingMessage;

  const char         * transport;

  bool                 forceRenderer;
  unsigned int         forceRendererIndex;

  const char *         windowTitle;
  const char *         appId;
  bool                 mouseRedraw;
  bool                 mouseTrace;
  int                  mouseSens;
  bool                 mouseSmoothing;
  bool                 rawMouse;
  bool                 autoCapture;
  bool                 captureInputOnly;
  bool                 showCursorDot;
  bool                 largeCursorDot;

  bool                 audioDebug;
  int                  audioPeriodSize;
  int                  audioLatencyOffset;
  enum AudioResampler  audioResampler;
  bool                 micShowIndicator;
  enum MicDefaultState micDefaultState;
  bool                 audioSyncVolume;
};

struct KeybindHandle
{
  int          sc;
  KeybindFn    callback;
  KeybindFn    shiftCallback;
  const char * description;
  const char * shiftDescription;
  void *       opaque;
  void *       shiftOpaque;
};

enum WarpState
{
  WARP_STATE_ON,
  WARP_STATE_OFF
};

struct CursorInfo
{
  /* x & y postiion */
  int  x , y;

  /* pointer hotspot offsets */
  int  hx, hy;

  /* true if the pointer is visible on the guest */
  bool visible;

  /* true if the details in this struct are valid */
  bool valid;
};

struct CursorState
{
  /* cursor is in grab mode */
  bool grab;

  /* true if we are to draw the cursor on screen */
  bool draw;

  /* true if the cursor is currently in our window */
  bool inWindow;

  /* true if the cursor is currently in the guest view area */
  bool inView;

  /* true if the cursor should be confined to the guest view area */
  bool viewReq;

  /* true if a pointer exit is waiting for confinement to release */
  bool exit;

  /* true if a Wayland surface exit is waiting for a real leave */
  bool surfaceExit;

  /* the local pointer exit target */
  struct DoublePoint exitPos;

  /* true if the guest should be realigned to the host when next drawn */
  bool realign;

  /* true if the guest is currently realigning to the host */
  bool realigning;

  /* true if the cursor needs re-drawing/updating */
  atomic_bool redraw;

  /* true if the cursor movements should be scaled */
  bool useScale;

  /* the amount to scale the X & Y movements by */
  struct DoublePoint scale;

  /* the error accumulator */
  struct DoublePoint acc;

  /* the local position */
  struct DoublePoint pos;

  /* true if the position is valid */
  bool valid;

  /* true if the last local position can be used to predict motion */
  bool motionValid;

  /* true if auto capture currently requests the keyboard */
  bool autoCaptureActive;

  /* the button state */
  unsigned int buttons;

  /* the scale factor for the mouse sensitiviy */
  int sens;

  /* the mouse warp state */
  enum WarpState warpState;

  /* the guest's cursor position */
  struct CursorInfo guest;
};

// forwards
extern struct AppState    g_state;
extern struct CursorState g_cursor;
extern struct AppParams   g_params;

int main_cursorThread(void * unused);
int main_frameThread(void * unused);
void main_videoStreamEnabled(void);
void main_framePresented(uint64_t frameToken, uint64_t presentTime,
    bool valid);

#define RENDERER(fn, ...) g_state.lgr->ops.fn(g_state.lgr, ##__VA_ARGS__)
