/******************************************************************************
    Project:    MicroMacro
    Author:     SolarStrike Software
    URL:        www.solarstrike.net
    License:    Modified BSD (see license.txt)
******************************************************************************/

#include "filesystem.h"
#include "strl.h"
#include "wininclude.h"

#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <fstream>


bool directoryExists(const char *path)
{
    DWORD dwAttrib = GetFileAttributes(path);

    if( (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) && (dwAttrib != INVALID_FILE_ATTRIBUTES) )
        return true;
    else
        return false;
}

bool fileExists(const char *filename)
{
    FILE *file = fopen(filename, "r");
    if( file )
    {
        fclose(file);
        return true;
    }
    else
        return false;
}

bool copyFile(const char *source, const char *dest)
{
    std::ifstream inFile(source, std::ios::binary);
    std::ofstream outFile(dest, std::ios::binary);

    bool copied = false;
    if( inFile.is_open() && outFile.is_open() )
    {
        /*std::string line;
        while( std::getline(inFile, line, '\n') )
        {
            outFile << line << '\n';
        }*/
        inFile.seekg(0, std::ios::end);
        std::streampos fileSize = inFile.tellg();
        char *contents = new char[fileSize];

        inFile.seekg(0, std::ios::beg);
        inFile.read(contents, fileSize);

        outFile.write(contents, fileSize);
        delete []contents;
        copied = true;
    }

    if( inFile.is_open() )
        inFile.close();
    if( outFile.is_open() )
        outFile.close();

    return copied;
}

std::vector<std::string> getDirectory(std::string path, std::string extension)
{
    std::vector<std::string> files;
    bool checkExt = (extension.size() > 0);
    DIR *dir = opendir(path.c_str());

    if( dir == NULL ) // If we can't open the directory,
        return files; // return early.

    dirent *dirp;
    char ext[128];
    while( (dirp = readdir(dir)) != NULL )
    {
        if( checkExt ) // Only check the extension if one is given
        {
            char *foundpos = strrchr(dirp->d_name, '.');
            if( foundpos && strlen(foundpos) > (sizeof(ext) - 1) )
            {   // Make sure the "extension" isn't ridiculous and overflows our buffer
                continue;
            }

            if( foundpos != NULL )
                strlcpy((char*)&ext, foundpos + 1, sizeof(ext) - 1);
            else
                strlcpy((char*)&ext, "", sizeof(ext) - 1);
        }

        if( strcmp(dirp->d_name, ".") != 0 &&
                strcmp(dirp->d_name, "..") != 0 )
        {
            if( !checkExt || extension.compare(ext) == 0 )
                files.push_back(std::string(dirp->d_name));
        }
    }
    closedir(dir);

    return files;
}

unsigned int filetimeDelta(FILETIME *t2, FILETIME *t1)
{
    LARGE_INTEGER tt_f;
    tt_f.LowPart = t2->dwLowDateTime - t1->dwLowDateTime;
    tt_f.HighPart = t2->dwHighDateTime - t1->dwHighDateTime;

    // Because Windows' FILETIME goes by 100ns increments,
    // convert it to seconds.
    return tt_f.QuadPart / 10000000;
}

std::string fixSlashes(std::string instr, int type)
{
    size_t i = SLASHES_TO_STANDARD;

    if( type == SLASHES_TO_STANDARD )
        i = instr.find("\\");
    else if( type == SLASHES_TO_WINDOWS )
        i = instr.find("/");

    while( i != std::string::npos )
    {
        if( type == SLASHES_TO_STANDARD ) {
            instr.replace(i, 1, "/");
            i = instr.find("\\", i + 1);
        }
        else if( type == SLASHES_TO_WINDOWS ) {
            instr.replace(i, 1, "\\");
            i = instr.find("/", i + 1);
        }
        else // Invalid conversion
            break;
    }

    return instr;
}

std::string getFileName(std::string fullpath)
{
    fullpath = fixSlashes(fullpath, SLASHES_TO_STANDARD);
    fullpath = fullpath.substr(fullpath.rfind("/") + 1);
    return fullpath;
}

std::string getFilePath(std::string fullpath, bool includeTrailingSlash)
{
    //fullpath = fixFileRelatives(fullpath); // Not necessary... call it manually if needed
    fullpath = fixSlashes(fullpath, SLASHES_TO_STANDARD);

    size_t foundpos = fullpath.rfind("/");

    if( foundpos != std::string::npos )
        fullpath = fullpath.substr(0, fullpath.rfind("/"));
    else
        fullpath = ""; // resides in "this" directory

    if( includeTrailingSlash )
        fullpath += "/";

    return fullpath;
}

std::string fixFileRelatives(std::string instr)
{
    instr = fixSlashes(instr, SLASHES_TO_STANDARD);

    /* Keep any leading ../ and ./ */
    std::string prefix;
    for(size_t i = 0; i < instr.length(); i++)
    {
        char c = instr.at(i);
        if( c == '.' || c == '/' )
        {
            prefix.append(1, c);
        }
        else
            break;
    }
    if( prefix.length() )
        instr.erase(0, prefix.length());

    /* Strip out any "../", and their previous directory (if sane) */
    size_t foundpos = instr.find("../");
    while( foundpos != std::string::npos )
    {
        size_t prevslash = instr.substr(0, foundpos - 1).rfind("/");

        if( prevslash != std::string::npos )
            instr.erase(prevslash + 1, foundpos - prevslash + 2);
        // +2 because of length of "../" - length of "/"
        else
        {
            instr.erase(foundpos - 1, 3); // 3 = length of "../"
        }

        foundpos = instr.find("../");
    }

    // now strip out any "./"
    foundpos = instr.find("./");
    while( foundpos != std::string::npos)
    {
        instr.erase(foundpos, 2); // 2, length of "./"

        foundpos = instr.find("./");
    }

    return prefix + instr;
}

std::string appendToPath(const std::string &base, const std::string &addition)
{
    std::string result = base;

    // Append '\\' (to result) if needed
    char lastChar = *result.rbegin();
    if( lastChar != '/' && lastChar != '\\' )
        result += "/";

    // Remove pre-pended '\\' (from addition) if needed
    char firstChar = *addition.begin();
    if( firstChar == '/' || firstChar == '\\' )
        result += addition.substr(1);
    else
        result += addition;

    return result;
}
