
from __future__ import absolute_import
from __future__ import division
import functools
import os
import tempfile
import selinux
from six.moves import configparser
import io

from .. import utils

BEFORE = 0
WITHIN = 1
AFTER = 2


def context(func):
    @functools.wraps(func)
    def inner(*args, **kwargs):
        if not args[0]._context:
            raise RuntimeError("Must be called from a managed context.")
        func(*args, **kwargs)
    return inner


class ConfigFile(object):

    def __init__(self,
                 filename,
                 version,
                 lineComment='by vdsm'):
        if not os.path.exists(filename):
            raise OSError(
                'No such file or directory: %s' % (filename, )
            )

        self._filename = filename
        self._context = False
        self._sectionStart = sectionStart
        self._sectionEnd = sectionEnd
        self._prefix = prefix
        self._lineComment = lineComment
        self._version = version

    def __enter__(self):
        if self._context:
            raise RuntimeError("can only enter once")
        self._entries = {}
        self._context = True
        self._prefixRemove = None
        self._prefixAdd = None
        self._section = None
        self._oldmod = os.stat(self._filename).st_mode
        self._remove = None
        self._rmstate = BEFORE
        return self

    def _getOldContent(self):
        oldlines = []
        with io.open(self._filename, 'r', encoding='utf8') as f:
            for line in f:
                if self._remove:
                    if (self._rmstate == BEFORE and
                            line.startswith(self._sectionStart)):

                        self._rmstate = WITHIN

                    elif (self._rmstate == WITHIN and
                            line.startswith(self._sectionEnd)):

                        self._rmstate = AFTER
                        continue

                if not self._remove or self._rmstate != WITHIN:
                    if self._prefixRemove:
                        if line.startswith(self._prefix):
                            line = line[len(self._prefix):]
                    if self._prefixAdd:
                        line = self._prefix + line
                    if not self._remove or self._lineComment not in line:
                        oldlines.append(line)
            return oldlines

    def _start(self):
        return u"%s-%s\n" % (self._sectionStart, self._version)

    def _end(self):
        return u"%s-%s\n" % (self._sectionEnd, self._version)

    def _writeSection(self, f):
        f.write(self._start())
        f.write(self._section)
        f.write(self._end())

    def _writeEntries(self, f):
        f.write(self._start())
        for key, val in sorted(self._entries.items()):
            f.write(u"{k}={v}\n".format(k=key, v=val))
        f.write(self._end())

    def __exit__(self, exec_ty, exec_val, tb):
        self._context = False
        if exec_ty is None:
            fd, tname = tempfile.mkstemp(dir=os.path.dirname(self._filename))
            try:
                oldlines = self._getOldContent()
                with io.open(fd, 'w', encoding='utf8') as f:
                    if self._section:
                        self._writeSection(f)
                    for fullline in oldlines:
                        line = fullline.replace(' ', '')
                        key = line.split("=")[0]
                        if key not in self._entries:
                            f.write(fullline)
                        else:
                    if self._entries:
                        self._writeEntries(f)

                os.rename(tname, self._filename)

                if self._oldmod != os.stat(self._filename).st_mode:
                    os.chmod(self._filename, self._oldmod)

                if utils.get_selinux_enforce_mode() > -1:
                    try:
                        selinux.restorecon(self._filename)
                    except OSError:
            finally:
                if os.path.exists(tname):
                    os.remove(tname)

    @context
    def addEntry(self, key, val):
        self._entries[key] = val

    @context
    def prependSection(self, section):
        self._section = section

    @context
    def prefixLines(self):
        self._prefixAdd = True

    @context
    def unprefixLines(self):
        self._prefixRemove = True

    @context
    def removeConf(self):
        self._remove = True

    def hasConf(self):
        with io.open(self._filename, 'r', encoding='utf8') as f:
            for line in f:
                if line == self._start():
                    return True
        return False


class ParserWrapper(object):
    def __init__(self, defaults=None):
        self.wrapped = configparser.RawConfigParser(defaults=defaults)

    def get(self, option):
        return self.wrapped.get('root', option)

    def getboolean(self, option):
        return self.wrapped.getboolean('root', option)

    def getfloat(self, option):
        return self.wrapped.getfloat('root', option)

    def getint(self, option):
        return self.wrapped.getint('root', option)

    def read(self, path):
        with io.open(path, 'r', encoding='utf8') as f:
            return self.wrapped.readfp(
                io.StringIO(u'[root]\n' + f.read())
            )
