# Copyright 2007 Matt Chaput. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#    1. Redistributions of source code must retain the above copyright notice,
#       this list of conditions and the following disclaimer.
#
#    2. Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY MATT CHAPUT ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
# EVENT SHALL MATT CHAPUT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and documentation are
# those of the authors and should not be interpreted as representing official
# policies, either expressed or implied, of Matt Chaput.

import os.path
import shutil
import sys
import tempfile
from contextlib import contextmanager

from whoosh.filedb.filestore import FileStorage
from whoosh.util import now, random_name


class TempDir(object):
    def __init__(self, basename="", parentdir=None, ext=".whoosh",
                 suppress=frozenset(), keepdir=False):
        self.basename = basename or random_name(8)
        self.parentdir = parentdir

        dirname = parentdir or tempfile.mkdtemp(ext, self.basename)
        self.dir = os.path.abspath(dirname)
        self.suppress = suppress
        self.keepdir = keepdir

    def __enter__(self):
        if not os.path.exists(self.dir):
            os.makedirs(self.dir)
        return self.dir

    def cleanup(self):
        pass

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.cleanup()
        if not self.keepdir:
            try:
                shutil.rmtree(self.dir)
            except OSError:
                e = sys.exc_info()[1]
                #sys.stderr.write("Can't remove temp dir: " + str(e) + "\n")
                #if exc_type is None:
                #    raise

        if exc_type is not None:
            if self.keepdir:
                sys.stderr.write("Temp dir=" + self.dir + "\n")
            if exc_type not in self.suppress:
                return False


class TempStorage(TempDir):
    def __init__(self, debug=False, **kwargs):
        TempDir.__init__(self, **kwargs)
        self._debug = debug

    def cleanup(self):
        self.store.close()

    def __enter__(self):
        dirpath = TempDir.__enter__(self)
        self.store = FileStorage(dirpath, debug=self._debug)
        return self.store


class TempIndex(TempStorage):
    def __init__(self, schema, ixname='', storage_debug=False, **kwargs):
        TempStorage.__init__(self, basename=ixname, debug=storage_debug,
                             **kwargs)
        self.schema = schema

    def __enter__(self):
        fstore = TempStorage.__enter__(self)
        return fstore.create_index(self.schema, indexname=self.basename)


def is_abstract_method(attr):
    """Returns True if the given object has __isabstractmethod__ == True.
    """

    return (hasattr(attr, "__isabstractmethod__")
            and getattr(attr, "__isabstractmethod__"))


def check_abstract_methods(base, subclass):
    """Raises AssertionError if ``subclass`` does not override a method on
    ``base`` that is marked as an abstract method.
    """

    for attrname in dir(base):
        if attrname.startswith("_"):
            continue
        attr = getattr(base, attrname)
        if is_abstract_method(attr):
            oattr = getattr(subclass, attrname)
            if is_abstract_method(oattr):
                raise Exception("%s.%s not overridden"
                                % (subclass.__name__, attrname))


@contextmanager
def timing(name=None):
    t = now()
    yield
    t = now() - t
    print("%s: %0.06f s" % (name or '', t))
