/* mb-applet-card - CF SD/MMC control applet

   Copyright 2004 Alexander Chukov

   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, 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.
*/

// Dec 2006 - updated by Hd Luc to allow mount/unmount of usb storage as well
// Mar 2007 - updated by Hd Luc with to support both 2.4 and 2.6 kernel as well as sudo

#include <libmb/mb.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>

#include <gtk/gtk.h>
#include <gdk/gdkx.h>

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#define IMG_EXT "png"

#define POPUP_WIDTH  130
#define POPUP_HEIGHT 40
#define POPUP_PTR_SZ 10

#define IMG_PREFIX	"/usr/share/pixmaps"

static char *ImgLookup[64] = {
  "cards."      IMG_EXT,
  "cf_mount."   IMG_EXT,
  "cf_unmount." IMG_EXT,
  "sd_mount."   IMG_EXT,
  "sd_unmount." IMG_EXT,
  "usb_mount."   IMG_EXT,
  "usb_unmount." IMG_EXT
};

static char *ThemeName = NULL;
static MBPixbuf *pb;
static MBPixbufImage *Imgs[7] = { 0,0,0,0,0,0,0 }, *ImgsScaled[7] = { 0,0,0,0,0,0,0 };
static Bool   PopupIsMapped = False;

GtkWidget *cards_window;
GtkWidget *cards;
GtkWidget *button;
GtkWidget *button1;
GtkWidget *button2;
GtkWidget *boxV;
GtkWidget *box_cf;
GtkWidget *box_sd;
GtkWidget *box_usb;

int old_sd_status = -1, old_cf_status = -1, old_usb_status = -1;

//////////////////////////////

GtkWidget *xpm_label_box( gchar     *xpm_filename,
                          gchar     *label_text )
{
    GtkWidget *box;
    GtkWidget *label;
    GtkWidget *image;

    /* Create box for image and label */
    box = gtk_hbox_new (FALSE, 0);
    gtk_container_set_border_width (GTK_CONTAINER (box), 2);

    /* Now on to the image stuff */
    image = gtk_image_new_from_file (xpm_filename);

    /* Create a label for the button */
    label = gtk_label_new (label_text);

    /* Pack the image and label into the box */
    gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 3);
    gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 3);

    gtk_widget_show (image);
    gtk_widget_show (label);

    return box;
}

void error_dialog(char *txt)
{
    GtkWidget *dialog;

    dialog = gtk_message_dialog_new(cards_window, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, txt);
    gtk_dialog_run(GTK_DIALOG(dialog));
    gtk_widget_destroy(dialog);
}

void info_dialog(char *txt)
{
    GtkWidget *dialog;

    dialog = gtk_message_dialog_new(cards_window, GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, txt);
    gtk_dialog_run(GTK_DIALOG(dialog));
    gtk_widget_destroy(dialog);
}

void callback_cf( GtkWidget *widget,
               gpointer   data )
{
    int err;
    FILE *fp;
    char buf[256];
    boolean kernelBug=FALSE;
    boolean modelwMD=FALSE;

    fp = fopen("/etc/hardware","r");
    if(fp) {
      fgets(buf, 256, fp);
      fclose(fp);
          if ((strncmp(buf, "Spitz",4 ) == 0) ||
              (strncmp(buf, "Borzoi",5 ) == 0) ||
              (strncmp(buf, "Terrier",6 ) == 0))
          modelwMD=TRUE;
	}

    fp = fopen("/etc/issue", "r");
    fgets(buf, 256, fp);
    fclose(fp);
    if (strncmp(buf, "pdaXrom 1.1.0r1",14 ) == 0)
      kernelBug=TRUE;
    
    gtk_widget_hide (cards_window);
    if (old_cf_status == 1) {
	fp = fopen("/sbin/cardctl","r");
	if(fp) {
          fclose(fp);
          err = system("sudo cardctl eject 0");
	}
	else
	{
	  if(kernelBug&&modelwMD)
            err = system("sudo pccardctl eject 1");
	  else
            err = system("sudo pccardctl eject 0");
	}
	if (err != 0) {
	    error_dialog("CF/PCMCIA card eject failed!");
	}
	else {
	    info_dialog("You can remove the CF card now.");
	}
    } else {
	fp = fopen("/sbin/cardctl","r");
	if(fp) {
          fclose(fp);
          err = system("sudo cardctl insert 0");
	}
	else
	{
	  if(kernelBug&&modelwMD)
            err = system("sudo pccardctl insert 1");
	  else
            err = system("sudo pccardctl insert 0");
	}
	if (err != 0) {
	    error_dialog("CF/PCMCIA card insert failed!");
	}
    }
}

void callback_sd( GtkWidget *widget,
               gpointer   data )
{
    FILE *fp;
    int err;
    boolean sdcontrol;

    fp = fopen("/etc/sdcontrol","r");
    if (fp) {
	fclose(fp);
	sdcontrol=TRUE;
    }
    else
	sdcontrol=FALSE;
    gtk_widget_hide (cards_window);
    if (old_sd_status == 1) {
	if (sdcontrol)
	    err = system("sudo /etc/sdcontrol eject");
	else
	    err = system("sudo umount /mnt/card");
	if (err != 0) {
	    error_dialog("SD/MMC card eject failed!");
	}
	else {
	    info_dialog("You can remove the SD/MMC card now.");
	}
    } else {
	if (sdcontrol)
	    err = system("sudo /etc/sdcontrol insert");
	else
	    err = system("sudo mount /mnt/card");
	if (err != 0) {
	    error_dialog("SD/MMC card insert failed!");
	}
    }
}

void callback_usb( GtkWidget *widget,
               gpointer   data )
{
    FILE *fp;
    int err;
    boolean usbcontrol;

    fp = fopen("/etc/hotplug/usb/usb-storage.off","r");
    if (fp) {
	fclose(fp);
	usbcontrol=TRUE;
    }
    else
	usbcontrol=FALSE;
    
    gtk_widget_hide (cards_window);
    if (old_usb_status == 1) {
	if (usbcontrol)
            err = system("sudo /etc/hotplug/usb/usb-storage.off");
	else
            err = system("sudo umount /mnt/usbstorage");
	if (err != 0) {
	    error_dialog("USB storage eject failed!");
	}
	else {
	    info_dialog("You can remove the USB storage device now.");
	}
    } else {
	if (usbcontrol)
	    err = system("sudo /etc/hotplug/usb/usb-storage");
	else
	    err = system("sudo mount /dev/sda1 /mnt/usbstorage");
	if (err != 0) {
	    error_dialog("USB storage mount failed!");
	}
    }
}

void init_buttons(void)
{
    boxV = gtk_vbox_new(FALSE, 0);

    // CF 
    button = gtk_button_new ();
    g_signal_connect (G_OBJECT (button), "clicked",
		      G_CALLBACK (callback_cf), (gpointer) "cool button");
    box_cf = xpm_label_box (IMG_PREFIX "/cf_mount.png", "CF eject\nlala");
    gtk_widget_show (box_cf);
    gtk_container_add (GTK_CONTAINER (button), box_cf);
    gtk_box_pack_start(GTK_BOX(boxV), button, FALSE, FALSE, 0);
    gtk_widget_show (button);

    // SD
    button1 = gtk_button_new ();
    g_signal_connect (G_OBJECT (button1), "clicked",
		      G_CALLBACK (callback_sd), (gpointer) "cool button1");
    box_sd = xpm_label_box (IMG_PREFIX "/sd_mount.png", "SD eject");
    gtk_widget_show (box_sd);
    gtk_container_add (GTK_CONTAINER (button1), box_sd);
    gtk_box_pack_start(GTK_BOX(boxV), button1, FALSE, FALSE, 0);
    gtk_widget_show (button1);

    // USB 
    button2 = gtk_button_new ();
    g_signal_connect (G_OBJECT (button2), "clicked",
		      G_CALLBACK (callback_usb), (gpointer) "cool button2");
    box_usb = xpm_label_box (IMG_PREFIX "/usb_mount.png", "USB eject");
    gtk_widget_show (box_usb);
    gtk_container_add (GTK_CONTAINER (button2), box_usb);
    gtk_box_pack_start(GTK_BOX(boxV), button2, FALSE, FALSE, 0);
    gtk_widget_show (button2);


    gtk_container_add (GTK_CONTAINER (cards_window), boxV);
    gtk_widget_show (boxV);
}

void check_for_cards(void)
{
    int sd_found = 0;
    int usb_found = 0;
    FILE *inf;
    char buf[256];
    inf = fopen("/var/lib/pcmcia/stab", "r");
    if(inf) {
    fgets(buf, 256, inf);
    fclose(inf);
    }
    else
    {
      boolean kernelBug=FALSE;
      boolean modelwMD=FALSE;

      inf = fopen("/etc/hardware","r");
      if(inf) {
        fgets(buf, 256, inf);
        fclose(inf);
          if ((strncmp(buf, "Spitz",4 ) == 0) ||
              (strncmp(buf, "Borzoi",5 ) == 0) ||
              (strncmp(buf, "Terrier",6 ) == 0))
          modelwMD=TRUE;
	}

      inf = fopen("/etc/issue", "r");
      fgets(buf, 256, inf);
      fclose(inf);
      if (strncmp(buf, "pdaXrom 1.1.0r1",14 ) == 0)
        kernelBug=TRUE;
   
      if(kernelBug&&modelwMD) 
        inf = fopen("/sys/bus/pcmcia/devices/1.0/prod_id1","r");
      else
        inf = fopen("/sys/bus/pcmcia/devices/0.0/prod_id1","r");
      if (inf) {
	fclose(inf);
	sprintf(buf,"Socket 0: CF Card");
      }
      else
	sprintf(buf,"Socket 0: empty");
    }
    if (strncmp(buf, "Socket 0: empty", 15 ) == 0) {
	    gtk_widget_destroy(box_cf);
	    box_cf = xpm_label_box (IMG_PREFIX "/cf_unmount.png", "empty");
	    gtk_widget_show (box_cf);
	    gtk_container_add (GTK_CONTAINER (button), box_cf);
	old_cf_status = 0;
    } else {
	    gtk_widget_destroy(box_cf);
	    box_cf = xpm_label_box (IMG_PREFIX "/cf_mount.png", &buf[10]);
	    gtk_widget_show (box_cf);
	    gtk_container_add (GTK_CONTAINER (button), box_cf);	    
	old_cf_status = 1;
    }

    inf = fopen("/etc/mtab", "r");
    while (!feof(inf)) {
	fgets(buf, 256, inf);
	if (strncmp(buf, "/dev/mmcd", 9) == 0) sd_found = 1;
	if (strncmp(buf, "/dev/mmcb", 9) == 0) sd_found = 1;
	if (strncmp(buf, "/dev/sda", 8) == 0) usb_found = 1;
    }
    fclose(inf);
    if (sd_found == 0) {
	    gtk_widget_destroy(box_sd);
	    box_sd = xpm_label_box (IMG_PREFIX "/sd_unmount.png", "empty");
	    gtk_widget_show (box_sd);
	    gtk_container_add (GTK_CONTAINER (button1), box_sd);
	old_sd_status = 0;
    } else {
	    gtk_widget_destroy(box_sd);
	    box_sd = xpm_label_box (IMG_PREFIX "/sd_mount.png", "SD card");
	    gtk_widget_show (box_sd);
	    gtk_container_add (GTK_CONTAINER (button1), box_sd);
	old_sd_status = 1;
    }
    if (usb_found == 0) {
	    gtk_widget_destroy(box_usb);
	    box_usb = xpm_label_box (IMG_PREFIX "/usb_unmount.png", "empty");
	    gtk_widget_show (box_usb);
	    gtk_container_add (GTK_CONTAINER (button2), box_usb);
	old_usb_status = 0;
    } else {
	    gtk_widget_destroy(box_usb);
	    box_usb = xpm_label_box (IMG_PREFIX "/usb_mount.png", "USB Storage");
	    gtk_widget_show (box_usb);
	    gtk_container_add (GTK_CONTAINER (button2), box_usb);
	old_usb_status = 1;
    }
}

/* -- tray app callbacks -- */

void
paint_callback (MBTrayApp *app, Drawable drw )
{
  MBPixbufImage *img_backing = NULL;
  
  img_backing = mb_tray_app_get_background (app, pb);

  /* CurrentVolLevel */
  mb_pixbuf_img_composite(pb, img_backing, 
			  ImgsScaled[0], 
			  0, 0);

  mb_pixbuf_img_render_to_drawable(pb, img_backing, drw, 0, 0);

  mb_pixbuf_img_free( pb, img_backing );
}

void
resize_callback (MBTrayApp *app, int w, int h )
{
  int i;

  for (i=0; i<1; i++)
    {
      if (ImgsScaled[i] != NULL) mb_pixbuf_img_free(pb, ImgsScaled[i]);
      ImgsScaled[i] = mb_pixbuf_img_scale(pb, Imgs[i], w, h);
    }
}

void
load_icons(void)
{
 int   i;
 char *icon_path;

  for (i=0; i<1; i++)
    {
      if (Imgs[i] != NULL) mb_pixbuf_img_free(pb, Imgs[i]);
      icon_path = mb_dot_desktop_icon_get_full_path (ThemeName, 
						     32, 
						     ImgLookup[i]);
      
      if (icon_path == NULL 
	  || !(Imgs[i] = mb_pixbuf_img_new_from_file(pb, icon_path)))
	{
	  fprintf(stderr, "cards: failed to load icon\n" );
	  exit(1);
	}

      free(icon_path);
    }
}

void 
theme_callback (MBTrayApp *app, char *theme_name)
{
  if (!theme_name) return;
  if (ThemeName) free(ThemeName);
  ThemeName = strdup(theme_name);
  load_icons(); 	
  resize_callback (app, mb_tray_app_width(app), mb_tray_app_width(app) );
}

gboolean
popup_close (GtkWidget *w, GtkWidget *list_view)
{
  gtk_widget_hide (cards_window);
  PopupIsMapped = False;
  return PopupIsMapped;
}

static void
cards_clicked (GtkWidget *w, GdkEventButton *ev)
{
  gdk_pointer_ungrab (ev->time);
  gtk_widget_hide (cards_window);
}


static void
button_callback (MBTrayApp *app, int cx, int cy, Bool is_released)
{
  int x, y, win_w, win_h;

  if (!is_released) return;

  if (PopupIsMapped) {
    gtk_widget_hide (cards_window);
    PopupIsMapped = False;
  }

  mb_tray_app_get_absolute_coords (app, &x, &y);

  gtk_widget_show_all (cards_window);

  gdk_window_get_geometry (cards_window->window, NULL, NULL,
			   &win_w, &win_h, NULL);

  if (mb_tray_app_tray_is_vertical (app))
    {
      if (x > (DisplayWidth(mb_tray_app_xdisplay(app), 
			    mb_tray_app_xscreen(app)) /2) )
	x -= ( mb_tray_app_width(app) + win_w );
      else
	x += mb_tray_app_width(app);
    }
  else
    {
      if (y < mb_tray_app_height(app))
	{ y = mb_tray_app_height(app); }
      else
	{ 
	  y = DisplayHeight(mb_tray_app_xdisplay(app), 
			    mb_tray_app_xscreen(app)) 
	    - win_h - mb_tray_app_height(app) - 4;
	}

      x -= (mb_tray_app_width(app)/2);
      
      if ((x + win_w) > DisplayWidth(mb_tray_app_xdisplay(app), 
				     mb_tray_app_xscreen(app)))
	x = DisplayWidth(mb_tray_app_xdisplay(app), 
			 mb_tray_app_xscreen(app)) - win_w - 2;
    }

  
  check_for_cards();
  
  gtk_widget_set_uposition (GTK_WIDGET (cards_window), x, y);
  
  gdk_pointer_grab (cards_window->window, TRUE, GDK_BUTTON_PRESS_MASK, NULL, NULL, CurrentTime);

  PopupIsMapped = True;
}


void
popup_vol_changed_cb (GtkAdjustment *adj, gpointer data)
{
  MBTrayApp *app = (MBTrayApp *)data;
  mb_tray_app_repaint(app);
}

void
popup_init(MBTrayApp *app)
{
  cards_window = gtk_window_new (GTK_WINDOW_POPUP);
  g_signal_connect (G_OBJECT (cards_window), "button-press-event", G_CALLBACK (cards_clicked), NULL);
  gtk_widget_add_events (cards_window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
  gtk_widget_realize (cards_window);
}

GdkFilterReturn
event_filter (GdkXEvent *xev, GdkEvent *gev, gpointer data)
{
  XEvent    *ev  = (XEvent *)xev;
  MBTrayApp *app = (MBTrayApp*)data;
  mb_tray_handle_xevent (app, ev); 
  return GDK_FILTER_CONTINUE;
}

static gboolean
cards_timeout_cb (MBTrayApp *app)
{
  mb_tray_app_repaint(app);
  return TRUE;
}

int
main( int argc, char *argv[])
{
  MBTrayApp *app = NULL;

  gtk_init (&argc, &argv);

#if ENABLE_NLS
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, DATADIR "/locale");
  bind_textdomain_codeset (PACKAGE, "UTF-8"); 
  textdomain (PACKAGE);
#endif

  /* XXX check for err_str here */
  
  app = mb_tray_app_new_with_display ( "CF/SD Monitor",
				       resize_callback,
				       paint_callback,
				       &argc,
				       &argv,
				       GDK_DISPLAY ());  
  
  if (!app) exit(0); 
  
  pb = mb_pixbuf_new(mb_tray_app_xdisplay(app), 
		     mb_tray_app_xscreen(app));
  
  mb_tray_app_set_theme_change_callback (app, theme_callback );

  mb_tray_app_set_button_callback (app, button_callback );
  
  gtk_timeout_add (500,
		   (GSourceFunc) cards_timeout_cb,
		   app);
  
  load_icons();
  
  mb_tray_app_set_icon(app, pb, Imgs[0]);

  popup_init(app);

  mb_tray_app_main_init (app);
  
  gdk_window_add_filter (NULL, event_filter, (gpointer)app );
  
  init_buttons();
  
  gtk_main ();
  
  return 1;
}
