/*
 * Copyright 2009 Funambol, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* $Id$ */

#include "FileSystemUtils.h"

#include <string>
#include <dirent.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>

#include "Logger/Logger.h"

extern const char* __progname;

namespace NS_DM_Client
{

const char c_pathSeparator = '/';
const char* const c_pathSeparatorStr = "/";

const mode_t c_fullAccessMode = S_IRWXU | S_IRWXG | S_IRWXO; // 0777
const mode_t c_fullAccessModeExceptExec = (S_IRWXU & ~ S_IXUSR) | (S_IRWXG & ~S_IXGRP) | (S_IRWXO & ~S_IXOTH); 
const mode_t c_accessMode = c_fullAccessModeExceptExec;//c_fullAccessMode;

//------------------------------------------------------------------------------------------------------
// need sync outside
bool is_entry_dir(const char* path)
{
    bool res = false;
    struct stat st;

    memset(&st, 0, sizeof(struct stat));
    if (lstat(path, &st) == 0)
    {
        if (S_ISDIR(st.st_mode))
        {
            if (!(path[strlen(path)-1] == '.'))
            {
                res = true;
            }
        }
    }
    return res;
}

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

bool is_entry_file(const char* path)
{
    bool res = false;
    struct stat st;
    if (lstat(path, &st) == 0)
    {
        if (S_ISREG(st.st_mode))
        {
            res = true;
        }
    }
    return res;
}

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

bool DeleteDirectoryByPath(const String& path)
{
    bool res = false;
    DIR* dir_handle = opendir(path.c_str());
    if (dir_handle)
    {
        struct dirent* entry_info;
        String entry_path;
        while ( (entry_info = readdir(dir_handle)) != 0 )
        {
            entry_path = path + entry_info->d_name;
            if (is_entry_file(entry_path.c_str()))
            {
                unlink(entry_path.c_str());
            }
            else if (is_entry_dir(entry_path.c_str()))
            {
                if (entry_path[entry_path.length()-1] != c_pathSeparator)
                {
                    entry_path = entry_path + c_pathSeparatorStr;
                }
                res = DeleteDirectoryByPath(entry_path);
            }
        }
        closedir(dir_handle);
        if (rmdir(path.c_str()) == 0)
        {
            res = true;
        }
    }
    return res;
}

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

bool DeleteFilesystemEntity(const String& path)
{
    bool res = false;
    // try delete as private data
    String entity = path + ".priv";
    if (unlink(entity.c_str()) != 0)
    {
        // if failed, try delete as directory
        String full_path = path;
        if (full_path[full_path.length()-1] != c_pathSeparator)
        {
            full_path = full_path + c_pathSeparatorStr;
        }
        res = DeleteDirectoryByPath(full_path);
    }
    else
    {
        res = true;
        rmdir(path.c_str());
    }
    return res;
}

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

bool CreatePath(const char* path, NS_Logging::Logger* logger)
{
    bool res = true;

    size_t pos_beg = 0;
    size_t pos_end = 0;

    String full_path = path;
    if (full_path[full_path.length()-1] != c_pathSeparator)
    {
        full_path = full_path + c_pathSeparatorStr;
    }

    String FormedPath;

    mode_t fumask = 0777 - (c_fullAccessMode & 0777);
    umask(fumask);
    mode_t mode = c_fullAccessMode | 0777;

    int status;
    bool first_separator = true;
    
    if (logger)
	{
        LOG_DEBUG_(*logger, "CreatePath: start");
	}

    while ( (pos_end = full_path.find(c_pathSeparatorStr, pos_beg)) != String::npos )
    {
        if (first_separator)
        {
            first_separator = false;
            pos_beg = pos_end + 1;
            continue;
        }

        FormedPath = full_path.substr(0, pos_end + 1);

	if (logger)
	{
            LOG_DEBUG_(*logger, "before mkdir. Path: %s", FormedPath.c_str());
	}

	
        status = mkdir(FormedPath.c_str(), mode);
        if ( ! ((status == 0) || ((status == -1) && (errno == EEXIST)))  )
        {
		if (logger)
		{
            	LOG_DEBUG_(*logger, "mkdir failed. Path: %s, error message: %s", FormedPath.c_str(), strerror(errno));
		}

            res = false;
            break;
        }
        pos_beg = pos_end + 1;
    }

    if (logger)
	{
        LOG_DEBUG_(*logger, "CreatePath: finish. Res: %d", res);
	}


    umask(0);
    return res;
}

}
