# -*- coding: UTF-8 -*-
# Copyright (C) 2006-2007, 2010 J. David Ibáñez <jdavid.ibp@gmail.com>
# Copyright (C) 2007 Rob McMullen <rob.mcmullen@gmail.com>
# Copyright (C) 2010 David Versmisse <versmisse@lil.univ-littoral.fr>
# Copyright (C) 2010 Hervé Cauwelier <herve@oursours.net>
 
#
# 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/>
 
# Import from the Standard Library
from datetime import datetime
from os import listdir, makedirs, remove as os_remove, renames, walk
from os import access, R_OK, W_OK
from os.path import exists, getatime, getctime, getmtime ,getsize
from os.path import isfile, isdir, join, basename, dirname
from os.path import abspath, relpath
from shutil import rmtree, copytree, copy as shutil_copy
 
# Import from itools
from itools.uri import Path
from common import WRITE, READ_WRITE, APPEND, READ, get_mimetype
 
 
MODES = {WRITE: 'wb', READ_WRITE: 'r+b', APPEND: 'ab', READ: 'rb'}
 
 
 
class LocalFolder(object):
 
    def __init__(self, path='.'):
        if not exists(path):
            raise IOError, "No such directory: '%s'" % path
        if isfile(path):
            raise IOError, "Is a directory: '%s'" % path
        self.path = Path(abspath(path))
 
 
    def _resolve_path(self, path):
        path = self.path.resolve2(path)
        return str(path)
 
 
    #######################################################################
    # Public API
    #######################################################################
    def exists(self, path):
        path = self._resolve_path(path)
        return exists(path)
 
 
    def is_file(self, path):
        path = self._resolve_path(path)
        return isfile(path)
 
 
    def is_folder(self, path):
        path = self._resolve_path(path)
        return isdir(path)
 
 
    def can_read(self, path):
        path = self._resolve_path(path)
        return access(path, R_OK)
 
 
    def can_write(self, path):
        path = self._resolve_path(path)
        return access(path, W_OK)
 
 
    def make_file(self, path):
        path = self._resolve_path(path)
        parent_path = dirname(path)
        if exists(parent_path):
            if exists(path):
                raise OSError, "File exists: '%s'" % path
        else:
            makedirs(parent_path)
        return file(path, 'wb')
 
 
    def make_folder(self, path):
        path = self._resolve_path(path)
        return makedirs(path)
 
 
    def get_ctime(self, path):
        path = self._resolve_path(path)
        ctime = getctime(path)
        return datetime.fromtimestamp(ctime)
 
 
    def get_mtime(self, path):
        path = self._resolve_path(path)
        mtime = getmtime(path)
        return datetime.fromtimestamp(mtime)
 
 
    def get_atime(self, path):
        path = self._resolve_path(path)
        atime = getatime(path)
        return datetime.fromtimestamp(atime)
 
 
    def get_mimetype(self, path):
        path = self._resolve_path(path)
        # Not a file ?
        if not isfile(path):
            return 'application/x-not-regular-file'
        name = basename(path)
        return get_mimetype(name)
 
 
    def get_size(self, path):
        path = self._resolve_path(path)
        return getsize(path)
 
 
    def open(self, path, mode=None):
        path = self._resolve_path(path)
        if isdir(path):
            return self.__class__(path)
        mode = MODES.get(mode, 'rb')
        return file(path, mode)
 
 
    def remove(self, path):
        path = self._resolve_path(path)
        if isdir(path):
            # Remove folder contents
            rmtree(path)
        else:
            os_remove(path)
 
 
    def copy(self, source, target):
        source = self._resolve_path(source)
        target = self._resolve_path(target)
        if isdir(source):
            # Copy inside target
            if exists(target):
                target = join(target, basename(source))
            copytree(source, target)
        else:
            # Will overwrite target file
            shutil_copy(source, target)
 
 
    def move(self, source, target):
        source = self._resolve_path(source)
        target = self._resolve_path(target)
        return renames(source, target)
 
 
    def get_names(self, path='.'):
        path = self._resolve_path(path)
        try:
            return listdir(path)
        except OSError, e:
            # Path does not exist or is not a directory
            if e.errno == 2 or e.errno == 20:
                return []
            raise
 
 
    def traverse(self, path='.'):
        path = self._resolve_path(path)
        if not exists(path):
            raise IOError, "No such directory: '%s'" % path
        yield path
        if isdir(path):
            for root, folders, files in walk(path, topdown=True):
                for name in folders:
                    yield join(root, name)
                for name in files:
                    yield join(root, name)
 
 
    def get_absolute_path(self, path='.'):
        path = self._resolve_path(path)
        return abspath(path)
 
 
    def get_relative_path(self, path):
        path = self._resolve_path(path)
        return relpath(path, start=str(self.path))
 
 
    #######################################################################
    # Used by itools.handlers
    #######################################################################
    @staticmethod
    def get_basename(path):
        if type(path) is not Path:
            path = Path(path)
        return path.get_name()
 
 
    @staticmethod
    def get_path(path):
        if type(path) is not Path:
            path = Path(path)
        return str(path)
 
 
    @staticmethod
    def resolve(base, path):
        if type(base) is not Path:
            base = Path(base)
        path = base.resolve(path)
        return str(path)
 
 
    @staticmethod
    def resolve2(base, path):
        if type(base) is not Path:
            base = Path(base)
        path = base.resolve2(path)
        return str(path)
 
 
    # Resolution method for handler database keys
    normalize_key = get_absolute_path
 
 
# The entrypoint is the current working directory
lfs = LocalFolder()