// 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
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

    void file::
    init (
        const std::string& name
    )
    {
        using namespace std;


        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 separator 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);

            ULARGE_INTEGER ull;
            ull.LowPart = data.ftLastWriteTime.dwLowDateTime;
            ull.HighPart = data.ftLastWriteTime.dwHighDateTime;
            std::chrono::nanoseconds epoch(100 * (ull.QuadPart - 116444736000000000));            
            state.last_modified = std::chrono::time_point<std::chrono::system_clock>(std::chrono::duration_cast<std::chrono::system_clock::duration>(epoch));
        } 

    }

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

    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
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

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

        
        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_

