#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <config.h>

#include "global.h"
#include "color.h"
#include "util.h"
#include "mountlist.h"
#include "panel.h"
#include "main.h"
#include "dialog.h"

#include "wtools.h"
#include "widget.h"

#include "cmd.h"
#include "tty.h"

#include "selmnt.h"

#ifdef HAVE_SLANG
#   include "myslang.h"
#endif

// uncomment to retrieve FS usage statistics (may be slow)
//#define MNT_USE_GET_FS_USAGE

extern unsigned char get_hotkey(int);

int mc_cd_mountpoint_and_dir = 0;

extern struct mount_entry *mount_list;
extern void listbox_select_by_number (struct WListbox *l, int n);

#ifdef __BEOS__
struct mount_entry *read_filesystem_list (int need_fs_type, int all_fs);

struct mount_entry *read_filesystem_list (int need_fs_type, int all_fs)
{
	static struct mount_entry	*me = NULL;

	if (me)
	{
		if (me->me_devname) free(me->me_devname);
		if (me->me_mountdir) free(me->me_mountdir);
		if (me->me_type) free(me->me_type);
		return (NULL);
	}
	else
		me = (struct mount_entry *)malloc(sizeof(struct mount_entry));

	
	me->me_devname = strdup("//");
	me->me_mountdir = strdup("//");
	me->me_type = strdup("unknown");

	return (me);
}
#endif /*__BEOS__*/
//------------------ functions

/* Returns the number of the item selected */
int run_custom_listbox (Listbox *l)
{
    int val;
    
    run_dlg (l->dlg);
    if (l->dlg->ret_value == B_CANCEL)
	val = -1;
    else
	val = l->list->pos;
    destroy_dlg (l->dlg);
    free (l);
    return val;
}

//------------------ create own listbox window

static cb_ret_t custom_listbox_callback (struct Dlg_head * h, dlg_msg_t Msg,
                                           int Par __attribute__ ((__unused__)))
{
    switch (Msg)
    {
	case DLG_KEY:
	case DLG_DRAW:
            attrset (COLOR_NORMAL);
            dlg_erase (h);
	    
		draw_box (h, 0, 0, h->lines, h->cols);
            attrset (COLOR_HOT_NORMAL);
    	    if (h->title){
		dlg_move (h, 0, (h->cols-strlen (h->title))/2);
		addstr (h->title);
	    }
            break;

	default:;
    }
    return 0;
}

static void
dialog_repaint (struct Dlg_head *h, int back, int title_fore)
{
    attrset (back);
    dlg_erase (h);
    draw_box (h, 1, 1, h->lines - 2, h->cols - 2);
    attrset (title_fore);
    if (h->title){
       dlg_move (h, 1, (h->cols-strlen (h->title))/2);
       addstr (h->title);
    }
}

static void
listbox_refresh (Dlg_head *h)
{
    dialog_repaint (h, COLOR_NORMAL, COLOR_HOT_NORMAL);
}

static void
x_set_dialog_title (Dlg_head *h, char *title)
{
    h->title = strdup(title);
}

static
void *xmalloc (int size, const char *msg)
{
    void *m = malloc (size);

    if (!m){
       fprintf (stderr, "memory exhausted (%s)\n", msg);
       exit (1);
    }
    return m;
}

Listbox *create_custom_listbox_window (WPanel *panel, int cols, int lines, char *title, char *help)
{
    int x, y, w, h;
    int widget_x, widget_y;
    Listbox  *listbox = (Listbox *)xmalloc(sizeof (Listbox), "create_custom_listbox_window");

    widget_x = panel->widget.x ;
    widget_y = panel->widget.y ;

    y = widget_y;
    h = lines+2;
    
    if (h <= y || y > LINES - 6)
    {
        h = min(h, y - 1);
        y -= h;
    }
    else
    {
        y++;
        h = min(h, LINES - y);
    }

    x = widget_x;

    if ((w = cols + 4) + x > COLS)
    {
        w = min(w,COLS);
    }

    x = widget_x + (panel->widget.cols - w)/2;
// additional checking
    x = x > 0 ? x : widget_x + 3;
    y = widget_y + (panel->widget.lines - h)/2;

//////////////////////////////
// Create components

    listbox->dlg = create_dlg (y, x, h, w, dialog_colors, custom_listbox_callback, 
				help, "listbox", DLG_NONE);

    x_set_dialog_title (listbox->dlg, title);

    listbox->list = listbox_new (1, 1, w - 2, h - 2, 0);

    add_widget (listbox->dlg, listbox->list);
    listbox_refresh(listbox->dlg);

    return listbox;
}

//------------------ show mounts list
static
int show_mnt (Mountp *mntpoints, WPanel *panel)
{
    unsigned char hotkey;
    Mountp *mz, *mhi;
    int menu_lines, i, maxlen=5, count = 0;
    Listbox* listbox;
    mz = mntpoints;

    if (!mz)
        return 0;

    while (mz->prev)           /* goto first */
        mz = mz->prev;

    mhi = mz;

    while (mhi)
    {
        if ((i = strlen (mhi->mpoint)
    		+ (mc_cd_mountpoint_and_dir ? 6:4)
    		+ ((mhi->path && mc_cd_mountpoint_and_dir) ? strlen(mhi->path) : 0)) > maxlen)
    	    maxlen = i;

        count++;
        mhi = mhi->next;
    }

// count = number of elements
// maxlen = maximum long of mountpoint menu item name

    menu_lines = count;

    /* Create listbox */
    listbox = create_custom_listbox_window (panel, maxlen, menu_lines," Mountpoints ", "Mountpoints");

    i=0;

    while (mz) {
	static char buffer[4096];
//	sprintf( buffer, "%s (%d/%d)", mz->mpoint, mz->total/1024, mz->avail/1024 ); //for example!
//	sprintf( buffer, "%s", mz->mpoint);

    hotkey = get_hotkey(i);

    if (mc_cd_mountpoint_and_dir && mz->path) 
	snprintf(buffer, sizeof(buffer), "%c  %s [%s]", hotkey, mz->mpoint, mz->path);
    else
	snprintf(buffer, sizeof(buffer),"%c  %s", hotkey, mz->mpoint);
    
	LISTBOX_APPEND_TEXT(listbox, hotkey, buffer, NULL);

	mz = mz->next;
	i++;
    }

    /* Select the default entry */
    listbox_select_by_number( listbox->list, 0 );

    return run_listbox(listbox);
}

//------------------ init Mountp
// First - we need helper, that must identify our last dir in this mountpoint
// from dir_history

static
char *path_from_history (WPanel *panel, char *mountpoint)
{
    GList *hd, *d;

    hd = panel->dir_history;

    if(!hd) 
        return NULL;

    if(!strcmp(mountpoint,PATH_SEP_STR)) 
        return NULL;

    d = hd = g_list_first(hd);

    while((d = g_list_next (d)))
	hd = d;

    do
    {
	if (strcmp(mountpoint, (char *)(hd->data)))
    	    if (!strncmp(mountpoint, (char *)(hd->data), strlen(mountpoint)))
		return (char *)(hd->data);

	    hd =  g_list_previous(hd);
    }
    while (hd);

    return NULL;
}

Mountp *init_mountp ( WPanel *panel )
{
    int lockm = 0;
    Mountp *mounts = NULL;
    struct mount_entry *temp = NULL;
    struct mount_entry *it = NULL;

#ifdef MNT_USE_GET_FS_USAGE
    struct fs_usage fs_use;
#endif
    
    it = temp = read_filesystem_list(0,0);

    if(!temp)
	return NULL;

    while(temp)
    {
	if(!strncmp(temp->me_mountdir, "/dev/", 5)
	    || !strncmp(temp->me_mountdir, "/sys/", 5)
	    || !strncmp(temp->me_mountdir, "/lib/", 5)
	    || !strncmp(temp->me_mountdir, "/proc/", 6))
	{
    	    temp = temp->me_next;
	    continue;
	}

	    if (!mounts)
	    {
#ifdef MNT_USE_GET_FS_USAGE
		get_fs_usage (temp->me_mountdir, &fs_use);
#endif
        	mounts = malloc (sizeof (Mountp));
        	memset (mounts, 0, sizeof (Mountp));

		mounts->mpoint = strdup (temp->me_mountdir);
		mounts->path = mc_cd_mountpoint_and_dir ? strdup(path_from_history (panel, temp->me_mountdir)) : NULL;
		mounts->name = strdup (temp->me_devname);

#ifdef MNT_USE_GET_FS_USAGE
		mounts->avail = getuid () ? fs_use.fsu_bavail/2 : fs_use.fsu_bfree/2;
		mounts->total = fs_use.fsu_blocks/2;
#endif
                lockm = 1;
	    }

        if (mounts->next)
        {
	    if(mounts->next->mpoint) { free (mounts->next->mpoint); mounts->next->mpoint = 0; }
	    if(mounts->next->name)   { free (mounts->next->name); mounts->next->name = 0;     }
        }
        else
        {
	    mounts->next = malloc(sizeof (Mountp));
    	    memset (mounts->next, 0, sizeof (Mountp));
            mounts->next->prev = mounts;
        }

        if (lockm != 1)
        {
#ifdef MNT_USE_GET_FS_USAGE
	    get_fs_usage (temp->me_mountdir, &fs_use);
#endif

            mounts = mounts->next;
	    mounts->mpoint = strdup (temp->me_mountdir);
	    mounts->name = strdup (temp->me_devname);
	    mounts->path = mc_cd_mountpoint_and_dir ? strdup(path_from_history (panel, temp->me_mountdir)) : NULL;
#ifdef MNT_USE_GET_FS_USAGE
	    mounts->avail = getuid () ? fs_use.fsu_bavail/2 : fs_use.fsu_bfree/2;
	    mounts->total = fs_use.fsu_blocks/2;
#endif
        }

        lockm = 0;
        temp = temp->me_next;
    }

    /* free memory */
    if(it)
    {
	temp = it;

	while(temp)
	{
    	    free(temp->me_type);
    	    free(temp->me_mountdir);
    	    free(temp->me_devname);

	    it = temp;
    	    temp = temp->me_next;
    	    free(it);
	}
    }

    return mounts; /* must be freed! */
}


static int select_mountpoint( WPanel *panel )
{
    int i, inta;
    char *s = NULL;
    Mountp *mount_ls = init_mountp(panel);
    Mountp *tmp = mount_ls;

    if(mount_ls && (mount_ls->prev || mount_ls->next))
    {
        inta = show_mnt (mount_ls, panel);

        if (inta != -1)
	{
	    while (mount_ls->prev)           /* goto first */
    		mount_ls = mount_ls->prev;

	    for (i=0; i<inta; i++)
	        if (mount_ls->next)
	    	    mount_ls = mount_ls->next;

	    // resulting dirname
    	    if (mc_cd_mountpoint_and_dir && mount_ls->path)
		s = mount_ls->path;
	    else if (mount_ls->mpoint)
		s = mount_ls->mpoint;
	}

    	if (s) /* execute CD */
    	    do_panel_cd (panel, s, cd_exact);
	else /* execute command */ 
	{ }

	/* free mem */
	while(tmp->prev)
    	    tmp = tmp->prev;

	mount_ls = tmp;

	while(mount_ls)
	{
	    free(mount_ls->path);
	    free(mount_ls->name);
	    free(mount_ls->mpoint);

	    tmp = mount_ls;
	    mount_ls = mount_ls->next;
	    free(tmp);
	}
    }

    return 1;
}

//------------------ programming interfaces
void select_mnt_left ( void )
{
    switch_to_listing ( 0 );
    select_mountpoint ( left_panel );
}


void select_mnt_right ( void )
{
    switch_to_listing ( 1 );
    select_mountpoint ( right_panel );
}
