# -*- coding: utf-8 -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8

"""Gestion de fichiers de formats spécifiques.

Pour le moment, seuls les fichiers textes sont supportés.
"""

__all__ = ('TextFile', )

from os import path

from .base import isstr, make_prop
from .uio import _s
from .lines import Lines

_marker = []

class TextFile(object):
    """Un fichier texte, accéder et modifié comme un ensemble de lignes.

    L'objet qui gère les lignes de ce fichier texte sont disponibles avec la
    propriété lines

    Lors de la lecture du contenu du fichier sur disque, on détermine la
    convention de fin de lignes utilisée, et on la reproduit lors de l'écriture
    sur disque.
    """

    _file, file = make_prop('_file')[:2]
    _absfile, absfile = make_prop('_absfile')[:2]
    _absfiledir, absfiledir = make_prop('_absfiledir')[:2]
    _absfilename, absfilename = make_prop('_absfilename')[:2]
    _inf, inf = make_prop('_inf')[:2]
    _lines, lines = make_prop('_lines')[:2]

    def __init__(self, file=None, raise_exception=True, readlines=True, lines=None):
        super(TextFile, self).__init__()
        if lines is not None: self._lines = lines
        if file is not None: self.open(file, raise_exception, readlines)

    def _new_Lines(self):
        """Retourner une nouvelle instance de BLines pour la lecture des lignes
        du fichier.

        Par défaut, retourner une instance de Lines, permettant de lire des
        lignes en unicode. Il faut surcharger cette méthode et retourner
        BLines() si l'on veut lire des lignes non unicode. Une alternative est
        de passer une instance de BLines() au constructeur.
        """
        return Lines()

    def open(self, file, raise_exception=True, readlines=True, lines=None):
        """Spécifier un nouveau fichier à charger, et charger ses lignes
        """
        # reset des valeurs
        self._file = None
        self._absfile = None
        self._absfiledir = None
        self._absfilename = None
        self._inf = None
        if lines is not None: self._lines = lines
        if self._lines is None: self._lines = self._new_Lines()
        # ouvrir le fichier
        if isstr(file):
            self._file = file
            self._absfile = path.abspath(self._file)
            self._absfiledir, self._absfilename = path.split(self._absfile)
        else:
            self._inf = file
        if readlines: return self.readlines(raise_exception)
        else: return None

    def close(self):
        if self._inf is not None:
            self._inf.close()
            self._inf = None

    def readlines(self, raise_exception=True, close=True, uio=None):
        """Lire les lignes du fichier.

        @return: self.lines
        """
        if self._inf is None:
            if self._absfile is None:
                raise ValueError("Should open() the file before reading")
            try:
                self._inf = open(self._absfile, 'rb')
            except IOError:
                if raise_exception: raise
        if self._inf is None :
            self._lines.reset()
        else:
            try:
                self._lines.readlines(self._inf, raise_exception=raise_exception, uio=uio)
            finally:
                if close: self.close()
        return self._lines

    def writelines(self, lines=None, outf=None, raise_exception=True, close=True, uio=None):
        """Ecrire les lignes self.lines dans le fichier.

        si lines!=None, il s'agit du nouvel ensemble de lignes.

        @return: True si l'écriture a pu se faire, False sinon.
        @rtype: bool
        """
        if lines is not None: self._lines[:] = lines
        if outf is None:
            if self._absfile is None: raise ValueError("outf is missing or open() the file before writing")
            outf = self._absfile
        if isstr(outf):
            try:
                outf = open(self._absfile, 'wb')
                close = True
            except IOError:
                if raise_exception: raise
                return False
        try:
            try:
                self._lines.writelines(outf, uio=uio)
                return True
            except IOError:
                if raise_exception: raise
                return False
        finally:
            if close: outf.close()

    def is_valid(self): return self._lines.is_valid()
    valid = property(is_valid)

    def get_nl(self): return self._lines.get_nl()
    def set_nl(self, nl): self._lines.set_nl(nl)
    nl = property(get_nl, set_nl)

    def reset(self): self._lines.reset()
    def grepi(self, pattern, indexes=None, inverse=False): return self._lines.grepi(pattern, indexes, inverse)
    def grep(self, pattern, indexes=None, inverse=False): return self._lines.grep(pattern, indexes, inverse)
    def replace(self, pattern, repl, count=0, indexes=None): return self._lines.replace(pattern, repl, count, indexes)
    def map(self, func, copy=False, **args): return self._lines.map(func, copy, **args)
    def filter(self, func, copy=False, **args): return self._lines.filter(func, copy, **args)
    def join(self): return self._lines.join()
