"""Various display related classes.

Authors : MinRK, gregcaporaso, dannystaple
"""
from os.path import exists, isfile, splitext, abspath, join, isdir
from os import walk, sep


class IFrame(object):
    """
    Generic class to embed an iframe in an IPython notebook
    """

    iframe = """
        <iframe
            width="{width}"
            height={height}"
            src="{src}{params}"
            frameborder="0"
            allowfullscreen
        ></iframe>
        """

    def __init__(self, src, width, height, **kwargs):
        self.src = src
        self.width = width
        self.height = height
        self.params = kwargs

    def _repr_html_(self):
        """return the embed iframe"""
        if self.params:
            from urllib import urlencode
            params = "?" + urlencode(self.params)
        else:
            params = ""
        return self.iframe.format(src=self.src,
                                  width=self.width,
                                  height=self.height,
                                  params=params)

class YouTubeVideo(IFrame):
    """Class for embedding a YouTube Video in an IPython session, based on its video id.

    e.g. to embed the video on this page:

    https://www.youtube.com/watch?v=foo

    you would do:

    vid = YouTubeVideo("foo")
    display(vid)

    To start from 30 seconds:

    vid = YouTubeVideo("abc", start=30)
    display(vid)

    To calculate seconds from time as hours, minutes, seconds use:
    start=int(timedelta(hours=1, minutes=46, seconds=40).total_seconds())

    Other parameters can be provided as documented at
    https://developers.google.com/youtube/player_parameters#parameter-subheader
    """

    def __init__(self, id, width=400, height=300, **kwargs):
        src = "https://www.youtube.com/embed/{0}".format(id)
        super(YouTubeVideo, self).__init__(src, width, height, **kwargs)

class VimeoVideo(IFrame):
    """
    Class for embedding a Vimeo video in an IPython session, based on its video id.
    """

    def __init__(self, id, width=400, height=300, **kwargs):
        src="https://player.vimeo.com/video/{0}".format(id)
        super(VimeoVideo, self).__init__(src, width, height, **kwargs)

class ScribdDocument(IFrame):
    """
    Class for embedding a Scribd document in an IPython session

    Use the start_page params to specify a starting point in the document
    Use the view_mode params to specify display type one off scroll | slideshow | book

    e.g to Display Wes' foundational paper about PANDAS in book mode from page 3

    ScribdDocument(71048089, width=800, height=400, start_page=3, view_mode="book")
    """

    def __init__(self, id, width=400, height=300, **kwargs):
        src="https://www.scribd.com/embeds/{0}/content".format(id)
        super(ScribdDocument, self).__init__(src, width, height, **kwargs)

class FileLink(object):
    """Class for embedding a local file link in an IPython session, based on path

    e.g. to embed a link that was generated in the IPython notebook as my/data.txt

    you would do::

        local_file = FileLink("my/data.txt")
        display(local_file)

    or in the HTML notebook, just::

        FileLink("my/data.txt")
    """

    html_link_str = "<a href='%s' target='_blank'>%s</a>"

    def __init__(self,
                 path,
                 url_prefix='files/',
                 result_html_prefix='',
                 result_html_suffix='<br>'):
        """
        Parameters
        ----------
        path : str
            path to the file or directory that should be formatted
        directory_prefix : str
            prefix to be prepended to all files to form a working link [default:
            'files']
        result_html_prefix : str
            text to append to beginning to link [default: none]
        result_html_suffix : str
            text to append at the end of link [default: '<br>']
        """
        if isdir(path):
            raise ValueError,\
             ("Cannot display a directory using FileLink. "
              "Use FileLinks to display '%s'." % path)
        self.path = path
        self.url_prefix = url_prefix
        self.result_html_prefix = result_html_prefix
        self.result_html_suffix = result_html_suffix

    def _format_path(self):
        fp = ''.join([self.url_prefix,self.path])
        return ''.join([self.result_html_prefix,
                        self.html_link_str % (fp, self.path),
                        self.result_html_suffix])

    def _repr_html_(self):
        """return html link to file
        """
        if not exists(self.path):
            return ("Path (<tt>%s</tt>) doesn't exist. "
                    "It may still be in the process of "
                    "being generated, or you may have the "
                    "incorrect path." % self.path)

        return self._format_path()

    def __repr__(self):
        """return absolute path to file
        """
        return abspath(self.path)

class FileLinks(FileLink):
    """Class for embedding local file links in an IPython session, based on path

    e.g. to embed links to files that were generated in the IPython notebook under my/data

    you would do:

    local_files = FileLinks("my/data")
    display(local_files)

    or in the HTML notebook, just

    FileLinks("my/data")

    """
    def __init__(self,
                 path,
                 url_prefix='files/',
                 included_suffixes=None,
                 result_html_prefix='',
                 result_html_suffix='<br>',
                 notebook_display_formatter=None,
                 terminal_display_formatter=None):
        """
            included_suffixes : list of filename suffixes to include when
             formatting output [default: include all files]

            See the FileLink (baseclass of LocalDirectory) docstring for
             information on additional parameters.

            notebook_display_formatter : func used to format links for display
             in the notebook. See discussion of formatter function below.

            terminal_display_formatter : func used to format links for display
             in the terminal. See discussion of formatter function below.


            Passing custom formatter functions
            ----------------------------------
             Formatter functions must be of the form:
              f(dirname, fnames, included_suffixes)
               dirname : the name of a directory (a string),
               fnames :  a list of the files in that directory
               included_suffixes : a list of the file suffixes that should be
                                   included in the output (passing None means
                                   to include all suffixes in the output in
                                   the built-in formatters)

               returns a list of lines that should will be print in the
               notebook (if passing notebook_display_formatter) or the terminal
               (if passing terminal_display_formatter). This function is iterated
               over for each directory in self.path. Default formatters are in
               place, can be passed here to support alternative formatting.

        """
        if isfile(path):
            raise ValueError,\
             ("Cannot display a file using FileLinks. "
              "Use FileLink to display '%s'." % path)
        self.included_suffixes = included_suffixes
        # remove trailing slashs for more consistent output formatting
        path = path.rstrip('/')

        self.path = path
        self.url_prefix = url_prefix
        self.result_html_prefix = result_html_prefix
        self.result_html_suffix = result_html_suffix

        self.notebook_display_formatter = \
             notebook_display_formatter or self._get_notebook_display_formatter()
        self.terminal_display_formatter = \
             terminal_display_formatter or self._get_terminal_display_formatter()

    def _get_display_formatter(self,
                               dirname_output_format,
                               fname_output_format,
                               fp_format,
                               fp_cleaner=None):
        """ generate built-in formatter function

           this is used to define both the notebook and terminal built-in
            formatters as they only differ by some wrapper text for each entry

           dirname_output_format: string to use for formatting directory
            names, dirname will be substituted for a single "%s" which
            must appear in this string
           fname_output_format: string to use for formatting file names,
            if a single "%s" appears in the string, fname will be substituted
            if two "%s" appear in the string, the path to fname will be
             substituted for the first and fname will be substituted for the
             second
           fp_format: string to use for formatting filepaths, must contain
            exactly two "%s" and the dirname will be subsituted for the first
            and fname will be substituted for the second
        """
        def f(dirname, fnames, included_suffixes=None):
            result = []
            # begin by figuring out which filenames, if any,
            # are going to be displayed
            display_fnames = []
            for fname in fnames:
                if (isfile(join(dirname,fname)) and
                       (included_suffixes == None or
                        splitext(fname)[1] in included_suffixes)):
                      display_fnames.append(fname)

            if len(display_fnames) == 0:
                # if there are no filenames to display, don't print anything
                # (not even the directory name)
                pass
            else:
                # otherwise print the formatted directory name followed by
                # the formatted filenames
                dirname_output_line = dirname_output_format % dirname
                result.append(dirname_output_line)
                for fname in display_fnames:
                    fp = fp_format % (dirname,fname)
                    if fp_cleaner is not None:
                        fp = fp_cleaner(fp)
                    try:
                        # output can include both a filepath and a filename...
                        fname_output_line = fname_output_format % (fp, fname)
                    except TypeError:
                        # ... or just a single filepath
                        fname_output_line = fname_output_format % fname
                    result.append(fname_output_line)
            return result
        return f

    def _get_notebook_display_formatter(self,
                                        spacer="&nbsp;&nbsp;"):
        """ generate function to use for notebook formatting
        """
        dirname_output_format = \
         self.result_html_prefix + "%s/" + self.result_html_suffix
        fname_output_format = \
         self.result_html_prefix + spacer + self.html_link_str + self.result_html_suffix
        fp_format = self.url_prefix + '%s/%s'
        if sep == "\\":
            # Working on a platform where the path separator is "\", so
            # must convert these to "/" for generating a URI
            def fp_cleaner(fp):
                # Replace all occurences of backslash ("\") with a forward
                # slash ("/") - this is necessary on windows when a path is
                # provided as input, but we must link to a URI
                return fp.replace('\\','/')
        else:
            fp_cleaner = None

        return self._get_display_formatter(dirname_output_format,
                                           fname_output_format,
                                           fp_format,
                                           fp_cleaner)

    def _get_terminal_display_formatter(self,
                                        spacer="  "):
        """ generate function to use for terminal formatting
        """
        dirname_output_format = "%s/"
        fname_output_format = spacer + "%s"
        fp_format = '%s/%s'

        return self._get_display_formatter(dirname_output_format,
                                           fname_output_format,
                                           fp_format)

    def _format_path(self):
        result_lines = []
        walked_dir = list(walk(self.path))
        walked_dir.sort()
        for dirname, subdirs, fnames in walked_dir:
            result_lines += self.notebook_display_formatter(dirname, fnames, self.included_suffixes)
        return '\n'.join(result_lines)

    def __repr__(self):
        """return newline-separated absolute paths
        """
        result_lines = []
        walked_dir = list(walk(self.path))
        walked_dir.sort()
        for dirname, subdirs, fnames in walked_dir:
            result_lines += self.terminal_display_formatter(dirname, fnames, self.included_suffixes)
        return '\n'.join(result_lines)
