#!/usr/bin/python2
# -*- coding: UTF-8 -*-
# ----------------------
# Name: tmdb_ui.py  Is a simple console user interface for the tmdb_api. The interface is used selection
#		of a movie from themoviesdb.org.
# Python Script
# Author:   dbr/Ben modified by R.D. Vaughan
# Purpose:  Console interface for selecting a movie from themoviedb.org. This interface would be invoked when
#			an exact match is not found and the invoking script has specified the "interactive = True" when
#			creating an instance of MovieDb().
#
# License:Creative Commons GNU GPL v2
# (http://creativecommons.org/licenses/GPL/2.0/)
#-------------------------------------
__title__ ="tmdb_ui - Is a simple console user interface for the tmdb_api. The interface is used selection of a movie oe person from themoviesdb.org.";
__author__="dbr/Ben modified by R.D. Vaughan"
__purpose__='''Console interface for selecting a movie from themoviedb.org. This interface would be invoked when an exact match is not found and the invoking script has specified the "interactive = True" when creating an instance of MovieDb().
'''
__version__="v0.1.5"
# 0.1.0 Initial development
# 0.1.1 Alpha Release
# 0.1.2 Release bump - no changes to this code
# 0.1.3 Release bump - no changes to this code
# 0.1.4 Release bump - no changes to this code
# 0.1.5 Modified automated selection when there was only one search result. It was causing
#       too many incorrect selections.
#       Also removed a duplicate initialization of the version number.


"""Contains included user interfaces for tmdb movie/person selection.

A UI is a callback. A class, it's __init__ function takes two arguments:

- config, which is the tmdb config dict, setup in tmdb_api.py
- log, which is tmdb's logger instance (which uses the logging module). You can
call log.info() log.warning() etc

It pass a dictionary "allElements", this is passed a list of dicts, each dict
contains at least the keys "name" (human readable movie name), and "id" (the movies/person)
ID as on themoviedb.org). for movies only if the key 'released' is included the year will be added
to the movie title like 'Avatar (2009)' all other keys will be ignored.
For example:

[{'name': u'Avatar', 'id': u'19995', 'released': '2009-12-25'},
 {'name': u'Avatar - Sequel', 'id': u'73181'}]
OR
[{'name': u'Tom Cruise', 'id': u'500'},
 {'name': u'Cruise Moylan', 'id': u'77716'}]


The "selectMovieOrPerson" method must return the appropriate dict, or it can raise
TmdbUiAbort (if the selection is aborted), TmdbMovieOrPersonNotFound (if the movie
or person cannot be found).

A simple example callback, which returns a random movie:

>>> import random
>>> from tmdb_api_ui import BaseUI
>>> class RandomUI(BaseUI):
...    def selectMovieOrPerson(self, allElements):
...            import random
...            return random.choice(allElements)

Then to use it..

>>> from tmdb_api import MovieDb
>>> t = MovieDb(custom_ui = RandomUI)
>>> random_matching_movie = t.searchTitle('Avatar')
>>> type(random_matching_movie)
[{"Avatar",'19995'}]
"""

import os, struct, sys, string
from tmdb_exceptions import TmdbUiAbort

class OutStreamEncoder(object):
    """Wraps a stream with an encoder"""
    def __init__(self, outstream, encoding=None):
        self.out = outstream
        if not encoding:
            self.encoding = sys.getfilesystemencoding()
        else:
            self.encoding = encoding

    def write(self, obj):
        """Wraps the output stream, encoding Unicode strings with the specified encoding"""
        if isinstance(obj, unicode):
            try:
                self.out.write(obj.encode(self.encoding))
            except IOError:
                pass
        else:
            try:
                self.out.write(obj)
            except IOError:
                pass

    def __getattr__(self, attr):
        """Delegate everything but write to the stream"""
        return getattr(self.out, attr)
sys.stdout = OutStreamEncoder(sys.stdout, 'utf8')
sys.stderr = OutStreamEncoder(sys.stderr, 'utf8')

# Two routines used for movie title search and matching
def is_punct_char(char):
    '''check if char is punctuation char
    return True if char is punctuation
    return False if char is not punctuation
    '''
    return char in string.punctuation

def is_not_punct_char(char):
    '''check if char is not punctuation char
    return True if char is not punctuation
    return False if chaar is punctuation
    '''
    return not is_punct_char(char)


def makeDict(movieORperson):
    '''Make a dictionary out of the chosen movie data
    return a dictionary of the movie and ancillary data
    '''
    selection = {}
    for key in movieORperson.keys():
        selection[key] = movieORperson[key]
    return selection
# end makeDict()


class BaseUI:
    """Default non-interactive UI, which auto-selects first results
    """
    def __init__(self, config, log, searchTerm=None):
        self.config = config
        self.log = log
        self.searchTerm = searchTerm

    def selectMovieOrPerson(self, allElements):
        return makeDict([allElements[0]])


class ConsoleUI(BaseUI):
    """Interactively allows the user to select a movie or person from a console based UI
    """

    def removeCommonWords(self, title):
        '''Remove common words from a title
        return title striped of common words
        '''
        if not title:
            return u' '
        wordList = [u'the ', u'a ', u'  '] # common word list. Leave double space as the last value.
        title = title.lower()
        for word in wordList:
            title = title.replace(word, u'')
        if not title:
            return u' '
        title = title.strip()
        return filter(is_not_punct_char, title)
    # end removeCommonWords()


    def _displayMovie(self, allElements):
        """Helper function, lists movies or people with corresponding ID
        """
        print u"themoviedb.org Search Results:"
        for i in range(len(allElements[:15])): # list first 15 search results
            i_show = i + 1 # Start at more human readable number 1 (not 0)
            self.log.debug('Showing allElements[%s] = %s)' % (i_show, allElements[i]))
            if allElements[i]['name'] == u'User choses to ignore video':
                print u"% 2s -> %s # %s" % (
                    i_show,
                    '99999999', "Set this video to be ignored by Jamu with a reference number of '99999999'"
                )
                continue
            if not allElements[i].has_key('released'):
            	title = allElements[i]['name']
            elif len(allElements[i]['released']) > 3:
            	title = u"%s (%s)" % (allElements[i]['name'], allElements[i]['released'][:4])
            else:
            	title = allElements[i]['name']
            if allElements[i]['url'].find('/person/') > -1:
                format = u"%2s -> %-30s # http://www.themoviedb.org/person/%s"
            else:
                format = u"%2s -> %-50s # http://www.themoviedb.org/movie/%s"
            print format % (
                i_show,
                title,
                allElements[i]['id']
            )
        print u"Direct search of themoviedb.org # http://themoviedb.org/"
	# end _displayMovie()

    def selectMovieOrPerson(self, allElements):
        if allElements[0]['url'].find('/movie/') > -1:
            morp = u'movie'
        else:
            morp = u'person'
        # Add the ability to select the skip inetref of '99999999' for movies only
        if morp == u'movie':
            allElements.append( {'id': '99999999', 'name': u'User choses to ignore video'} )

        self._displayMovie(allElements)

        refsize = 5     # The number of digits required in a TMDB number is directly entered
        if len(allElements) == 2 and morp == u'movie':
            data = makeDict(allElements[0])
            if self.removeCommonWords(data['name']) == self.removeCommonWords(self.searchTerm) and data.has_key('released'):
                # Single result, return it!
                print u"Automatically selecting only result"
                return [data]

        if len(allElements) == 1 and morp == u'person':
            data = makeDict(allElements[0])
            if self.removeCommonWords(data['name']) == self.removeCommonWords(self.searchTerm):
                # Single result, return it!
                print u"Automatically selecting only result"
                return [data]

        if self.config['select_first'] is True:
            print u"Automatically returning first search result"
            return [makeDict(allElements[0])]

        while True: # return breaks this loop
            try:
                print u'Enter choice:\n("Enter" key equals first selection (1)) or input a zero padded 5 digit %s TMDB id number, ? for help):' % morp
                ans = raw_input()
            except KeyboardInterrupt:
                raise TmdbUiAbort("User aborted (^c keyboard interupt)")
            except EOFError:
                raise TmdbUiAbort("User aborted (EOF received)")

            self.log.debug(u'Got choice of: %s' % (ans))
            try:
                if ans == '': # Enter pressed which equates to the first selection
					selected_id = 0
                else:
                    if int(ans) == 0:
                        raise ValueError
                    selected_id = int(ans) - 1 # The human entered 1 as first result, not zero
            except ValueError: # Input was not number
                if ans == "q":
                    self.log.debug(u'Got quit command (q)')
                    raise TmdbUiAbort("User aborted ('q' quit command)")
                elif ans == "?":
                    print u"## Help"
                    print u"# Enter the number that corresponds to the correct movie."
                    print u"# Paste a TMDB %s ID number (pad with leading zeros to make 5 digits) from themoviedb.org and hit 'Enter'" % morp
                    print u"# ? - this help"
                    print u"# q - abort/skip movie selection"
                else:
                	print '! Unknown/Invalid keypress "%s"\n' % (ans)
                	self.log.debug('Unknown keypress %s' % (ans))
            else:
                self.log.debug('Trying to return ID: %d' % (selected_id))
                try:
                    data = makeDict(allElements[selected_id])
                    data['userResponse'] = u'User selected'
                    return [data]
                except IndexError:
                    if len(ans) == refsize:
                        return [{'userResponse': u'User input', 'id': u'%d' % int(ans)}]
            #end try
        #end while not valid_input
	# end selectMovieOrPerson()
