/*****************************************************************************
 * dwm-msg-writer
 *
 * This program writes, each 10 seconds, the current time and
 * battery status in the user dwm-wrapper pipe.
 *
 * ----------------------------------------------------------------------------
 * DWM Scripts - wrapper for dwm/dmenu/slock
 *   (C) 2007-2009 Gerardo García Peña
 *   Programmed by Gerardo García Peña
 *
 *   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., 51 Franklin Street,
 *   Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include <X11/Xlib.h>
#include <X11/XKBlib.h>

#ifdef HAVE_ACPI
#include <libacpi.h>
#endif

/* X-Window global vars */
Display *dpy;
Window rootwin;

#ifdef HAVE_ACPI
/* acpi global vars */
global_t *global;
#endif

/* finalization routine */
void killemall(void)
{
  XCloseDisplay(dpy);
#ifdef HAVE_ACPI
  if(global)
    free(global);
#endif
  exit(0);
}

/* initialize and check x keyboard extensions */
int init_xkb_extension(int *xkbev, int *xkberr)
{
  int code;
  int maj = XkbMajorVersion;
  int min = XkbMinorVersion;
    
  if(!XkbLibraryVersion(&maj, &min))
    return 0;
  if(!XkbQueryExtension(dpy, &code, xkbev, xkberr, &maj, &min))
    return 0;

  return -1;
}

/* main program (and loop) */
int main(int argc, char **argv)
{
  char s[1024];
  char batstat[1024];
  int r;
#ifdef HAVE_ACPI
  int i;
  int acpi_enabled;
  int rem_time,
      on_ac;
  int rem_percent, rem_percent_bcount;
#endif
  struct tm ts;
  time_t t;
  int screen;
  int xkb_ext = -1;
  int xkbev, xkberr;

  /* install exit() handler */
  if(atexit(killemall))
  {
    fprintf(stderr, "Cannot install exit handler.\n");
    exit(1);
  }

  /* Get X11 root window and screen */
  if(!(dpy = XOpenDisplay(0)))
  {
    fprintf(stderr, "Cannot open display\n");
    exit(1);
  }
  screen = DefaultScreen(dpy);
  rootwin = RootWindow(dpy, screen);

  /* Initialize ACPI */
#ifdef HAVE_ACPI
  acpi_enabled = 0;
  if((global = malloc(sizeof(global_t))) == NULL)
    fprintf(stderr, "No mem for global acpi structure.\n");
  else
  if(check_acpi_support() != SUCCESS)
    fprintf(stderr, "No acpi support.\n");
  else
  if(init_acpi_batt(global) != SUCCESS)
    fprintf(stderr, "warning: Cannot initialize battery structures.\n");
  else
  if(init_acpi_acadapt(global) != SUCCESS)
    fprintf(stderr, "Cannot initialize acadapt structures.\n");
  else
    acpi_enabled = -1;
#endif

  /* keyboard sexy leds initialization (this sentence has no sense) */
  if (!init_xkb_extension(&xkbev, &xkberr))
  {
    fprintf(stderr, "warning: no sexy kbd extension found.\n");
    xkb_ext = 0;
  } else {
    if(!XkbSelectEvents(dpy, XkbUseCoreKbd, XkbIndicatorStateNotifyMask, XkbIndicatorStateNotifyMask))
    {
      fprintf(stderr, "warning: no sexy kbd event filter.\n");
      xkb_ext = 0;
    }
  }

  /* main loop */
  while(1)
  {
    /* read ac/battery state */
    *batstat = '\0';
#ifdef HAVE_ACPI
    if(acpi_enabled)
    {
      /* get ac/battery state */
      read_acpi_acstate(global);
      on_ac = global->adapt.ac_state == P_AC;
      rem_time           = 0;
      rem_percent        = 0;
      rem_percent_bcount = 0;
      for(i = 0; i < global->batt_count; i++)
      {
        if(read_acpi_batt(i) != SUCCESS)
          fprintf(stderr, "Cannot read battery #%d status.\n", i);
        r += batteries[i].percentage;
        if(r > 0)
        {
          rem_percent += r;
          rem_percent_bcount++;
        }
        r = on_ac
              ? batteries[i].charge_time
              : batteries[i].remaining_time;
        if(r > 0)
          rem_time += r;
      }
      rem_percent = rem_percent_bcount
                      ? rem_percent / rem_percent_bcount
                      : 0;

      /* print report */
      if(on_ac)
        strcat(batstat, "AC");
      if(!on_ac || rem_percent < 100)
      {
        if(on_ac) strcat(batstat, " charging ");
        sprintf(batstat + strlen(batstat), "%d%%, %d:%02d",
                rem_percent,
                rem_time / 60,
                rem_time % 60);
      }
      strcat(batstat, " ");
    }
#endif

    /* what time is it? [TIME TO DIE MOTHERFUCKER!!!] */
    time(&t);
    localtime_r(&t, &ts);

    /* format msg */
    sprintf(s, "%02d:%02d %02d/%02d/%d [",
            ts.tm_hour, ts.tm_min,
            ts.tm_mday, ts.tm_mon + 1, ts.tm_year + 1900);

    /* add ac/battery status report */
    strcat(s, batstat);

    /* get key status */
    if(xkb_ext)
    {
      unsigned int state;
      char *t = s + strlen(s);

      XkbGetIndicatorState(dpy, XkbUseCoreKbd, &state);
      *t++ = state & (1 << 0) ? 'M' : '_';
      *t++ = state & (1 << 1) ? 'N' : '_';
      *t++ = state & (1 << 2) ? 'S' : '_';
      *t = '\0';
    }
    strcat(s, "]");

    /* update dwm title */
    r = XStoreName(dpy, rootwin, s);
    XFlush(dpy);

    /* wait a little before repeat */
    sleep(1);
  }
}

