// Copyright (C) 2003  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.
#ifndef DLIB_DIR_NAV_KERNEL_1_CPp_
#define DLIB_DIR_NAV_KERNEL_1_CPp_
#include "../platform.h"

#ifdef WIN32

#include "dir_nav_kernel_1.h"
#include "../string.h"


#ifdef __BORLANDC__
// Apparently the borland compiler doesn't define this.
#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
#endif

namespace dlib
{

// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
    // file object implementation
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

    file::
    file (
        const std::string& name
    )
    {
        using namespace std;
        state = new data;
        state->count = 1;


        char buf[3000];
        char* str;
        if (GetFullPathNameA(name.c_str(),sizeof(buf),buf,&str) == 0)
        {
            // the file was not found
            throw file_not_found("Unable to find file " + name);
        }
        state->full_name = buf;
        

        string::size_type pos = state->full_name.find_last_of(directory::get_separator());
        if (pos == string::npos)
        {
            // no valid full path has no separtor characters.  
            throw file_not_found("Unable to find file " + name);
        }
        state->name = state->full_name.substr(pos+1);


        // now find the size of this file
        WIN32_FIND_DATAA data;
        HANDLE ffind = FindFirstFileA(state->full_name.c_str(), &data);
        if (ffind == INVALID_HANDLE_VALUE ||
            (data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) != 0)
        {
            throw file_not_found("Unable to find file " + name);                
        }
        else
        {
            uint64 temp = data.nFileSizeHigh;            
            temp <<= 32;
            temp |= data.nFileSizeLow;
            state->file_size = temp;
            FindClose(ffind);
        } 

    }

// ----------------------------------------------------------------------------------------

    bool file::
    operator == (
        const file& rhs
    ) const 
    { 
        using namespace std;

        if (state->full_name.size() != rhs.state->full_name.size())
            return false;
        
        // compare the strings but ignore the case because file names
        // are not case sensitive on windows
        return tolower(state->full_name) == tolower(rhs.state->full_name);
    }

// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
    // directory object implementation
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

    directory::
    directory (
        const std::string& name
    )
    {
        using namespace std;

        state = new data;
        state->count = 1;        
        
        char buf[3000];
        char* str;
        if (GetFullPathNameA(name.c_str(),sizeof(buf),buf,&str) == 0)
        {
            // the directory was not found
            throw dir_not_found("Unable to find directory " + name);
        }
        state->full_name = buf;
  
        
        const char sep = get_separator();
        if (is_root_path(state->full_name) == false)
        {
            // ensure that thre is not a trialing separator
            if (state->full_name[state->full_name.size()-1] == sep)
                state->full_name.erase(state->full_name.size()-1);

            // pick out the directory name
            string::size_type pos = state->full_name.find_last_of(sep);
            state->name = state->full_name.substr(pos+1);
        }
        else
        {
            // ensure that there is a trailing separator
            if (state->full_name[state->full_name.size()-1] != sep)
                state->full_name += sep;
        }


        // now check that this is actually a valid directory
        DWORD attribs = GetFileAttributesA(state->full_name.c_str());
        if (attribs == INVALID_FILE_ATTRIBUTES ||
            (attribs&FILE_ATTRIBUTE_DIRECTORY) == 0)
        {
            // the directory was not found
            throw dir_not_found("Unable to find directory " + name);
        }

    }

// ----------------------------------------------------------------------------------------

    char directory::
    get_separator (
    ) 
    {
        return '\\';
    }

// ----------------------------------------------------------------------------------------

    bool directory::
    operator == (
        const directory& rhs
    ) const 
    { 
        using namespace std;

        if (state->full_name.size() != rhs.state->full_name.size())
            return false;

        // compare the strings but ignore the case because file names
        // are not case sensitive on windows
        return tolower(state->full_name) == tolower(rhs.state->full_name);
    }

// ----------------------------------------------------------------------------------------

    const directory directory::
    get_parent (
    ) const
    {
        using namespace std;
        // if *this is the root then just return *this
        if (is_root())
        {
            return *this;
        }
        else
        {
            directory temp;

            const char sep = get_separator();

            string::size_type pos = state->full_name.find_last_of(sep);
            temp.state->full_name = state->full_name.substr(0,pos);

            if ( is_root_path(temp.state->full_name))
            {
                temp.state->full_name += sep;
            }
            else
            {
                pos = temp.state->full_name.find_last_of(sep);
                if (pos != string::npos)
                {
                    temp.state->name = temp.state->full_name.substr(pos+1);
                }
                else
                {
                    temp.state->full_name += sep;
                }
            }
            return temp;
        }
    }

// ----------------------------------------------------------------------------------------

    bool directory::
    is_root_path (
        const std::string& path
    ) const
    {
        using namespace std;
        const char sep = get_separator();
        bool root_path = false;
        if (path.size() > 2 && path[0] == sep && path[1] == sep)
        {
            // in this case this is a windows share path
            string::size_type pos = path.find_first_of(sep,2);
            if (pos != string::npos)
            {                
                pos = path.find_first_of(sep,pos+1);

                if (pos == string::npos && path[path.size()-1] != sep)
                    root_path = true;
                else if (pos == path.size()-1)
                    root_path = true;
            }

        }
        else if ( (path.size() == 2 || path.size() == 3) && path[1] == ':')
        {
            // if this is a valid windows path then it must be a root path
            root_path = true;
        }

        return root_path;
    }

// ----------------------------------------------------------------------------------------

}

#endif // WIN32

#endif // DLIB_DIR_NAV_KERNEL_1_CPp_

