/* Directory hotlist -- for the Midnight Commander
   Copyright (C) 1994 Radek Doulik
   Copyright (C) 1995 Janne Kukonlehto

   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>
#ifdef HAVE_UNISTD_H
#   include <unistd.h>
#endif
#include <string.h>
#include <stdio.h>
#include <stdlib.h>		/* For malloc() */
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <grp.h>
#include <pwd.h>
#include "tty.h"
#include "mad.h"
#include "util.h"		/* Needed for the externs */
#include "win.h"
#include "color.h"
#include "dlg.h"
#include "widget.h"
#include "dialog.h"		/* For do_refresh() */
#include "setup.h"		/* For profile_bname */
#include "profile.h"		/* Load/save directories hotlist */

#include "../vfs/vfs.h"
/* Needed for the extern declarations of integer parameters */
#include "dir.h"
#include "panel.h"		/* Needed for the externs */
#include "file.h"
#include "main.h"
#include "global.h"
#include "hotlist.h"

#define UX		5
#define UY		2

#define BX		5
#define BY		18

#define BUTTONS		4
#define LABELS          3
#define B_ADD		B_USER
#define B_REMOVE        B_USER + 1

static WListbox *l_hotlist;

static Dlg_head *hotlist_dlg;

static WLabel *pname;

static char *hotlist_section = "Hotlist";

struct {
    int ret_cmd, y, x;
    char *text;
    int hkey, hpos;
} hotlist_but[BUTTONS] = {
    { B_CANCEL, 0, 53, "[ Cancel ]",  'c', 2, },
    { B_ADD,    0, 25, "[ Add new ]", 'a', 2, },
    { B_REMOVE, 0, 13, "[ Remove ]",  'r', 2, },
    { B_ENTER,  0,  0, "[[ Chdir ]]", 'd', 5, },
};

struct {
    int y, x;
    char *text;
} hotlist_text [LABELS] = {
    { UY, UX + 1, " Directory label " },
    { UY+12, UX+1, " Directory path "},
    { UY+13, UX+2, "" },
};

/* Directory hotlist */
static struct hotlist{
    char *directory;
    char *label;
    struct hotlist *next;
} *hotlist = NULL;

#ifndef HAVE_X
static void hotlist_refresh (void)
{
    attrset (REVERSE_COLOR);
    dlg_erase (hotlist_dlg);
    
    draw_box (hotlist_dlg, 1, 2, 20, 70);
    draw_box (hotlist_dlg, UY, UX, 12, 63);
    draw_box (hotlist_dlg, UY + 12, UX, 3, 63);
}
#endif

/* If current->data is 0, then we are dealing with a VFS pathname */
static INLINE void update_path_name ()
{
    char *text;

    if (!l_hotlist->current)
	return;
    
    if (l_hotlist->current->data != 0)
	text = ((struct hotlist *)l_hotlist->current->data)->directory;
    else {
	text = l_hotlist->current->text;
    }
    label_set_text (pname, name_trunc (text, 59));
}

static int hotlist_callback (Dlg_head * h, int Par, int Msg)
{
    switch (Msg) {
#ifndef HAVE_X    
    case DLG_DRAW:
	hotlist_refresh ();
	break;
#endif	

    case DLG_POST_KEY:
	/* fall */
    case DLG_INIT:
	attrset (MENU_ENTRY_COLOR);
	update_path_name ();
	break;
    }
    return 0;
}

static int l_call (void *data)
{
    return 1;
}

static void add_name_to_list (char *path)
{
    listbox_add_item (l_hotlist, 0, 0, path, 0);
}

static void init_hotlist (int is_vfs_list)
{
    int i;
    struct hotlist *current = hotlist;

    do_refresh ();

    hotlist_dlg = create_dlg (0, 0, 22, 74, dialog_colors,
			      hotlist_callback, "[Hotlist]",
			      is_vfs_list ? "vfshot":"hotlist",
			      DLG_CENTER);
    x_set_dialog_title (hotlist_dlg,
        is_vfs_list ? "Active VFS directories":"Directory hotlist");

#define XTRACT(i) BY+hotlist_but[i].y, BX+hotlist_but[i].x, hotlist_but[i].ret_cmd, hotlist_but[i].text, hotlist_but[i].hkey, hotlist_but[i].hpos, 0, 0

    for (i = 0; i < BUTTONS; i++){
	if (is_vfs_list && (i == 1 || i == 2))
	    continue;
	add_widgetl (hotlist_dlg, button_new (XTRACT (i)), (i == BUTTONS - 1) ?
	    XV_WLAY_CENTERROW : XV_WLAY_RIGHTOF);
    }

    /* We add the labels.  We are interested in the last one,
     * that one will hold the path name label
     */
    for (i = 0; i < LABELS; i++){
	pname = label_new (hotlist_text [i].y,
			   hotlist_text [i].x, hotlist_text [i].text);
	add_widget (hotlist_dlg, pname);
    }
    /* get new listbox */
    l_hotlist = listbox_new (UY + 1, UX + 1, 61, 10, 0, l_call);

    /* Fill the hotlist with the active VFS or the hotlist */
    if (is_vfs_list){
	listbox_add_item (l_hotlist, 0, 0, home_dir, 0);
	vfs_fill_names (add_name_to_list);
    } else
	while (current){
	    listbox_add_item (l_hotlist, 0, 0, current->label, current);
	    current = current->next;
	}

    add_widgetl (hotlist_dlg, l_hotlist, XV_WLAY_EXTENDWIDTH); 
    /* add listbox to the dialogs */
}

static void hotlist_done (void)
{
    destroy_dlg (hotlist_dlg);
    if (0)
	update_panels (UP_OPTIMIZE, UP_KEEPSEL, UP_KEEPSEL);
    repaint_screen ();
}

static void add2hotlist (char *label, char *directory)
{
    struct hotlist *current, *old;

    old = NULL;
    current = hotlist;
    while (current && strcmp (current->label, label) <= 0){
	old = current;
	current = current->next;
    }

    if (old == NULL){
	hotlist = malloc (sizeof (struct hotlist));
	hotlist->label = label;
	hotlist->directory = directory;
	hotlist->next = current;
    } else {
	struct hotlist *new;
	new = malloc (sizeof (struct hotlist));
	new->label = label;
	new->directory = directory;
	old->next = new;
	new->next = current;
    }
}

void add2hotlist_cmd (void)
{
    char *prompt, *label;

    prompt = malloc (strlen (cpanel->cwd) + 20);
    sprintf (prompt, "Label for \"%s\":", cpanel->cwd);
    label = input_dialog (" Add to hotlist ", prompt, "");
    free (prompt);
    if (!label || !*label)
	return;

    add2hotlist (label, strdup (cpanel->cwd));
}

static void remove_from_hotlist (struct hotlist *entry)
{
    if (entry == hotlist){
	hotlist = hotlist->next;
    } else {
	struct hotlist *current = hotlist;
	while (current && current->next != entry)
	    current = current->next;
	if (current){
	    current->next = entry->next;
	}
    }
    
    /* FIXME: Commented out because freeing seems to cause corruption
       after some add and remove operations */
    /*free (entry->label);
    free (entry->directory);
    free (entry);*/
}

char *hotlist_cmd (int vfs_or_hotlist)
{
    char *target = NULL;

    init_hotlist (vfs_or_hotlist);

    /* display file info */
    attrset (SELECTED_COLOR);

    run_dlg (hotlist_dlg);

    switch (hotlist_dlg->ret_value) {
    case B_CANCEL:
	break;

    case B_ADD:
	add2hotlist_cmd ();
	break;

    case B_REMOVE:
	remove_from_hotlist (l_hotlist->current->data);
	break;

    case B_ENTER:
	if (l_hotlist->current->data)
	    target = ((struct hotlist*) l_hotlist->current->data) -> directory;
	else
	    target = strdup (l_hotlist->current->text);
	break;
    }

    hotlist_done ();
    return target;
}

void load_hotlist (void)
{
    void *profile_keys;
    char *key, *value;
    
    profile_keys = profile_init_iterator (hotlist_section, profile_name);

    if (!profile_keys){
	add2hotlist (strdup ("Home directory"), strdup (home_dir));
	add2hotlist (strdup ("Root directory"), strdup (PATH_SEP_STR));
	return;
    }
    
    while (profile_keys){
	profile_keys = profile_iterator_next (profile_keys, &key, &value);
	add2hotlist (strdup (value), strdup (key));
    }
}

void save_hotlist (void)
{
    struct hotlist *current = hotlist;
    
    profile_clean_section (hotlist_section, profile_name);
    for (;current; current = current->next){
	WritePrivateProfileString (hotlist_section,
				   current->directory,
				   current->label,
				   profile_name);
    }
    /* So that maybe later setup is not saved if not asked to */
    sync_profiles ();
}

void done_hotlist (void)
{
    struct hotlist *current = hotlist;
    struct hotlist *next;

    for (; current; current = next){
	next = current->next;
	free (current->label);
	free (current->directory);
	free (current);
    }
}
