/*
	2004.02.01
		first released source code for IOMP
*/

#include <stdlib.h>
#include <stdio.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <unistd.h>
#include <limits.h>


#include "../config.h"

#include "../m_struct.h"
#include "../m_option.h"

#include "img_format.h"
#include "mp_image.h"

#include "menu.h"
#include "menu_list.h"
#include "../input/input.h"
#include "../linux/keycodes.h"

#define DEBUG0(x)   printf x
static char *THIS_PARTITION = "This Partition";
static char *ROOT = NULL;

// Mode 0: MP3 & directory selection
// Mode 1: mpg, mpeg, avi file selection
#define MP3_MODE    0
#define MPGAVI_MODE 1
static int Avamax_Mode = MP3_MODE;

#define FILE_SEL_ONLY   0
#define FOLDER_SEL_ONLY 1
#define BOTH_SEL_ONLY   2
static int Selection_Mode = FILE_SEL_ONLY;

struct list_entry_s {
  struct list_entry p;
  int d;
	int child;
};

struct menu_priv_s {
  menu_list_priv_t p;
  char* dir; // current dir
  /// Cfg fields
  char* path;
  char* title;
  char* file_action;
  char* dir_action;
  int auto_close;
	    int mode;
};

static struct menu_priv_s cfg_dflt = {
  MENU_LIST_PRIV_DFLT,
  NULL,

  NULL,
  "Select a directory: %p",
  "loadfile '%p'",
  NULL,
  0
};

#define ST_OFF(m) M_ST_OFF(struct menu_priv_s,m)

static m_option_t cfg_fields[] = {
  MENU_LIST_PRIV_FIELDS,
  { "path", ST_OFF(path),  CONF_TYPE_STRING, 0, 0, 0, NULL },
  { "title", ST_OFF(title),  CONF_TYPE_STRING, 0, 0, 0, NULL },
  { "file-action", ST_OFF(file_action),  CONF_TYPE_STRING, 0, 0, 0, NULL },
  { "dir-action", ST_OFF(dir_action),  CONF_TYPE_STRING, 0, 0, 0, NULL },
  { "auto-close", ST_OFF(auto_close), CONF_TYPE_FLAG, 0, 0, 1, NULL },
  { "mode", ST_OFF(mode), CONF_TYPE_INT, 0, 10, 0, NULL },
	{ NULL, NULL, NULL, 0,0,0,NULL }
};

#define mpriv (menu->priv)

static void free_entry(list_entry_t* entry) {
  free(entry->p.txt);
  free(entry);
}

static char* replace_path(char* title , char* dir) {
  char *p = strstr(title,"%p");
  if(p) {
    int tl = strlen(title);
    int dl = strlen(dir);
    int t1l = p-title; 
    int l = tl - 2 + dl;
    char*r = malloc(l + 1);
    memcpy(r,title,t1l);
    memcpy(r+t1l,dir,dl);
    if(tl - t1l - 2 > 0)
      memcpy(r+t1l+dl,p+2,tl - t1l - 2);
    r[l] = '\0';
    return r;
  } else
    return title;
}

typedef int (*kill_warn)(const void*, const void*);

static int mylstat(char *dir, char *file,struct stat* st) {
  int l = strlen(dir) + strlen(file);
  char s[l+2];
  sprintf(s,"%s/%s",dir,file);
	printf("mylstat %s\n",s);
  return lstat(s,st);
}

static int compare(char **a, char **b){
int la,lb;
  la = strlen(*a);
  lb = strlen(*b);
  if((*a)[strlen(*a) - 1] == '/') {
    if((*b)[strlen(*b) - 1] == '/')
      return strcmp(*b, *a) ;
    else
      return 1;
  } else {
    if((*b)[strlen(*b) - 1] == '/')
      return -1;
    else
      return strcmp(*b, *a);
  }
}

static int MatchExtName(char *ptr)
{
    if (ptr) {
        switch (Avamax_Mode) {
            case 0: {
                if (strcasecmp(ptr,".mp3") == 0) {
                    return 1;
                }
            } break;
            case 1: {
                if ((strcasecmp(ptr,".mpg") == 0) ||
                    (strcasecmp(ptr,".avi") == 0) ||
                    (strcasecmp(ptr,".mpeg") == 0))
                {
                    return 1;
                }
            } break;
        }
    }
    return 0;
}

static int NumberOfSubdir(char *path,int mode) // mode=1:anthing,0:dir-only
{
    struct dirent *dp;
    DIR *dirp;
    struct stat st;
    int n = 0;
    char *tempdir;

    DEBUG0(("Number of Subdir [%s] mode=%d\n",path,mode));
    if (ROOT) {
        tempdir = (char *) malloc(strlen(ROOT) + strlen(path) + 2);
        sprintf(tempdir,"%s%s",ROOT,path);
    } else {
        tempdir = strdup(path);
    }
    if ((dirp = opendir (tempdir)) == NULL){
        DEBUG0(("opendir error: %s", strerror(errno)));
        return 0;
    }
    while ((dp = readdir(dirp)) != NULL) {
        if(dp->d_name[0] == '.')
            continue;
        if (strcmp(dp->d_name,"..") == 0)
            continue;
        if (mode == 1) {
            n++;
            break;
        }
        mylstat(tempdir,dp->d_name,&st);
        if(S_ISDIR(st.st_mode)) {
            n++;
            break;
        }
    }
    free(tempdir);
    closedir(dirp);
    return n;
}

static void AppendTail(char *src,char *ptr)
{
    int len,flag;
    char *chtr;
    flag = 0;
    len = strlen(src);
    chtr = strrchr(src,'/');
    if (chtr && (*(chtr+1) == 0)) { // Is a Directory
        if (*ptr) {
            strcpy((char *) ptr,".../");
            flag = 1;
        }
    } else {
        chtr = strrchr(src,'.');
        if (chtr) {
            if ((strcasecmp(chtr,".mp3") == 0) ||
                (strcasecmp(chtr,".mpg") == 0) ||
                (strcasecmp(chtr,".avi") == 0))
            {
                if (*ptr) {
                    sprintf((char *) ptr,"...%s",chtr);
                    flag = 1;
                }
            }
        }
    }

    if ((*ptr) && (flag == 0)) {
        strcpy((char *) ptr,"...");
    }
}

static char *TruncateNameTail(char *src)
{
    static unsigned char result[70];
    strncpy((char *) result,src,63);
    unsigned char *ptr = (unsigned char *) result;
    while (*ptr) {
        if (*ptr >= 0xa1) ptr++; 
			// Big-5 首碼在 161 (\241, 0xA1) 到 254 (\376,0xFE) 之間. 
			// 次碼在 64 (\100, 0x40) 到 126 (\176, 0x7E) 之間, 
			// 或是與首碼相同的 161 (\241, 0xA1) 到 254 (\376, 0xFE) 之間
        ptr++;
        if (ptr - result > 31)
            break;
    }
    AppendTail(src,ptr);
    return (char *) result;
}

static char *TruncateNameMiddle(char *src)
{
    static unsigned char result[70];
    if (strlen(src) > 30) {
        unsigned char *ptr = src;
        unsigned char *ptr1;
        unsigned char old;
        while (*ptr) {
            if (*ptr >= 0xa1) ptr++;
            ptr++;
            if ((ptr - (unsigned char *) src) > 15)
                break;
        }
        ptr1 = ptr;
        while (*ptr) {
            if (*ptr >= 0xa1) ptr++;
            ptr++;
            if (strlen(ptr) < 15)
                break;
        }
        old = *ptr1;
        *ptr1 = 0;
        sprintf(result,"%s...%s",src,ptr);
        *ptr1 = old;
    } else {
        strcpy(result,src);
    }

    return (char *) result;
}

static char *TruncateNameFront(char *src)
{
    static unsigned char result[70];
    if (strlen(src) > 30) {
        unsigned char *ptr = src;
        while (*ptr) {
            if (*ptr >= 0xa1) ptr++;
            ptr++;
            if (strlen(ptr) < 30)
                break;
        }
        sprintf(result,"...%s",ptr);
    } else {
        strcpy(result,src);
    }
    return (char *) result;
}

static char *TruncateName(char *src)
{
    return TruncateNameTail(src);
}

static int open_dir(menu_t* menu,char* args) {
  char **namelist, **tp;
  struct dirent *dp;
  struct stat st;
  int n;
  char* p = NULL;
  list_entry_t* e;
  DIR* dirp;
  int IsTop = 0;
  char *tempdir = NULL;

  menu_list_init(menu);

    if (strcmp(args,"/") == 0) {
        IsTop = 1;
    }

  if(mpriv->dir)
    free(mpriv->dir);
  mpriv->dir = strdup(args);
  if(mpriv->p.title && mpriv->p.title != mpriv->title && mpriv->p.title != cfg_dflt.p.title)
    free(mpriv->p.title);
  p = strstr(mpriv->title,"%p");

 if (strlen(mpriv->dir) > 30) {
    char *str1 = strdup(mpriv->dir);
    char *ptr = strrchr(str1,'/');
    char *ptr1;
    if (*ptr) {
        *ptr = 0;
        ptr1 = strrchr(str1,'/');
        char *temp = (char *) malloc(strlen(ptr1) + 6);
        sprintf(temp,"...%s/",TruncateName(ptr1));
        mpriv->p.title = replace_path(mpriv->title,temp);
        free(temp);
    } else {
      mpriv->p.title = replace_path(mpriv->title,mpriv->dir);
    }
    free(str1);
  } else {
  	mpriv->p.title = replace_path(mpriv->title,mpriv->dir);
  }

	if (ROOT) {
        tempdir = (char *) malloc(strlen(ROOT) + strlen(mpriv->dir) + 2);
        sprintf(tempdir,"%s%s",ROOT,mpriv->dir);
    } else {
        tempdir = strdup(mpriv->dir);
    }

printf("open_linux_dir [%s]\n",args);
  if ((dirp = opendir (tempdir)) == NULL){
    printf("opendir error: %s", strerror(errno));
    return 0;
  }

  namelist = (char **) malloc(sizeof(char *));

  n=0;
  while ((dp = readdir(dirp)) != NULL) {
    if(dp->d_name[0] == '.' && strcmp(dp->d_name,"..") != 0)
      continue;
    if(n%20 == 0){ // Get some more mem
      if((tp = (char **) realloc(namelist, (n+20) * sizeof (char *)))
         == NULL) {
        printf("realloc error: %s", strerror(errno));
	n--;
        goto bailout;
      } 
      namelist=tp;
    }

    namelist[n] = (char *) malloc(strlen(dp->d_name) + 2);
    if(namelist[n] == NULL){
      printf("malloc error: %s", strerror(errno));
      n--;
      goto bailout;
    }
     
    strcpy(namelist[n], dp->d_name);
    mylstat(tempdir,namelist[n],&st); 
    if (S_ISDIR(st.st_mode)) {
      strcat(namelist[n], "/");
      n++;
    } else if (S_ISREG(st.st_mode)) {
        char *ptr = strrchr(dp->d_name,'.');
        if (MatchExtName(ptr)) {
            n++;
        }
	}
  }

bailout:
  qsort(namelist, n, sizeof(char *), (kill_warn)compare);
  free(tempdir);
  if (n < 0) {
    printf("readdir error: %s\n",strerror(errno));
    return 0;
  }
  while(n--) {
     e = calloc(1,sizeof(list_entry_t));
    	e->p.dirtxt = NULL;
    	e->d = 0;
    	e->p.next = NULL;
		if ((strcmp(namelist[n],"../") == 0) && (IsTop == 1)) {
        	e->p.txt = strdup(THIS_PARTITION);
        	e->d = 2;
    	}
    	else {
        	e->p.txt = strdup(TruncateName(namelist[n]));
        	e->p.dirtxt = strdup(namelist[n]);
        	if(strchr(namelist[n], '/') != NULL)
            	e->d = 1;
    	}
    	e->child = -1;
		menu_list_add_entry(menu,e);
   	 	free(namelist[n]);
  }
  free(namelist);

  return 1;
}
    

static void read_cmd(menu_t* menu,int cmd) {
  mp_cmd_t* c = NULL;
  switch(cmd) {
  case MENU_CMD_CANCEL: {
        My_SendMessage("OPENDIR_ERROR",150);
    } break;
    case MENU_CMD_CHOICE: {
        char filename[512];
        if (mpriv->p.current->d == 1) {
            if ((Selection_Mode == FOLDER_SEL_ONLY) ||
                (Selection_Mode == BOTH_SEL_ONLY))
                {
                sprintf(filename,"SELECTDIR %s%s",
                    mpriv->dir,mpriv->p.current->p.dirtxt);
                My_SendMessage(filename,150);
            }
        }
		else if(mpriv->p.current->d == 2) {
            if ((Selection_Mode == FOLDER_SEL_ONLY) ||
                (Selection_Mode == BOTH_SEL_ONLY))
                {
                strcpy(filename,"SELECTDIR /");
                My_SendMessage(filename,150);
            }
        }
        else if (mpriv->p.current->d == 0) {
            sprintf(filename,"SELECTFILE %s%s",
                mpriv->dir,mpriv->p.current->p.dirtxt);
            My_SendMessage(filename,150);
        }
    } break;
	case MENU_CMD_OK: {
		printf(" MENU_CMD_OK....\n");
    	// Directory
    	if(mpriv->p.current->d) {
      		if(mpriv->dir_action) {
			int fname_len = strlen(mpriv->dir) + 
							strlen(mpriv->p.current->p.txt) + 1;
			char filename[fname_len];
            char* str;
            sprintf(filename,"%s%s",mpriv->dir,mpriv->p.current->p.dirtxt);
            str = replace_path(mpriv->dir_action,filename);
            c = mp_input_parse_cmd(str);
            if(str != mpriv->dir_action)
            free(str);
      	} else { // Default action : open this dirctory ourself
		printf(" MENU_CMD_OK....1111111\n");
			int l = strlen(mpriv->dir);
			char *slash =  NULL, *p = NULL;
			if(strcmp(mpriv->p.current->p.txt,THIS_PARTITION) == 0) {
            	char filename[32];
                strcpy(filename,"SELECTDIR /");
                My_SendMessage(filename,150);
            } else {
		printf(" MENU_CMD_OK....222222\n");
				if(strcmp(mpriv->p.current->p.txt,"../") == 0) {
	  				if(l <= 1) break;
	  				mpriv->dir[l-1] = '\0';
	  				slash = strrchr(mpriv->dir,'/');
	  				if(!slash) break;
	  				slash[1] = '\0';
	  				p = strdup(mpriv->dir);
				} else {
	  				p = malloc(l + strlen(mpriv->p.current->p.txt) + 1);
	  				sprintf(p,"%s%s",mpriv->dir,mpriv->p.current->p.txt);
				}
				if (mpriv->p.current->child == -1) {
                        mpriv->p.current->child =
                            NumberOfSubdir(p,
                                (Selection_Mode == FOLDER_SEL_ONLY) ? 0 : 1);
                    }
		printf(" MENU_CMD_OK....%s   child=%d\n",p,mpriv->p.current->child);
                    if (mpriv->p.current->child > 0) {
						menu_list_uninit(menu,free_entry);
					printf("Call open_dir\n");
						if(!open_dir(menu,p)) {
	  						printf("Can't open directory %s\n",p);
	  						menu->cl = 1;
							My_SendMessage("OPENDIR_ERROR",150);
                        }
                    }
					free(p);
      			}
			}
    	} else { // Files
			char *filename;
            filename = (char *) malloc(strlen(mpriv->dir) +
                                        strlen(mpriv->p.current->p.dirtxt) +
                                        20);
            sprintf(filename,"SELECTFILE %s%s",
                mpriv->dir,mpriv->p.current->p.dirtxt);
            My_SendMessage(filename,150);
            free(filename);
        }
    	if(c) {
      		mp_input_queue_cmd(c);
      		if(mpriv->auto_close)
			menu->cl = 1;
    	}
  	} break;
  default:
    menu_list_read_cmd(menu,cmd);
  }
}

static void read_key(menu_t* menu,int c){
  if(c == KEY_BS) {
    mpriv->p.current = mpriv->p.menu; // Hack : we consider that the first entry is ../
    read_cmd(menu,MENU_CMD_OK);
  } else
    menu_list_read_key(menu,c,1);
}

static void clos(menu_t* menu) {
  menu_list_uninit(menu,free_entry);
  free(mpriv->dir);
}

static int open_fs(menu_t* menu, char* args) {
  char *path = mpriv->path;
  int r = 0;
  char wd[PATH_MAX+1];
  args = NULL; // Warning kill

	Avamax_Mode = mpriv->mode;
	switch (Avamax_Mode) {
    	case 0: { // MP3 + Dir
        	Selection_Mode = BOTH_SEL_ONLY;
    	} break;
    	case 1: { // MPG/AVI
        	Selection_Mode = FILE_SEL_ONLY;
    	} break;
  	}

  menu->draw = menu_list_draw;
  menu->read_cmd = read_cmd;
  menu->read_key = read_key;
  menu->close = clos;

	printf("menu_file open_fs:%s\n",path);
  getcwd(wd,PATH_MAX);
  if(!path || path[0] == '\0') {
    int l = strlen(wd) + 2;
    char b[l];
    sprintf(b,"%s/",wd);
    r = open_dir(menu,b);
  } else if(path[0] != '/') {
    int al = strlen(path);
    int l = strlen(wd) + al + 3;
    char b[l];
    if(b[al-1] != '/')
      sprintf(b,"%s/%s/",wd,path);
    else
      sprintf(b,"%s/%s",wd,path);
    r = open_dir(menu,b);
  } else {
	ROOT = strdup(path);
    ROOT[strlen(ROOT)-1] = 0;
    r = open_dir(menu,"/");
  }
  if (r == 0)
     My_SendMessage("OPENDIR_ERROR",150);
  return r;
}
  
const menu_info_t menu_info_filesel = {
  "File seletor menu",
  "filesel",
  "Albeu",
  "",
  {
    "fs_cfg",
    sizeof(struct menu_priv_s),
    &cfg_dflt,
    cfg_fields
  },
  open_fs
};
