/*
 *    wmmaiload - A dockapp to monitor mails numbers
 *    Copyright (C) 2002  Thomas Nemeth <tnemeth@free.fr>
 *
 *    Based on work by Seiichi SATO <ssato@sh.rim.or.jp>
 *    Copyright (C) 2001,2002  Seiichi SATO <ssato@sh.rim.or.jp>
 *    and on work by Mark Staggs <me@markstaggs.net>
 *    Copyright (C) 2002  Mark Staggs <me@markstaggs.net>
 
 *    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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include "config.h"
#include "main.h"
#include "options.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pwd.h>
#include <signal.h>
#include <dirent.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <utime.h>
#include <time.h>
#include <math.h>
#ifdef HAVE_POP3
# include "pop3client.h"
#endif
#ifdef HAVE_IMAP
# include "imapclient.h"
#endif
#include "dockapp.h"
#include "checkthread.h"
#include "backlight_on.xpm"
#include "backlight_off.xpm"
#include "parts.xpm"


Bool             test_size   = False;
time_t           check_delay = 30;
MailBox         *mboxes      = NULL;
char            *notif_cmd   = NULL;
Bool             run_once    = False;
int              boxnum      = 0;
Bool             use_threads = True;
pthread_mutex_t  mutex_list;

Pixmap pixmap;
Pixmap backdrop_on;
Pixmap backdrop_off;
Pixmap parts;
Pixmap mask;
static char	 *display_name      = "";
static char	 *light_color       = NULL; /* back-light color */
static unsigned   update_interval   = 1;
static light      backlight         = LIGHTOFF;
static unsigned   new_mails         = 0;
static unsigned   total_mails       = 0;
static Bool       switch_authorized = True;
static Bool       display_new       = True;
static char      *command           = NULL;
static char      *config_file       = NULL;
static time_t     config_mtime      = 0;
static off_t      config_fsize      = 0;
static MailAlarm  alarm_type        = NOALARM;
static StrLst    *box_list          = NULL;

static Option opttab[] = {
        OHLP("this help"),
        OVRS("version"),
        OBOL('w', NULL,                      &dockapp_iswindowed,
             "run in windowed mode"),
        OBOL('k', NULL,                      &dockapp_isbrokenwm,
             "broken WM mode"),
        FBOL('b', NULL,        "Backlight",  &backlight,
             "set backlight"),
        FBOL('s', NULL,        "Blink",      &switch_authorized,
             "disable blinking"),
        FBOL('r', NULL,        "RunOnce",    &run_once,
             "notify only once"),
        FBOL('z', NULL,        "CheckSize",  &test_size,
             "check boxes size instead of time"),
        FBOL('t', NULL,        "UseThreads", &use_threads,
             "don't use threads"),
        OSTR('d', "<display>",               &display,
             "set display"),
        FSTR('c', "<color>",   "Color",      &light_color,
             "set light color"),
        FSTR('x', "<command>", "Command",    &command,
             "command to launch with button 2"),
        FSTR('n', "<command>", "Notify",     &notif_cmd,
             "command to notify new mail arrival"),
        OSTR('f', "<file>",                  &config_file,
             "load configuration file"),
        FINT('i', "<secs>",    "Interval",   &update_interval,
             "refresh interval"),
        FINT('o', "<box-num>", "DefaultBox", &boxnum,
             "box to open directly"),
        FINT('l', "<delay>",   "CheckDelay", &check_delay,
             "network boxes check frequency"),
        FSTL('m', "<box-def>", "Mailbox",    &box_list,
             "mail box definition between double-quotes (see manpage)"),
        OSTP
};



/* prototypes */
static void control(XEvent event);
static void switch_box_or_light(XEvent event);
static void exec_command(XEvent event);
static void switch_box_or_blink(XEvent event);
static void install_pixmaps();
static void update();
static void switch_light();
static void switch_leds();
static void draw_dockapp();
static void draw_boxdigit(int num);
static void draw_newdigit(int num);
static void draw_totaldigit(int num);
static void mbox_add(MailBox **list, const char *value);
static BoxType getboxtype(const char *t);
static void free_mbox(MailBox **list);
static int nb_mbox(MailBox *list);
static void check_cfg_values();
static void replace_box_list(MailBox *replace);


int main(int argc, char **argv)
{
        XEvent event;
        struct sigaction sa;

        sa.sa_handler = SIG_IGN;
#ifdef SA_NOCLDWAIT
        sa.sa_flags = SA_NOCLDWAIT;
#else
        sa.sa_flags = 0;
#endif
        sigemptyset(&sa.sa_mask);
        sigaction(SIGCHLD, &sa, NULL);

#if DEBUG_LEVEL>0
        printf("startup...\n");
#endif
        init_app(PACKAGE, PROGRAM, VERSION, BUILD, AUTHORS);
#if DEBUG_LEVEL>0
        printf("application data initialized.\n");
#endif
        parse_argv(opttab, argc, argv);
#if DEBUG_LEVEL>0
        printf("arguments parsed.\n");
#endif
        check_cfg_values();
        /* Load default configuration */
        if (! config_file)
        {
                char *Home = robust_home();
                config_file = xmalloc(strlen(Home) + strlen(DEFAULT_CFGFILE) + 2);
                sprintf(config_file, "%s/%s", Home, DEFAULT_CFGFILE);
        }
        if (fexist(config_file)) load_cfgfile(False);
#if DEBUG_LEVEL>0
        printf("configuration loaded.\n");
        {
                MailBox * box = mboxes;
                int       i   = 0;
                while (box)
                {
                        printf("Box #%d : %s\n", ++i, box->entry);
                        box = box->next;
                }
        }
#endif

        /* Initialize Application */
#ifdef HAVE_THREADS
        if (use_threads)
        {
                launch_threads();
#  if DEBUG_LEVEL>0
                printf("threads launched.\n");
#  endif
        }
        else
        {
                check_boxes();
#  if DEBUG_LEVEL>0
                printf("no thread launched : initial check done.\n");
#  endif
        }
#else
        check_boxes();
#  if DEBUG_LEVEL>0
        printf("initial check done.\n");
#  endif
#endif
        mail_getnumbers(&new_mails, &total_mails, &alarm_type);
        dockapp_open_window(display_name, PACKAGE, SIZE, SIZE, argc, argv);
        dockapp_set_eventmask(ButtonPressMask);

        install_pixmaps();
        draw_dockapp();

        /* Main loop */
        while (1)
        {
                if (dockapp_nextevent_or_timeout(&event, update_interval * 1000))
                {
                        /* Next Event */
                        control(event);
                }
                else
                {
                        /* Time Out */
                        update();
                }
        }

        return 0;
}


static void control(XEvent event)
{
        switch (event.type)
        {
                case ButtonPress:
                        switch (event.xbutton.button)
                        {
                                case 1:
                                        switch_box_or_light(event);
                                        break;
                                case 2:
                                        exec_command(event);
                                        break;
                                case 3:
                                        switch_box_or_blink(event);
                                        break;
                                default:
                                        break;
                        }
                        break;
                default:  /* make gcc happy */
                        break;
        }
}


static void switch_box_or_light(XEvent event)
{
        if ( (event.xbutton.x >= 13) && (event.xbutton.x <= 17) &&
             (event.xbutton.y >= 22) && (event.xbutton.y <= 26) )
        {
                if (nb_mbox(mboxes) > 1) boxnum++;
                if (boxnum > nb_mbox(mboxes) ) boxnum = 0;
        }
        else if ( (event.xbutton.x >= 7) && (event.xbutton.x <= 11) &&
                  (event.xbutton.y >= 22) && (event.xbutton.y <= 26) )
        {
                if (nb_mbox(mboxes) > 1) boxnum--;
                if (boxnum < 0 ) boxnum = nb_mbox(mboxes);
        }
        else
        {
                switch_light();
        }
        draw_dockapp();
}


static void exec_command(XEvent event)
{
        if ((event.xbutton.state & ControlMask) == ControlMask)
                my_system("wmmaiload-config -f ", config_file);
        else
                if (command) my_system(command, NULL);
}


static void switch_box_or_blink(XEvent event)
{
        if ( (event.xbutton.x >= 13) && (event.xbutton.x <= 17) &&
             (event.xbutton.y >= 22) && (event.xbutton.y <= 26) )
        {
                if (nb_mbox(mboxes) > 1) boxnum += 10;
                if (boxnum > nb_mbox(mboxes) ) boxnum = 0;
        }
        else if ( (event.xbutton.x >= 7) && (event.xbutton.x <= 11) &&
                  (event.xbutton.y >= 22) && (event.xbutton.y <= 26) )
        {
                if (nb_mbox(mboxes) > 1) boxnum -= 10;
                if (boxnum < 0 ) boxnum = nb_mbox(mboxes);
        }
        else
        {
                switch_authorized = !switch_authorized;
        }
}


static void install_pixmaps()
{
        XpmColorSymbol colors[2] = { {"Back0", NULL, 0}, {"Back1", NULL, 0} };
        int ncolor = 0;

        if (light_color)
        {
                colors[0].pixel = dockapp_getcolor(light_color);
                colors[1].pixel = dockapp_blendedcolor(light_color, -24, -24, -24, 1.0);
                ncolor = 2;
        }

        /* change raw xpm data to pixmap */
        if (dockapp_iswindowed)
        {
                backlight_on_xpm[1] = WINDOWED_BG;
                backlight_off_xpm[1] = WINDOWED_BG;
        }

        if (backdrop_on) XFreePixmap(display, backdrop_on);
        if (!dockapp_xpm2pixmap(backlight_on_xpm, &backdrop_on, &mask, colors, ncolor))
        {
                fprintf(stderr, "Error initializing backlit background image.\n");
                exit(1);
        }
        if (backdrop_off) XFreePixmap(display, backdrop_off);
        if (!dockapp_xpm2pixmap(backlight_off_xpm, &backdrop_off, NULL, NULL, 0))
        {
                fprintf(stderr, "Error initializing background image.\n");
                exit(1);
        }
        if (parts) XFreePixmap(display, parts);
        if (!dockapp_xpm2pixmap(parts_xpm, &parts, NULL, colors, ncolor))
        {
                fprintf(stderr, "Error initializing parts image.\n");
                exit(1);
        }

        /* shape window */
        if (!dockapp_iswindowed) dockapp_setshape(mask, 0, 0);
        if (mask) XFreePixmap(display, mask);

        /* pixmap : draw area */
        if (pixmap) XFreePixmap(display, pixmap);
        pixmap = dockapp_XCreatePixmap(SIZE, SIZE);

        /* Initialize pixmap */
        if (backlight == LIGHTON)
                dockapp_copyarea(backdrop_on, pixmap, 0, 0, SIZE, SIZE, 0, 0);
        else
                dockapp_copyarea(backdrop_off, pixmap, 0, 0, SIZE, SIZE, 0, 0);

        dockapp_set_background(pixmap);
        dockapp_show();
}


/* called by timer */
static void update()
{
        static light pre_backlight;
        static MailAlarm in_alarm_mode = NOALARM;

        /* get current mailbox' mails numbers */
        mail_getnumbers(&new_mails, &total_mails, &alarm_type);

        /* alarm mode */
        if (alarm_type != NOALARM)
        {
                if (in_alarm_mode == NOALARM)
                {
                        in_alarm_mode = alarm_type;
                        pre_backlight = backlight;
                }
                if ( (alarm_type == NEWGLOB) || (alarm_type == NEWBOTH) )
                {
                        if ( (switch_authorized) ||
                             ( (! switch_authorized) && (backlight != pre_backlight) ) )
                        {
                                switch_light();
                        }
                        if (alarm_type == NEWGLOB) display_new = True;
                }
                if ( (alarm_type == NEWHERE) || (alarm_type == NEWBOTH) )
                {
                        if (switch_authorized) switch_leds();
                        if (alarm_type == NEWHERE) backlight = pre_backlight;
                }
        }
        else
        {
                if (in_alarm_mode != NOALARM)
                {
                        in_alarm_mode = NOALARM;
                        if (backlight != pre_backlight) switch_light();
                        display_new = True;
                }
        }

        draw_dockapp();
}


/* called when mouse button pressed */
static void switch_light()
{
        switch (backlight)
        {
                case LIGHTOFF:
                        backlight = LIGHTON;
                        break;
                case LIGHTON:
                        backlight = LIGHTOFF;
                        break;
        }
}


static void switch_leds()
{
        display_new = !display_new;
}


static void draw_dockapp()
{
        /* all clear */
        if (backlight == LIGHTON)
                dockapp_copyarea(backdrop_on, pixmap, 0, 0, 58, 58, 0, 0);
        else
                dockapp_copyarea(backdrop_off, pixmap, 0, 0, 58, 58, 0, 0);

        /* draw digit */
        draw_boxdigit(boxnum);
        if (display_new) draw_newdigit(new_mails);
        draw_totaldigit(total_mails);

        /* show */
        dockapp_copy2window(pixmap);
}


static void draw_boxdigit(int num)
{
        int x = 0, y = 31;

        if (backlight == LIGHTON)
        {
                x = 50;
                y = 40;
        }

        /* draw digits */
        if (num != 0)
        {
                dockapp_copyarea(parts, pixmap, (num % 10) * 5 + x, 40, 5, 9, 13, 12);
                dockapp_copyarea(parts, pixmap, (num / 10) * 5 + x, 40, 5, 9, 7, 12);
        }
        else
        {
                dockapp_copyarea(parts, pixmap, 100, y, 5, 9, 13, 12);
                dockapp_copyarea(parts, pixmap, 100, y, 5, 9, 7, 12);
        }
}


static void draw_newdigit(int num)
{
        int v1000, v100, v10, v1;
        int y = 0;

        if (num < 0) num = 0;

        v1000 = num / 1000;
        v100 = (num - v1000 * 1000) / 100;
        v10 = (num - v1000 * 1000 - v100 * 100) / 10;
        v1 = (num - v1000 * 1000 - v100 * 100 - v10 * 10);

        if (backlight == LIGHTON) y = 20;

        /* draw digits */
        dockapp_copyarea(parts, pixmap, v1 * 10, y, 10, 20, 45, 7);
        if ( (v10 != 0) || ( (v10 == 0) && (v100 != 0) ) )
                dockapp_copyarea(parts, pixmap, v10 * 10, y, 10, 20, 33, 7);
        if (v100 != 0)
                dockapp_copyarea(parts, pixmap, v100 * 10, y, 10, 20, 21, 7);
}


static void draw_totaldigit(int num)
{
        int v10k, v1000, v100, v10, v1;
        int y = 0;

        if (num < 0) num = 0;

        v10k = num / 10000;
        v1000 = (num - v10k * 10000) / 1000;
        v100 = (num - v10k * 10000 - v1000 * 1000) / 100;
        v10 = (num - v10k * 10000 - v1000 * 1000 - v100 * 100) / 10;
        v1 = (num - v10k * 10000 - v1000 * 1000 - v100 * 100 - v10 * 10);

        if (backlight == LIGHTON) y = 20;

        /* draw digits */
        dockapp_copyarea(parts, pixmap, v1 * 10, y, 10, 20, 45, 34);
        if ( (v10 != 0) || ( (v10 == 0) && (v100 != 0) ) || ( (v10 == 0) && (v100 == 0) && (v1000 != 0) ) )
                dockapp_copyarea(parts, pixmap, v10 * 10, y, 10, 20, 33, 34);
        if ( (v100 != 0) || ( (v100 == 0) && (v1000 != 0) ) )
                dockapp_copyarea(parts, pixmap, v100 * 10, y, 10, 20, 21, 34);
        if (v1000 != 0)
                dockapp_copyarea(parts, pixmap, v1000 * 10, y, 10, 20, 9, 34);
}


Bool fexist(const char *filename)
{
        struct stat s;

        if (stat(filename, &s) == -1) return False;
        return True;
}


Bool filetime(const char *filename, time_t *time, int mode)
{
        struct stat s;
        time_t t = 0;

        if (stat(filename, &s) == -1) return False;
        switch (mode)
        {
                case MTIME:
                        t = s.st_mtime;
                        break;
                case ATIME:
                        t = s.st_atime;
                        break;
                case CTIME:
                        t = s.st_ctime;
                        break;
                default:
                        break;
        }
        if (t == *time)
        {
                return False;
        }
        else
        {
                *time = t;
                return True;
        }
}


Bool filesize(const char *filename, off_t *fsize)
{
        struct stat s;

        if (stat(filename, &s) == -1) return False;
        if (s.st_size == *fsize)
        {
                return False;
        }
        else
        {
                *fsize = s.st_size;
                return True;
        }
        return False;  /* Should not pass here ! */
}


static void mbox_add(MailBox **list, const char *value)
{
        MailBox *lst = *list;
        Bool ok = True;
        char box[100];
        char user[25];
        char pass[25];
        char btype[10];
        char folder[100];
        char *pstr;
        int count, port = 0;

        if (! value) return ;
        memset(box, 0, 100);
        memset(btype, 0, 10);
        memset(user, 0, 25);
        memset(pass, 0, 25);
        memset(folder, 0, 100);
        count = sscanf(value, "%s %s %s %s %s", btype, box, user, pass, folder);

        pstr = strchr(box, ':');
        if (pstr != NULL)
        {
                *pstr = 0;
                pstr++;
                port = atoi(pstr);
        }

        switch (getboxtype(btype))
        {
                case MBOX:
                case MAILDIR:
                case MH:
                        if (count < 2)
                        {
                                fprintf(stderr,
                                        "Invalid Mail box parameters !\n");
                                return ;
                        }
                        if (! fexist(box))
                        {
                                fprintf(stderr,
                                        "Mail box '%s' does not exist !\n",
                                        box);
                                return ;
                        }
                        break;
                case IMAP:
                        if (port == 0) port = 143;
                        if (count != 5) strcat(folder, "INBOX");
                        break;
                case POP3:
                        if (port == 0) port = 110;
                        break;
                case HOTMAIL:
                        if (port == 0) port = 1100;
                        break;
                default:
                        break;
        }

        if (! lst)
        {
                lst = xmalloc(sizeof(MailBox));
                *list = lst;
        }
        else
        {
                if (strcmp(value, lst->entry) == 0) ok = False;
                while ( (lst->next) && ok)
                {
                        lst = lst->next;
                        if (strcmp(value, lst->entry) == 0) ok = False;
                }
                if (! ok) return ;
                lst->next = xmalloc(sizeof(MailBox));
                lst = lst->next;
        }
        lst->entry     = xstrdup(box);
        lst->time      = 0;
        lst->size      = 0;
        lst->new       = 0;
        lst->total     = 0;
        lst->updated   = False;
        lst->changed   = False;
        lst->in_thread = False;
        lst->type      = getboxtype(btype);
        lst->username  = *user ? xstrdup(user) : NULL;
        lst->password  = *pass ? xstrdup(pass) : NULL;
        lst->folder    = *folder ? xstrdup(folder) : NULL;
        lst->port      = port;
        lst->next      = NULL;
}


MailBox * get_mailbox(int box_num)
{
        int bn = 1;
        MailBox * box = mboxes;
        while (box && (bn != box_num))
        {
                box = box->next;
        }
        return box;
}


static BoxType getboxtype(const char *t)
{
        if ((strcmp(t, "MBOX")) == 0)    return MBOX;
        if ((strcmp(t, "MAILDIR")) == 0) return MAILDIR;
        if ((strcmp(t, "MH")) == 0)      return MH;
        if ((strcmp(t, "HOTMAIL")) == 0) return HOTMAIL;
        if ((strcmp(t, "POP3")) == 0)    return POP3;
        if ((strcmp(t, "IMAP")) == 0)    return IMAP;
        return MBOX;
}


static void free_mbox(MailBox **list)
{
        MailBox *lst = *list, *next;
        while (lst)
        {
                next = lst->next;
                FREE(lst->entry);
                FREE(lst->username);
                FREE(lst->password);
                FREE(lst->folder);
                free(lst);
                lst = next;
        }
        *list = NULL;
}


static int nb_mbox(MailBox *list)
{
        MailBox *box = list;
        int n = 0;
        while (box)
        {
                n++;
                box = box->next;
        }
        return n;
}


Bool load_cfgfile(Bool wait)
{
        if ( (!test_size && !filetime(config_file, &config_mtime, MTIME)) ||
             (test_size && !filesize(config_file, &config_fsize)) )
        {
                return False;
        }
        load_config(opttab, config_file, True);
        check_cfg_values(wait);
        return True;
}


static void check_cfg_values(Bool wait)
{
        StrLst  *p         = box_list;
        MailBox *new_boxes = NULL;

        if ( (boxnum < 0) || (boxnum > nb_mbox(mboxes) ) )
        {
                fprintf(stderr,
                        "wmmaiload warning : box-num must be >=0 and <=%d\n",
                        nb_mbox(mboxes));
                boxnum = 0;
        }
        if (check_delay < 15)
        {
                fprintf(stderr,
                        "wmmaiload warning : check delay must be >= 15\n");
                check_delay = 15;
        }
        if (update_interval < 1)
        {
                fprintf(stderr, "wmmaiload warning : interval must be >= 1\n");
                update_interval = 1;
        }
        if (p)
        {
                while (p)
                {
                        mbox_add(&new_boxes, p->entry);
                        p = p->next;
                }
                if (new_boxes)
                {
                        MailBox *tmp = mboxes;

#ifdef HAVE_THREADS
                        if (use_threads)
                        {
                                if (wait) wait_for_threads();

                                pthread_mutex_lock(&mutex_list);
                        }
#endif
                        replace_box_list(new_boxes);
#ifdef HAVE_THREADS
                        if (use_threads)
                        {
                                pthread_mutex_unlock(&mutex_list);
                        }
#endif

                        free_mbox(&tmp);
                }
                free_strlst(&box_list);
        }
}


static void replace_box_list(MailBox *replace)
{
        MailBox *n = replace;

        while (n)
        {
                MailBox *o = mboxes;
                while (o)
                {
                        
                        if (strcmp(o->entry, n->entry) == 0)
                        {
                                n->time  = o->time;
                                n->size  = o->size;
                                n->new   = o->new;
                                n->total = o->total;
                                break;
                        }
                        o = o->next;
                }
                n = n->next;
        }
        mboxes = replace;
}

