#include <X11/Xlib.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/Scrollbar.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Cardinals.h>

#include "clap/clap.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define GUI_WIDTH 300
#define GUI_HEIGHT 360

enum Parameter { onOff, drive, gain, COUNT };

struct MyPlugin {
  clap_plugin_t plugin;
  double parameter[Parameter::COUNT] = { 1.0, 0.5, 0.5 };
  Widget top_widget;
  Widget sliders[3];
  Widget values[3];
};

static void update_value_label(MyPlugin* plugin, int param, double value) {
  char buffer[16];
  switch (param) {
  case onOff:
    snprintf(buffer, sizeof(buffer), value > 0.5 ? "On" : "Off");
    break;
  default:
    snprintf(buffer, sizeof(buffer), "%.2f", value);
    break;
  }
  XtVaSetValues(plugin->values[param], XtNlabel, buffer, NULL);
}

static void slider_callback(Widget widget, XtPointer client_data, XtPointer call_data) {
  MyPlugin* plugin = (MyPlugin*)client_data;

  for (int i = 0; i < 3; ++i) {
    if (plugin->sliders[i] == widget) {
      float fraction;
      XtVaGetValues(widget, XtNtopOfThumb, &fraction, NULL);
      double value = fraction;
      if (i == onOff)
        value = value > 0.5 ? 1.0 : 0.0;

      plugin->parameter[i] = value;
      update_value_label(plugin, i, value);
    }
  }
}

static void create_gui(MyPlugin* plugin, Display* display) {
  Widget toplevel = XtVaAppInitialize(NULL, "ClapGui", NULL, 0,
    NULL, NULL, NULL, NULL);

  Widget form = XtVaCreateManagedWidget("form", formWidgetClass, toplevel, NULL);
  const char* labels[] = { "On/Off", "Drive", "Gain" };

  for (int i = 0; i < 3; ++i) {
    plugin->values[i] = XtVaCreateManagedWidget("label", labelWidgetClass, form,
      XtNlabel, "0.00",
      XtNfromVert, i == 0 ? NULL : plugin->sliders[i - 1],
      XtNleft, XawChainLeft,
      XtNright, XawChainRight,
      NULL);

    plugin->sliders[i] = XtVaCreateManagedWidget("slider", scrollbarWidgetClass, form,
      XtNorientation, XtorientHorizontal,
      XtNlength, 200,
      XtNfromVert, plugin->values[i],
      XtNtop, XawChainTop,
      XtNbottom, XawChainTop,
      XtNleft, XawChainLeft,
      XtNright, XawChainRight,
      NULL);

    XtAddCallback(plugin->sliders[i], XtNscrollProc, slider_callback, plugin);
    XtAddCallback(plugin->sliders[i], XtNjumpProc, slider_callback, plugin);

    update_value_label(plugin, i, plugin->parameter[i]);
  }

  plugin->top_widget = toplevel;
}

static bool plugin_gui__create(const clap_plugin_t* plugin, const char* api, bool is_floating) {
  if (strcmp(api, CLAP_WINDOW_API_X11) != 0 || is_floating)
    return false;

  MyPlugin* my_plugin = (MyPlugin*)plugin->plugin_data;
  Display* display = XOpenDisplay(NULL);
  if (!display)
    return false;

  create_gui(my_plugin, display);
  return true;
}

static bool plugin_gui__set_parent(const clap_plugin_t* plugin, const clap_window_t* window) {
  // Not really applicable in simple X11 - embedding requires reparenting
  return true;
}

static bool plugin_gui__show(const clap_plugin_t* plugin) {
  MyPlugin* my_plugin = (MyPlugin*)plugin->plugin_data;
  XtRealizeWidget(my_plugin->top_widget);
  XtAppMainLoop(XtWidgetToApplicationContext(my_plugin->top_widget));
  return true;
}

static bool plugin_gui__hide(const clap_plugin_t* plugin) {
  MyPlugin* my_plugin = (MyPlugin*)plugin->plugin_data;
  XtUnrealizeWidget(my_plugin->top_widget);
  return true;
}

// Other plugin_gui__* functions would be implemented similarly.

