#include <windows.h>
#include <shellapi.h>
#include "tray.h"

#define WM_TRAY_CALLBACK_MESSAGE (WM_USER + 1)
#define WC_TRAY_CLASS_NAME "TRAY"
#define ID_TRAY_FIRST 1000

static WNDCLASSEX wc;
static NOTIFYICONDATA nid;
static HWND hwnd;
static HMENU hmenu = NULL;
static struct tray *g_tray = NULL;
static int g_icon_is_shared = 0;

static LRESULT CALLBACK _tray_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam,
                                       LPARAM lparam) {
  switch (msg) {
  case WM_CLOSE:
    DestroyWindow(hwnd);
    return 0;
  case WM_DESTROY:
    PostQuitMessage(0);
    return 0;
  case WM_TRAY_CALLBACK_MESSAGE:
    if (lparam == WM_LBUTTONUP) {
      if (g_tray && g_tray->left_click_cb) {
        g_tray->left_click_cb(g_tray->left_click_context);
      }
      return 0;
    }
    if (lparam == WM_RBUTTONUP) {
      POINT p;
      GetCursorPos(&p);
      SetForegroundWindow(hwnd);
      WORD cmd = TrackPopupMenu(hmenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON |
                                           TPM_RETURNCMD | TPM_NONOTIFY,
                                p.x, p.y, 0, hwnd, NULL);
      SendMessage(hwnd, WM_COMMAND, cmd, 0);
      return 0;
    }
    break;
  case WM_COMMAND:
    if (wparam >= ID_TRAY_FIRST) {
      MENUITEMINFO item = {
          .cbSize = sizeof(MENUITEMINFO), .fMask = MIIM_ID | MIIM_DATA,
      };
      if (GetMenuItemInfo(hmenu, wparam, FALSE, &item)) {
        struct tray_menu *menu = (struct tray_menu *)item.dwItemData;
        if (menu != NULL && menu->cb != NULL) {
          menu->cb(menu);
        }
      }
      return 0;
    }
    break;
  }
  return DefWindowProc(hwnd, msg, wparam, lparam);
}

static HMENU _tray_menu(struct tray_menu *m, UINT *id) {
  HMENU hmenu = CreatePopupMenu();
  for (; m != NULL && m->text != NULL; m++, (*id)++) {
    if (strcmp(m->text, "-") == 0) {
      InsertMenuW(hmenu, *id, MF_SEPARATOR, TRUE, L"");
    } else {
      // UTF-8 zu UTF-16 (WCHAR) konvertieren
      int len = MultiByteToWideChar(CP_UTF8, 0, m->text, -1, NULL, 0);
      if (len > 0) {
        wchar_t *wtext = (wchar_t*)malloc(len * sizeof(wchar_t));
        if (wtext) {
          MultiByteToWideChar(CP_UTF8, 0, m->text, -1, wtext, len);

          MENUITEMINFOW item;
          memset(&item, 0, sizeof(item));
          item.cbSize = sizeof(MENUITEMINFOW);
          item.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE | MIIM_DATA;
          item.fType = 0;
          item.fState = 0;
          if (m->submenu != NULL) {
            item.fMask = item.fMask | MIIM_SUBMENU;
            item.hSubMenu = _tray_menu(m->submenu, id);
          }
          if (m->disabled) {
            item.fState |= MFS_DISABLED;
          }
          if (m->checked) {
            item.fState |= MFS_CHECKED;
          }
          item.wID = *id;
          item.dwTypeData = wtext;
          item.dwItemData = (ULONG_PTR)m;

          InsertMenuItemW(hmenu, *id, TRUE, &item);
          free(wtext);
        }
      }
    }
  }
  return hmenu;
}

int tray_init(struct tray *tray) {
  memset(&wc, 0, sizeof(wc));
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.lpfnWndProc = _tray_wnd_proc;
  wc.hInstance = GetModuleHandle(NULL);
  wc.lpszClassName = WC_TRAY_CLASS_NAME;
  if (!RegisterClassEx(&wc)) {
    return -1;
  }

  hwnd = CreateWindowEx(0, WC_TRAY_CLASS_NAME, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0);
  if (hwnd == NULL) {
    return -1;
  }
  UpdateWindow(hwnd);

  memset(&nid, 0, sizeof(nid));
  nid.cbSize = sizeof(NOTIFYICONDATA);
  nid.hWnd = hwnd;
  nid.uID = 0;
  nid.uFlags = NIF_ICON | NIF_MESSAGE;
  nid.uCallbackMessage = WM_TRAY_CALLBACK_MESSAGE;
  Shell_NotifyIcon(NIM_ADD, &nid);

  g_tray = tray;
  tray_update(tray);
  return 0;
}

int tray_loop(int blocking) {
  MSG msg;
  if (blocking) {
    GetMessage(&msg, hwnd, 0, 0);
  } else {
    PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE);
  }
  if (msg.message == WM_QUIT) {
    return -1;
  }
  TranslateMessage(&msg);
  DispatchMessage(&msg);
  return 0;
}

void tray_update(struct tray *tray) {
  g_tray = tray;
  HMENU prevmenu = hmenu;
  UINT id = ID_TRAY_FIRST;
  hmenu = _tray_menu(tray->menu, &id);
  SendMessage(hwnd, WM_INITMENUPOPUP, (WPARAM)hmenu, 0);
  HICON icon = NULL;
  int icon_is_shared = 0;
  if (tray->icon_handle) {
    icon = (HICON)tray->icon_handle;
    icon_is_shared = tray->icon_is_shared;
  } else if (tray->icon) {
    ExtractIconEx(tray->icon, 0, NULL, &icon, 1);
  }
  if (nid.hIcon && !g_icon_is_shared) {
    DestroyIcon(nid.hIcon);
  }
  if (icon) {
    nid.hIcon = icon;
    g_icon_is_shared = icon_is_shared;
  }
  if(tray->tooltip != 0 && strlen(tray->tooltip) > 0) {
    MultiByteToWideChar(CP_UTF8, 0, tray->tooltip, -1, nid.szTip, sizeof(nid.szTip)/sizeof(wchar_t));
    nid.uFlags |= NIF_TIP;
  }
  Shell_NotifyIcon(NIM_MODIFY, &nid);

  if (prevmenu != NULL) {
    DestroyMenu(prevmenu);
  }
}

void tray_exit(void) {
  Shell_NotifyIcon(NIM_DELETE, &nid);
  if (nid.hIcon != 0 && !g_icon_is_shared) {
    DestroyIcon(nid.hIcon);
  }
  if (hmenu != 0) {
    DestroyMenu(hmenu);
  }
  PostQuitMessage(0);
  UnregisterClass(WC_TRAY_CLASS_NAME, GetModuleHandle(NULL));
}

