#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

#include "toba.h"
#include "runtime.h"
#include "java_io_File.h"


/* java/io/File exists0 ()Z */
Boolean exists0__tR5Qq(Object Harg1) 
{
    struct in_java_io_File *this = (struct in_java_io_File *)Harg1;

    return name_access(this->path, F_OK);
}

/* java/io/File canWrite0 ()Z */
Boolean canWrite0__guNHN(Object Harg1) 
{
    struct in_java_io_File *this = (struct in_java_io_File *)Harg1;

    return name_access(this->path, W_OK);
}

/* java/io/File canRead0 ()Z */
Boolean canRead0__HxF0z(Object Harg1) 
{
    struct in_java_io_File *this = (struct in_java_io_File *)Harg1;

    return name_access(this->path, R_OK);
}

/* java/io/File isFile0 ()Z */
Boolean isFile0__1t5K0(Object Harg1) 
{
    struct in_java_io_File *this = (struct in_java_io_File *)Harg1;
    struct stat buf;

    if(name_stat(this->path, &buf) == 0 &&
       S_ISREG(buf.st_mode))
        return JAVA_TRUE;
    else
        return JAVA_FALSE;
}

/* java/io/File isDirectory0 ()Z */
Boolean isDirectory0__W3a1O(Object Harg1) 
{
    struct in_java_io_File *this = (struct in_java_io_File *)Harg1;
    struct stat buf;

    if(name_stat(this->path, &buf) == 0 &&
       S_ISDIR(buf.st_mode))
        return JAVA_TRUE;
    else
        return JAVA_FALSE;
}

/* java/io/File lastModified0 ()J */
Long lastModified0__gGFxv(Object Harg1) 
{
    struct in_java_io_File *this = (struct in_java_io_File *)Harg1;
    struct stat buf;

    /* 
     * looks weird but this is what sun does! 
     * should throw an exception
     */
    if(name_stat(this->path, &buf) == -1)
        return 0;

    /* sun's returns time as milliseconds since epoch like this */
    return (Long)buf.st_mtime * 1000;
}

/* java/io/File length0 ()J */
Long length0__7nqWU(Object Harg1) 
{
    struct in_java_io_File *this = (struct in_java_io_File *)Harg1;
    struct stat buf;

    if(name_stat(this->path, &buf) == -1)
        return 0;

    return buf.st_size;
}

/* java/io/File mkdir0 ()Z */
Boolean mkdir0__ET4cn(Object Harg1) 
{
    struct in_java_io_File *this = (struct in_java_io_File *)Harg1;
    char *path;

    if(!this->path)
        throwNullPointerException("mkdir0");
    path = cstring(this->path);

    /* sun's uses mode 771 - but thats what umask is for */
    if(mkdir(path, 0777) == 0)
        return JAVA_TRUE;
    else
        return JAVA_FALSE;
}

/* java/io/File renameTo0 (Ljava/io/File;)Z */
Boolean renameTo0_F_5mBrA(Object Harg1, Object Harg2) 
{
    struct in_java_io_File *this = (struct in_java_io_File *)Harg1;
    struct in_java_io_File *arg2 = (struct in_java_io_File *)Harg2;
    char *path1, *path2;

    if(!this->path || !arg2->path)
        throwNullPointerException("renameTo0");
    path1 = cstring(this->path);
    path2 = cstring(arg2->path);

    if(rename(path1, path2) == 0)
        return JAVA_TRUE;
    else
        return JAVA_FALSE;
}

/* java/io/File delete0 ()Z */
Boolean delete0__MPjaS(Object Harg1) 
{
    struct in_java_io_File *this = (struct in_java_io_File *)Harg1;
    char *path;

    if(!this->path)
        throwNullPointerException("delete0");
    path = cstring(this->path);

    if(unlink(path) == 0)
        return JAVA_TRUE;
    else
        return JAVA_FALSE;
}

#define BUFLEN 4096
struct ll_node {
    struct ll_node *next;
    char *data;
};

static struct dirent *
portable_readdir_r(DIR *dir, struct dirent *dep)
/*ARGSUSED*/
{ 
#ifdef _REENTRANT
#ifdef sun
    /* sun botched readdir_r */
    return (readdir_r(dir, dep) == 0) ? 0 : dep;
#else
    return (readdir_r(dir, dep) == -1) ? 0 : dep;
#endif
#else
    return readdir(dir);
#endif
}

/* java/io/File list0 ()[Ljava/lang/String; */
Object list0__GHvG0(Object Harg1) 
{
    struct in_java_io_File *this = (struct in_java_io_File *)Harg1;
    struct ll_node *head = 0, **tailp = &head, *node;
    struct aarray *res = 0;
    struct stat stbuf;
    struct dirent de, *dep;
    DIR *dir;
    char *path;
    int old_errno, count = 0;
 
    if(!this->path)
        throwNullPointerException("list0");
    path = cstring(this->path);

    /* if not readable or not a dir sun's implementation returns a null ref */
    if(stat(path, &stbuf) == -1 || !S_ISDIR(stbuf.st_mode))
        return res;
    dir = opendir(path);
    if(!dir)
        return res;

    /* to avoid reading the dir twice we store it in a linked list */
    while((dep = portable_readdir_r(dir, &de)) != 0) {
        /* skip . and .. */
        if(strcmp(dep->d_name, ".") == 0 ||
           strcmp(dep->d_name, "..") == 0)
            continue;
 
        node = (struct ll_node *)allocate(sizeof(*node));
        node->data = allocate(strlen(dep->d_name) + 1);
        strcpy(node->data, dep->d_name);
        node->next = *tailp;
        *tailp = node;
        tailp = &node->next;
        count ++;
    }
    old_errno = errno;
    closedir(dir);
    /* XXX
    if(old_errno)
        throwIOException("readdir", old_errno);
    */

    /* we now have a list of entries - put them into a String[] */
    res = (struct aarray *)anewarray(&cl_java_lang_String.C, count);
    count = 0;
    for(; head; head = head->next) {
        res->data[count] = javastring(head->data);
        count++;
    }

    return res;
}

/* java/io/File isAbsolute ()Z */
Boolean isAbsolute__eWFSf(Object Harg1) 
{
    struct in_java_io_File *this = (struct in_java_io_File *)Harg1;
    struct in_java_lang_String *path;
    struct carray *value;
    Char separatorchar;

    if(!this->path)
        throwNullPointerException("isAbsolute");
    path = (struct in_java_lang_String *)this->path;
    if(!path->value)
        throwNullPointerException("isAbsolute");
    value = (struct carray *)path->value;

    separatorchar = this->class->V.separatorChar;
    if(value->data[0] == separatorchar)
        return JAVA_TRUE;
    else
        return JAVA_FALSE;
}

