/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * The contents of this file are subject to the Netscape Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/NPL/
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code is Mozilla Communicator client code, 
 * released March 31, 1998. 
 *
 * The Initial Developer of the Original Code is Netscape Communications 
 * Corporation.  Portions created by Netscape are
 * Copyright (C) 1998-1999 Netscape Communications Corporation. All
 * Rights Reserved.
 *
 * Contributor(s): 
 *     Doug Turner <dougt@netscape.com>
 */


#include "nsCOMPtr.h"
#include "nsIAllocator.h"

#include "nsIFileImplWin.h"
#include "nsFileUtils.h"

#include "prtypes.h"
#include "prio.h"

#include <direct.h>
#include "windows.h"

// For older version (<6.0) of the VC Compiler
#if (_MSC_VER == 1100)
#define INITGUID
#include "objbase.h"
DEFINE_OLEGUID(IID_IPersistFile, 0x0000010BL, 0, 0);
#endif

#include "shlobj.h"
#include "shellapi.h"
#include "shlguid.h"


nsIFileImpl::nsIFileImpl()
{
    NS_INIT_REFCNT();
    CoInitialize(NULL);  // FIX: we should probably move somewhere higher up during startup
    makeDirty();
}

nsIFileImpl::~nsIFileImpl()
{
    CoUninitialize();
}

/* nsISupports interface implementation. */
NS_IMPL_ISUPPORTS1(nsIFileImpl, nsIFile)

NS_METHOD
nsIFileImpl::Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
{
    NS_ENSURE_ARG_POINTER(aInstancePtr);
    NS_ENSURE_PROPER_AGGREGATION(outer, aIID);

    nsIFileImpl* inst = new nsIFileImpl();
    if (inst == NULL)
        return NS_ERROR_OUT_OF_MEMORY;
    
    nsresult rv = inst->QueryInterface(aIID, aInstancePtr);
    if (NS_FAILED(rv))
    {
        delete inst;
        return rv;
    }
    return NS_OK;
}


// This function resets any cached information about the file.
void
nsIFileImpl::makeDirty()
{
    mResolutionDirty = PR_TRUE;
    mStatDirty       = PR_TRUE;
}

//----------------------------------------------------------------------------------------
//
// resolveWorkingPath
//  this function will walk the native path of |this| resolving any symbolic
//  links found.  The new resulting path will be placed into mResolvedPath 
//  and mResolutionDirty will be set to true.
//----------------------------------------------------------------------------------------

nsresult 
nsIFileImpl::resolveWorkingPath()
{
    if (!mResolutionDirty)
        return NS_OK;
    
    nsresult rv = NS_OK;
    HRESULT hres; 
    IShellLink* psl; 
    
    // Get the native path for |this|
    
    char* filePath = (char*) nsAllocator::Clone( mWorkingPath, strlen(mWorkingPath)+1 );

    if (filePath == nsnull)
        return NS_ERROR_NULL_POINTER;

    // Get a pointer to the IShellLink interface. 
    hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void**)&psl); 
    if (SUCCEEDED(hres)) 
    { 
        IPersistFile* ppf; 
        
        // Get a pointer to the IPersistFile interface. 
        hres = psl->QueryInterface(IID_IPersistFile, (void**)&ppf); 
        
        if (SUCCEEDED(hres)) 
        {
            char* slash = strchr(filePath, '\\');
            
            if (slash == nsnull)
                return NS_ERROR_NULL_POINTER;

            // skip the first '\\'
            slash = strchr(++slash, '\\');
            
            while (slash)
            {
                *slash = '\0';
                
                WORD wsz[MAX_PATH]; 

                if (strstr(filePath, ".lnk") == 0)
                {
                    char linkStr[MAX_PATH];
                    strcpy(linkStr, filePath);
                    strcat(linkStr, ".lnk");

                    // Ensure that the string is Unicode. 
                    MultiByteToWideChar(CP_ACP, 0, linkStr, -1, wsz, MAX_PATH); 
                }
                else
                {
                    // Ensure that the string is Unicode. 
                    MultiByteToWideChar(CP_ACP, 0, filePath, -1, wsz, MAX_PATH); 
                }
                
                // Load the shortcut. 
                hres = ppf->Load(wsz, STGM_READ); 
                if (SUCCEEDED(hres)) 
                {
                    // Resolve the link. 
                    hres = psl->Resolve(nsnull, SLR_NO_UI ); 
                    if (SUCCEEDED(hres)) 
                    { 
                        char szGotPath[MAX_PATH]; 
                        WIN32_FIND_DATA wfd; 

                        // Get the path to the link target. 
                        hres = psl->GetPath( szGotPath, MAX_PATH, &wfd, SLGP_UNCPRIORITY ); 

                        if (SUCCEEDED(hres))
                        {
                            char *temp = (char*) nsAllocator::Alloc( MAX_PATH );
                            char *carot;

                            strcpy(temp, szGotPath);
                            strcat(temp, "\\");
                            
                            // save where we left off.
                            carot = (temp + strlen(temp) -1 );

                            // append all the stuff that we have not done.
                            strcat(temp, ++slash);
                            
                            nsAllocator::Free(filePath);

                            filePath = temp;
                            slash = carot;
                        }
                    } 
                }
                *slash = '\\';
                ++slash;
                slash = strchr(slash, '\\');
                
                rv = NS_OK;
            }
            
            // Release the pointer to the IPersistFile interface. 
            ppf->Release(); 
        }
        // Release the pointer to the IShellLink interface. 
        psl->Release();
    }
    
    mResolvedPath.SetString(filePath);
    
    if(filePath)
        nsAllocator::Free(filePath);
    
    return rv;
}

nsresult
nsIFileImpl::bufferedStat(struct stat *st)
{
    if (!mStatDirty)
    {
        *st = mBuffered_st;
        return mBuffered_stat_rv;
    }
    
    nsresult rv = resolveWorkingPath();
    
    if (NS_FAILED(rv))
        return rv;

    const char *filePath = mResolvedPath.GetBuffer();

    mBuffered_stat_rv = stat(filePath, &mBuffered_st);
    *st = mBuffered_st;
    return rv;
}

NS_IMETHODIMP  
nsIFileImpl::InitWithKey(const char *fileKey)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP  
nsIFileImpl::InitWithFile(nsIFile *file)
{
    NS_ENSURE_ARG(file);

    // TODO:  how do we get to the |file|'s working    XXXXXX
    //        directory so that we do not expose
    //        symlinked directories.

    char* aFilePath;
    file->GetPath(nsIFile::NATIVE_PATH, &aFilePath);

    if (aFilePath == nsnull)
        return NS_ERROR_FILE_UNRECONGNIZED_PATH;

    mWorkingPath.SetString(aFilePath);

    nsAllocator::Free(aFilePath);
    
    makeDirty();

    return NS_OK;
}

NS_IMETHODIMP  
nsIFileImpl::InitWithPath(PRUint32 pathType, const char *filePath)
{
    NS_ENSURE_ARG(filePath);

    makeDirty();

    char* nativeFilePath = nsnull;
    char* temp;

    if (pathType == nsIFile::UNIX_PATH)
    {
        if (*filePath != '/')
	        return NS_ERROR_FILE_INVALID_PATH;

        // Since it was an absolute path, check for the drive letter
        // format looks like:  /d|/
        //                     ^
        //                     |
        //                    filePath
		char* colonPointer = ((char*)(filePath)) + 2;  // this case is okay since I am not changing
		if (strstr(filePath, "|/") == colonPointer)    // filePath
        {
            // allocate new string
	        nativeFilePath = (char*) nsAllocator::Clone( filePath+1, strlen(filePath+1)+1 );
            nativeFilePath[1] = ':';
        }
        else
        {
            // there is not a drive letter specifier.  we need to preserve the first slash.
            nativeFilePath = (char*) nsAllocator::Clone( filePath, strlen(filePath)+1 );
        }
    
        // Convert '/' to '\'.
        temp = nativeFilePath;
        do
        {
            if (*temp == '/')
                *temp = '\\';
        }
        while (*temp++);
    }
    else if (pathType == nsIFile::NATIVE_PATH  || pathType == nsIFile::NSPR_PATH)  //TODO does NSPR take any other kind of path?
    {
        // just do a sanity check.  if it has any forward slashes, it is not a Native path
        // on windows.  Also, it must have a colon at after the first char.

        if ((strchr(filePath, '/') == 0) && filePath[1] == ':')
        {
            // This is a native path
            nativeFilePath = (char*) nsAllocator::Clone( filePath, strlen(filePath)+1 );
        }
    }

    // kill any trailing seperator
    if(nativeFilePath)
    {
        temp = nativeFilePath;
        int len = strlen(temp) - 1;
        if(temp[len] == '\\')
            temp[len] = '\0';
    }

    if (nativeFilePath == nsnull)
        return NS_ERROR_FILE_UNRECONGNIZED_PATH;
    
    mWorkingPath.SetString(nativeFilePath);
    
    nsAllocator::Free( nativeFilePath );

    return NS_OK;
}


NS_IMETHODIMP  
nsIFileImpl::Create(PRUint32 type, PRUint32 attributes)
{
    nsresult rv = resolveWorkingPath();

    if (NS_FAILED(rv))
        return rv;
        
    if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
        return NS_ERROR_FILE_UNKNOWN_TYPE;


   // create nested directories to target
    char* slash = strchr(mResolvedPath, '\\');
    // skip the first '\\'
    ++slash;
    slash = strchr(slash, '\\');

    while (slash)
    {
        *slash = '\0';

        int result = mkdir(mResolvedPath);  // todo: pass back the result

        *slash = '\\';
        ++slash;
        slash = strchr(slash, '\\');
    }


    if (type == NORMAL_FILE_TYPE)
    {
        PRFileDesc* file = PR_Open(mResolvedPath, PR_RDONLY | PR_CREATE_FILE | PR_APPEND, attributes);
        if (file) PR_Close(file);
        return NS_OK;
    }

    if (type == DIRECTORY_TYPE)
    {
	    int result = mkdir(mResolvedPath);  // todo: pass back the result
        return NS_OK;
    }

    return NS_ERROR_FILE_UNKNOWN_TYPE;
}
    
NS_IMETHODIMP  
nsIFileImpl::AppendPath(const char *node)
{
    if ( (node == nsnull) || (*node == '/') || strchr(node, '\\') )
        return NS_ERROR_FILE_UNRECONGNIZED_PATH;

    makeDirty();

    // We only can append relative unix styles strings.

    // Convert '\' to '/'
	nsString path(node);

    char* nodeCString = path.ToNewCString();    
    char* temp = nodeCString;
    for (; *temp; temp++)
    {
        if (*temp == '/')
            *temp = '\\';
    }

    // kill any trailing seperator
    if(nodeCString)
    {
        temp = nodeCString;
        int len = strlen(temp) - 1;
        if(temp[len] == '\\')
            temp[len] = '\0';
    }
    
    mWorkingPath.Append("\\");
    mWorkingPath.Append(nodeCString);
    
    Recycle(nodeCString);

    return NS_OK;
}

NS_IMETHODIMP  
nsIFileImpl::GetFileName(char * *aFileName)
{
    NS_ENSURE_ARG_POINTER(aFileName);

    const char* temp = mWorkingPath.GetBuffer();
    if(temp == nsnull)
        return NS_ERROR_FILE_UNRECONGNIZED_PATH;

    const char* leaf = strrchr(temp, '\\');
    
    // if the working path is just a node without any lashes.
    if (leaf == nsnull)
        leaf = temp;
    else
        leaf++;

    *aFileName = (char*) nsAllocator::Clone(leaf, strlen(leaf)+1);
    return NS_OK;
}


NS_IMETHODIMP  
nsIFileImpl::GetPath(PRUint32 pathType, char **_retval)
{
    NS_ENSURE_ARG_POINTER(_retval);
    *_retval = nsnull;

    nsresult rv = resolveWorkingPath();
    
    if (NS_FAILED(rv)) return rv;

    const char *filePath = mResolvedPath.GetBuffer();
    char* canonicalPath = (char*) nsAllocator::Clone(filePath, strlen(filePath)+1);
    
    if (canonicalPath == nsnull)
        return NS_ERROR_NULL_POINTER;

    if (pathType == nsIFile::UNIX_PATH)
    {
        // Convert the drive-letter separator, if present
	    nsString path;
        path.SetString("/");

	    char* cp = (char*)canonicalPath + 1;
	
        if (strstr(cp, ":\\") == cp)
		    *cp = '|';    // absolute path
        else
            path.SetString("\0"); // relative path
	
	    // Convert '\' to '/'
	    for (; *cp; cp++)
        {
            if (*cp == '\\')
                *cp = '/';
        }

        path.Append(canonicalPath);
        
        cp = path.ToNewCString();
        
        *_retval = (char*) nsAllocator::Clone(cp, strlen(cp)+1);

        delete [] cp;
    
    }
    else if (pathType == nsIFile::NATIVE_PATH  || pathType == nsIFile::NSPR_PATH)  //TODO does NSPR take any other kind of path?
    {
        *_retval = (char*) nsAllocator::Clone(canonicalPath, strlen(canonicalPath)+1);
    }
    
    nsAllocator::Free((char*)canonicalPath);

    if (*_retval == nsnull)
        return NS_ERROR_FILE_UNRECONGNIZED_PATH;

    return NS_OK;
}


nsresult
nsIFileImpl::copymove(nsIFile *newParentDir, const char *newName, PRBool followSymlinks, PRBool move)
{
    NS_ENSURE_ARG(newParentDir);
    
    PRBool exists;
    nsresult rv = IsExists(&exists);
    if (NS_FAILED(rv))
        return rv;

    if (!exists)
        return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;

    const char *filePath = mResolvedPath.GetBuffer();

    newParentDir->IsExists(&exists);
    if (exists == PR_FALSE)
    {
        rv = newParentDir->Create(DIRECTORY_TYPE, 0644);
        if (NS_FAILED(rv))
            return rv;
    }
    else
    {
        PRBool isDir;
        newParentDir->IsDirectory(&isDir);
        if (isDir == PR_FALSE)
            return NS_ERROR_FILE_DESTINATION_NOT_DIR;
    }        

    // get the path that we are going to copy to.
    char* inFilePath;
    newParentDir->GetPath(nsIFile::NATIVE_PATH, &inFilePath);  //todo this needs to return a resolved path?
    nsCString inFileCString = inFilePath;
    nsAllocator::Free(inFilePath);

    inFileCString.Append("\\");

    if (newName == nsnull)
    {
        char *aFileName;
        GetFileName(&aFileName);
        inFileCString.Append(aFileName);
        nsAllocator::Free(aFileName);
    }
    else
    {
        inFileCString.Append(newName);
    }
    
    // check to see if we are a directory, if so enumerate it.
    PRBool isDir;
    IsDirectory(&isDir);
    if (isDir)
    {
        nsCOMPtr<nsIDirectoryEnumerator> iterator;
        NS_NewDirectoryEnumerator(this, followSymlinks, getter_AddRefs(iterator));
            
        PRBool more;
        iterator->HasMoreElements(&more);
        while (more)
        {
            nsCOMPtr<nsISupports> item;
            nsCOMPtr<nsIFile> file;
            iterator->GetNext(getter_AddRefs(item));
            file = do_QueryInterface(item);

            char* filePath;
            file->GetPath(nsIFile::NATIVE_PATH, &filePath);
            
            
            int copyOK;
            
            if (!move)
                copyOK = CopyFile(filePath, inFileCString, PR_TRUE);
            else
                copyOK = MoveFile(filePath, inFileCString);

            nsAllocator::Free(filePath);

            // CopyFile returns non-zero if succeeds
            if (!copyOK)
                return NS_ERROR_FILE_COPY_OR_MOVE_FAILED;;
        
            iterator->HasMoreElements(&more);
        }
    }
    else
    {
        char* filePath;
        newParentDir->GetPath(nsIFile::NATIVE_PATH, &filePath);

        int copyOK;
        
        if (!move)
            copyOK = CopyFile(filePath, inFileCString, PR_TRUE);
        else
            copyOK = MoveFile(filePath, inFileCString);
    
        nsAllocator::Free(filePath);

        // CopyFile returns non-zero if succeeds
        if (!copyOK)
            return NS_ERROR_FILE_COPY_OR_MOVE_FAILED;;
    }

    // If we moved, we want to adjust this.
    if (move)
    {
        if (newName == nsnull)
        {
            char *aFileName;
            GetFileName(&aFileName);
            InitWithFile(newParentDir);
            AppendPath(aFileName); 
            nsAllocator::Free(aFileName);
        }
        else
        {
            InitWithFile(newParentDir);
            AppendPath(newName);
        }
        makeDirty();
    }
        
    return NS_OK;
}

NS_IMETHODIMP  
nsIFileImpl::CopyTo(nsIFile *newParentDir, const char *newName)
{
    return copymove(newParentDir, newName, PR_FALSE, PR_FALSE);
}

NS_IMETHODIMP  
nsIFileImpl::CopyToFollowingLinks(nsIFile *newParentDir, const char *newName)
{
    return copymove(newParentDir, newName, PR_TRUE, PR_FALSE);
}

NS_IMETHODIMP  
nsIFileImpl::MoveTo(nsIFile *newParentDir, const char *newName)
{
    return copymove(newParentDir, newName, PR_FALSE, PR_TRUE);
}

NS_IMETHODIMP  
nsIFileImpl::MoveToFollowingLinks(nsIFile *newParentDir, const char *newName)
{
    return copymove(newParentDir, newName, PR_TRUE, PR_TRUE);
}

NS_IMETHODIMP  
nsIFileImpl::Execute(const char *args)
{
    PRBool isFile;
    nsresult rv = IsFile(&isFile);

    if (NS_FAILED(rv))
        return rv;

    nsCString fileNameWithArgs(mResolvedPath);

    if(args)
    {
        fileNameWithArgs.Append(" ");
        fileNameWithArgs.Append(args);
    }

    int execResult = WinExec( fileNameWithArgs, SW_NORMAL );     
    if (execResult > 31)
        return NS_OK;

    return NS_ERROR_FILE_EXECUTION_FAILED;
}

NS_IMETHODIMP  
nsIFileImpl::Delete(PRBool recursive)
{
    makeDirty();
    
    PRBool isDir;
    
    nsresult rv = IsDirectory(&isDir);
    if (NS_FAILED(rv))
        return rv;

    const char *filePath = mResolvedPath.GetBuffer();

    if (isDir)
    {
        if (recursive)
        {
            nsCOMPtr<nsIDirectoryEnumerator> iterator;
            NS_NewDirectoryEnumerator(this, false, getter_AddRefs(iterator));
            
            PRBool more;
            iterator->HasMoreElements(&more);
            while (more)
            {
                nsCOMPtr<nsISupports> item;
                nsCOMPtr<nsIFile> file;
                iterator->GetNext(getter_AddRefs(item));
                file = do_QueryInterface(item);
    
                file->Delete(recursive);
                
                iterator->HasMoreElements(&more);
            }
        }
        rmdir(filePath);  // todo: save return value?
    }
    else
    {
        remove(filePath); // todo: save return value?
    }
    
    return NS_OK;
}

NS_IMETHODIMP  
nsIFileImpl::GetLastModificationDate(PRUint32 *aLastModificationDate)
{
    NS_ENSURE_ARG(aLastModificationDate);
    return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP  
nsIFileImpl::SetLastModificationDate(PRUint32 aLastModificationDate)
{
    makeDirty();
    return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP  
nsIFileImpl::GetLastModificationDateOfLink(PRUint32 *aLastModificationDateOfLink)
{
    NS_ENSURE_ARG(aLastModificationDateOfLink);
    return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP  
nsIFileImpl::SetLastModificationDateOfLink(PRUint32 aLastModificationDateOfLink)
{
    makeDirty();
    return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP  
nsIFileImpl::GetPermissions(PRUint32 *aPermissions)
{
    NS_ENSURE_ARG(aPermissions);
    return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP  
nsIFileImpl::GetPermissionsOfLink(PRUint32 *aPermissionsOfLink)
{
    NS_ENSURE_ARG(aPermissionsOfLink);
    return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP  
nsIFileImpl::GetFileSize(PRUint32 *aFileSize)
{
    NS_ENSURE_ARG(aFileSize);
    
    *aFileSize = 0;

    struct stat st;
    nsresult rv = bufferedStat( &st );
    
    if (NS_FAILED(rv))
        return rv;
    
    if (0 == mBuffered_stat_rv)
        *aFileSize = (PRUint32)st.st_size;

    return NS_OK;
}

NS_IMETHODIMP  
nsIFileImpl::GetFileSizeOfLink(PRUint32 *aFileSizeOfLink)
{
    /* Link files on windows are not auto resolved. */
    return GetFileSize(aFileSizeOfLink);
}

NS_IMETHODIMP  
nsIFileImpl::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable)
{
    NS_ENSURE_ARG(aDiskSpaceAvailable);
    
    nsresult rv = resolveWorkingPath();
    
    if (NS_FAILED(rv))
        return rv;

    const char *filePath = mResolvedPath.GetBuffer();

    PRInt64 int64;
    
    LL_I2L(int64 , LONG_MAX);

    char aDrive[_MAX_DRIVE + 2];
	_splitpath( (const char*)filePath, aDrive, NULL, NULL, NULL);
	strcat(aDrive, "\\");

    // Check disk space
    DWORD dwSecPerClus, dwBytesPerSec, dwFreeClus, dwTotalClus;
    ULARGE_INTEGER liFreeBytesAvailableToCaller, liTotalNumberOfBytes, liTotalNumberOfFreeBytes;
    double nBytes = 0;

    BOOL (WINAPI* getDiskFreeSpaceExA)(LPCTSTR lpDirectoryName, 
                                       PULARGE_INTEGER lpFreeBytesAvailableToCaller,
                                       PULARGE_INTEGER lpTotalNumberOfBytes,    
                                       PULARGE_INTEGER lpTotalNumberOfFreeBytes) = NULL;

    HINSTANCE hInst = LoadLibrary("KERNEL32.DLL");
    NS_ASSERTION(hInst != NULL, "COULD NOT LOAD KERNEL32.DLL");
    if (hInst != NULL)
    {
        getDiskFreeSpaceExA =  (BOOL (WINAPI*)(LPCTSTR lpDirectoryName, 
                                               PULARGE_INTEGER lpFreeBytesAvailableToCaller,
                                               PULARGE_INTEGER lpTotalNumberOfBytes,    
                                               PULARGE_INTEGER lpTotalNumberOfFreeBytes)) 
        GetProcAddress(hInst, "GetDiskFreeSpaceExA");
        FreeLibrary(hInst);
    }

    if (getDiskFreeSpaceExA && (*getDiskFreeSpaceExA)(aDrive,
                                                      &liFreeBytesAvailableToCaller, 
                                                      &liTotalNumberOfBytes,  
                                                      &liTotalNumberOfFreeBytes))
    {
        nBytes = (double)(signed __int64)liFreeBytesAvailableToCaller.QuadPart;
    }
    else if ( GetDiskFreeSpace(aDrive, &dwSecPerClus, &dwBytesPerSec, &dwFreeClus, &dwTotalClus))
    {
        nBytes = (double)dwFreeClus*(double)dwSecPerClus*(double) dwBytesPerSec;
    }
    return (PRInt64)nBytes;


}

NS_IMETHODIMP  
nsIFileImpl::GetParent(nsIFile * *aParent)
{
    NS_ENSURE_ARG_POINTER(aParent);

    nsCString parentPath = mWorkingPath;

    PRInt32 offset = parentPath.RFindChar('\\');
    if (offset == -1)
        return NS_ERROR_FILE_UNRECONGNIZED_PATH;

    parentPath.Truncate(offset);

    const char* filePath = parentPath.GetBuffer();

    nsIFileImpl* file = new nsIFileImpl();
    if (file == nsnull)
        return NS_ERROR_OUT_OF_MEMORY;
    
    file->AddRef();
    file->InitWithPath(nsIFile::NATIVE_PATH, filePath);
    *aParent = file;

    return NS_OK;
}
NS_IMETHODIMP  
nsIFileImpl::IsExists(PRBool *_retval)
{
    NS_ENSURE_ARG(_retval);
    
    struct stat st;
    nsresult rv = bufferedStat( &st );
    
    if (0 == mBuffered_stat_rv)
        *_retval = PR_TRUE;
    else
        *_retval = PR_FALSE;
    
    return rv;
}

NS_IMETHODIMP  
nsIFileImpl::IsWriteable(PRBool *_retval)
{
    NS_ENSURE_ARG(_retval);
    return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP  
nsIFileImpl::IsReadable(PRBool *_retval)
{
    NS_ENSURE_ARG(_retval);
    return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP  
nsIFileImpl::IsDirectory(PRBool *_retval)
{
    NS_ENSURE_ARG(_retval);

    struct stat st;
    nsresult rv = bufferedStat( &st );

    if (0 == mBuffered_stat_rv && (_S_IFDIR & st.st_mode))
        *_retval = PR_TRUE;
    else
        *_retval = PR_FALSE;

    return rv;
}

NS_IMETHODIMP  
nsIFileImpl::IsFile(PRBool *_retval)
{
    NS_ENSURE_ARG(_retval);
    struct stat st;
    nsresult rv = bufferedStat( &st );

    if (0 == mBuffered_stat_rv && (_S_IFREG & st.st_mode))
        *_retval = PR_TRUE;
    else
        *_retval = PR_FALSE;

    return rv;
}

NS_IMETHODIMP  
nsIFileImpl::IsHidden(PRBool *_retval)
{
    NS_ENSURE_ARG(_retval);
    nsresult rv = resolveWorkingPath();
    const char* in = mWorkingPath.GetBuffer();

    DWORD attr = GetFileAttributes(in);
    if (FILE_ATTRIBUTE_HIDDEN & attr)
        *_retval = PR_TRUE;
    else
        *_retval = PR_FALSE;

    return rv;
}

NS_IMETHODIMP  
nsIFileImpl::IsSymlink(PRBool *_retval)
{
    NS_ENSURE_ARG(_retval);

// just check for the .lnk extension

    *_retval = PR_FALSE;
    if (mWorkingPath.Find(".lnk", PR_FALSE, (mWorkingPath.Length() - 4)))
        *_retval = PR_TRUE;
    return NS_OK;
}

NS_IMETHODIMP
nsIFileImpl::IsEqual(nsIFile *inFile, PRBool *_retval)
{
    NS_ENSURE_ARG(inFile);
    NS_ENSURE_ARG(_retval);

    *_retval = PR_FALSE;

    char* inFilePath;
    inFile->GetPath(nsIFile::NATIVE_PATH, &inFilePath);
    
    char* filePath;
    GetPath(nsIFile::NATIVE_PATH, &filePath);

    if (strcmp(inFilePath, filePath) == 0)
        *_retval = PR_TRUE;
    
    nsAllocator::Free(inFilePath);
    nsAllocator::Free(filePath);

    return NS_OK;
}

NS_IMETHODIMP
nsIFileImpl::IsContainedIn(nsIFile *inFile, PRBool *_retval)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}