/*
 * $Id: Info.xs,v 0.70 2005/08/09 15:47:00 dankogai Exp $
 */

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

static int
not_here(char *s)
{
    croak("%s not implemented on this architecture", s);
    return -1;
}

#include "common/util.c"
#include "common/macdate.c"
#include <Finder.h>

/* #define CATALOGINFONEEDED kFSCatInfoGettableInfo */
#define CATALOGINFONEEDED (kFSCatInfoNodeFlags|kFSCatInfoCreateDate|kFSCatInfoContentMod|kFSCatInfoFinderInfo)

#define NUMMEMBERSINFO 7

static SV *
xs_getfinfo(char *path){
    FSRef  Ref;
    FSSpec Spec;
    FSCatalogInfo Catalog;
    FInfo  *finfo = (FInfo *)(&Catalog.finderInfo);

    SV*   sva[NUMMEMBERSINFO];
    OSErr err;

    if (err = FSPathMakeRef(path, &Ref, NULL)){
	seterr(err);
	return &PL_sv_undef;
    }
    
    /* 
     * to make it work with both directory and file, we
     * use FSGetCatalogInfo() instead of FSGetFInfo()
     */

    if (err = FSGetCatalogInfo(&Ref,
			       CATALOGINFONEEDED,
			       &Catalog,
			       NULL,
			       NULL,
			       NULL))
    {
	seterr(err);
	return &PL_sv_undef;
    }

    sva[0] =  sv_2mortal(newSVpv((char *)&Ref, sizeof(Ref)));
    sva[1] =  sv_2mortal(newSViv(Catalog.nodeFlags));

    if (kFSNodeIsDirectoryMask & Catalog.nodeFlags){
	sva[2] =  sv_2mortal(newSVpv("", 0));
	sva[3] =  sv_2mortal(newSVpv("", 0));
    }else{
	sva[2] =  sv_2mortal(newSVpv(Catalog.finderInfo, 4));
	sva[3] =  sv_2mortal(newSVpv(Catalog.finderInfo+4, 4));
    }

    sva[4] =  sv_2mortal(newSVuv(finfo->fdFlags));

    sva[5] =  sv_2mortal(newSVnv(UDT2D(&Catalog.createDate)));
    sva[6] =  sv_2mortal(newSVnv(UDT2D(&Catalog.contentModDate)));

    return newRV_noinc((SV *)av_make(NUMMEMBERSINFO, sva));
 }

static int
xs_setfinfo(
    SV   *svref,
    unsigned int  nodeFlags,
    unsigned char *type,
    unsigned char *creator,
    unsigned int  fdFlags,
    double        ctime,
    double        mtime,
    char          *path
    )
{
    FSRef  Ref, *rp;
    FSSpec Spec;
    FSCatalogInfo Catalog;
    FInfo  *finfo = (FInfo *)(&Catalog.finderInfo);
    OSErr err;

    if (path != NULL && strlen(path) != 0){
	if (err = FSPathMakeRef(path, &Ref, NULL)){
	    return seterr(err);
	}else{
	    rp = &Ref;
	}
    }else{
	rp = (FSRef *)SvPV_nolen(svref);
    }

    /* prefetch destination catalog; may be used for file locks */
    if (err = FSGetCatalogInfo(rp,
			       kFSCatInfoSettableInfo|kFSCatInfoNodeFlags,
			       &Catalog,
			       NULL, NULL, NULL))
    {
	return seterr(err);
    }
    
    /* 
     * unlock the file first.
     * Note FSp(Rst|Set)FLock dies with segfault when applied to
     * directories! 
     */

    FSRef2FSSpec(rp, &Spec);

    if (!(Catalog.nodeFlags & kFSNodeIsDirectoryMask)){
	if (err = FSpRstFLock(&Spec)){
	    return seterr(err);
	}
    }

    /* now set Catalog */

    Catalog.nodeFlags = nodeFlags;
    finfo->fdType    = char2OSType(type);
    finfo->fdCreator = char2OSType(creator);
    finfo->fdFlags =  fdFlags;
    D2UDT(ctime, &Catalog.createDate);
    D2UDT(mtime, &Catalog.contentModDate);

    if (err = FSSetCatalogInfo(rp, CATALOGINFONEEDED, &Catalog)){
	return seterr(err);
    }

    /* Lock the File if neccesary */

    if (!(Catalog.nodeFlags & kFSNodeIsDirectoryMask)){
	if (Catalog.nodeFlags & kFSNodeLockedMask){
	    err = FSpSetFLock(&Spec);
	}
    }

    return seterr(err);
}

MODULE = MacOSX::File::Info		PACKAGE = MacOSX::File::Info	

PROTOTYPES: ENABLE

SV *
xs_getfinfo(path)
    char *path;
    CODE:
        RETVAL = xs_getfinfo(path);
    OUTPUT:
	RETVAL

int
xs_setfinfo(svref, nodeFlags, type, creator, fdFlags, ctime, mtime, path)
    SV   *svref;
    unsigned int  nodeFlags;
    unsigned char *type;
    unsigned char *creator;
    unsigned int  fdFlags;
    double        ctime;
    double        mtime;
    char          *path;
    CODE:
        RETVAL = xs_setfinfo(svref, nodeFlags, type, creator, 
			     fdFlags, ctime, mtime, path);
    OUTPUT:
        RETVAL