/*
  Copyright (C) 2010-2011  Charles Pence <charles@charlespence.net>
  Copyright (C) 1996-1997  Id Software, Inc.

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "../pch.h"

#include <sys/types.h>
#include <sys/stat.h>

#include "../quakedef.h"
#include "filesystem.h"

#include "physfs.h"

namespace filesystem
{

void Init(const char *argv0)
{
    // Check the version of PhysicsFS for safety's sake
    PHYSFS_Version compiled, linked;
    
    PHYSFS_VERSION(&compiled);
    PHYSFS_getLinkedVersion(&linked);
    
    if (compiled.major != linked.major ||
        compiled.minor != linked.minor ||
        compiled.patch != linked.patch)
    {
        Con_Printf("WARNING: Using a different version of PhysicsFS than the "
            "one we were linked with, unexpected behavior may occur\n");
    }
    
    if (PHYSFS_init(argv0) == 0)
        Sys_Error("Cannot initialize PhysicsFS");
}

void Shutdown()
{
    PHYSFS_deinit();
}


bool Add(const std::string &path)
{
    // Check to make sure the directory exists (and is a directory)
    struct stat st;
    if (stat(path.c_str(), &st) != 0)
    {
        Con_Printf("ERROR: Tried to add %s to filesystem (does not exist)\n", path.c_str());
        return false;
    }
    
    if (!(st.st_mode & S_IFDIR))
    {
        Con_Printf("ERROR: Tried to add %s to filesystem, not a directory\n", path.c_str());
        return false;
    }
    
    
    // Get the initial search path
    char **initial_search_path = PHYSFS_getSearchPath();
    if (!initial_search_path)
        return false;
    
    // Remove everything from the search path, ignoring errors
    for (char **p = initial_search_path ; *p != NULL ; ++p)
        PHYSFS_removeFromSearchPath(*p);
    
    
    // Add the new directory
    if (!PHYSFS_addToSearchPath(path.c_str(), 0))
    {
        // Failure, reset search path and bail
        for (char **p = initial_search_path ; *p != NULL ; ++p)
            PHYSFS_addToSearchPath(path.c_str(), 1);
        return false;
    }
    
    
    // Search for PAK/PK3 files
    std::vector<std::string> pak_files;
    char **file_list = PHYSFS_enumerateFiles("/");
    for (char **p = file_list ; *p != NULL ; ++p)
    {
        if (PathExtension(*p) == ".pak" || PathExtension(*p) == ".pk3")
            pak_files.push_back(path + std::string(PHYSFS_getDirSeparator()) 
                + std::string(*p));
    }
    
    
    // Remove everything from the search path, once again
    for (char **p = PHYSFS_getSearchPath() ; *p != NULL ; ++p)
        PHYSFS_removeFromSearchPath(*p);
    
    
    // Add back the original state of the search path
    for (char **p = initial_search_path ; *p != NULL ; ++p)
        PHYSFS_addToSearchPath(path.c_str(), 1);
    
    // Add all the PAK files
    foreach(std::string pak, pak_files)
    {
        if (PHYSFS_addToSearchPath(pak.c_str(), 1) == 0)
            Con_SafePrintf("filesystem::Add: Failed to add %s\n", pak.c_str());
    }
    
    // And add the directory itself for loose files
    PHYSFS_addToSearchPath(path.c_str(), 1);
    
    return true;
}



std::string PathWithoutExtension(const std::string &path)
{
    size_t last_dot = path.find_last_of('.');
    if (last_dot == std::string::npos)
        return path;
    
    return path.substr(0, last_dot);
}

std::string PathExtension(const std::string &path)
{
    size_t last_dot = path.find_last_of('.');
    if (last_dot == std::string::npos)
        return std::string();
    
    return path.substr(last_dot);
}

std::string PathStem(const std::string &path)
{
    std::string no_extension = PathWithoutExtension(path);
    
    size_t last_slash = no_extension.find_last_of('/');
    if (last_slash == std::string::npos)
        return no_extension;
    
    if (last_slash == no_extension.size() - 1)
        return std::string();
    
    return no_extension.substr(last_slash + 1);
}

std::string DefaultPathExtension(const std::string &path, const std::string &extension)
{
    if (PathExtension(path).length())
        return path;
    
    std::string e(extension);
    if (e[0] != '.')
        e = "." + extension;
    
    return path + e;
}

}