# -*- coding: utf-8 -*-

__revision__ = '$Id$'

# Copyright (c) 2005-2009 Vasco Nunes, Piotr Ożarowski

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA

# You may use and distribute this software under the terms of the
# GNU General Public License, version 2 or later

import argparse
import logging
import os
import sys
from gettext import gettext as _
from locale import getlocale
from pathlib import Path

import gutils
# import should be here so that the default is set
from gdebug import griffith_debug

log = logging.getLogger("Griffith")


def define_command_line_arguments():
    parser = argparse.ArgumentParser(
        description="Griffith's command line arguments, all are optional")

    parser.add_argument('-v', '--version', action='store_true',
                        help="prints Griffith's version")
    parser.add_argument('-D', '--debug', action='store_true',
                        help="run with more debug info")
    parser.add_argument('-C', '--clean', action='store_true',
                        help="find and delete orphan files in posters "
                             "directory")
    parser.add_argument('--check-dep', action='store_true',
                        dest='check_dep',
                        help="check dependencies")
    parser.add_argument('--show-dep', action='store_true',
                        dest='show_dep', help="show dependencies")
    parser.add_argument('--shell', action='store_true',
                        help="open interactive shell")
    parser.add_argument('--sqlecho', action='store_true',
                        help="print SQL queries")
    parser.add_argument('--home', metavar='home_dir',
                        help="set Griffith's home directory (instead of the "
                             "default ~/.griffith")
    parser.add_argument('--config', metavar='ConfigFile',
                        help="an alternative Griffith configuration file")
    parser.add_argument('-c', '--cast', metavar='Cast',
                        help="Film cast")
    parser.add_argument('-d', '--director', metavar='Director',
                        help="director of the film")
    parser.add_argument('-o', '--original_title',
                        metavar='Original_Title',
                        help="Original title of the film")
    parser.add_argument('-t', '--title', metavar='Title',
                        help="Title of the film")
    parser.add_argument('-y', '--year', metavar='Year', type=int,
                        help="Release year of the film")
    parser.add_argument('--number', metavar='#Media',
                        type=int, help="Number of media")
    parser.add_argument('-s', '--sort', metavar='Sort_Order',
                        help="Specify sort order by listing the columns of "
                             "table 'Movie' comma separated")
    parser.add_argument('--seen', action='store_true',
                        help="has the film been watched")
    parser.add_argument('--loaned', action='store_true',
                        help="is the film loaned to somebody")
    parser.add_argument('--rating', metavar='Rating',
                        type=int, help="Rating of the film")
    return parser


def check_args():
    default_lang, default_enc = getlocale()
    if not default_enc:
        default_enc = 'UTF-8'

    config_filename = '.griffith.cfg'
    home = get_griffith_home_directory_path()

    parser = define_command_line_arguments()

    args = parser.parse_args()
    if args.version:
        import version
        print(version.pversion)
        sys.exit()
    elif args.debug:
        from platform import platform
        import version
        griffith_debug.set_debug(logdir=home)
        log.debug("Starting %s %s", version.pname, version.pversion)
        log.debug("Platform: %s (%s)", platform(), os.name)
        log.debug('Dependencies:')
        show_dependencies()
    elif args.clean:
        # FIXME: the function clean_posters_dir is called as a method but
        #  defined as a function
        gutils.clean_posters_dir()  # TODO: parameter 'self' unfilled on call
        sys.exit()
    elif args.sqlecho:
        sa_log = logging.getLogger("sqlalchemy")
        sa_log.setLevel(logging.INFO)
    elif args.home:
        home = args.home
    elif args.config:
        config_filename = args.config
    elif args.check_dep:
        check_dependencies()
        sys.exit()
    elif args.show_dep:
        show_dependencies()
        sys.exit()
    # set log file directory for current mode
    griffith_debug.set_logdir(home)
    return home, config_filename


def get_griffith_home_directory_path() -> str:
    home = Path.home() / ".griffith" / ""
    return str(home)


def check_args_with_db(griffith_obj):
    # FIXME: Why are there two command parsing functions? Potentially the
    #  checks on args.clean, args.debug, args.sort, args.shell should be only
    #  in check_args
    # FIXME: Originally all the command line arguments related to a film are
    #  in 'elif' clauses which likely doesn't make much sense. Therefore
    #  they have been made 'if' statements.
    if len(sys.argv) > 1:
        where = {}
        parser = define_command_line_arguments()
        args = parser.parse_args()
        sort = args.sort
        shell = args.shell
        if args.clean:
            gutils.clean_posters_dir(griffith_obj)
            sys.exit()
        elif args.debug:
            griffith_obj.debug_mode = True
        if args.original_title:
            where['o_title'] = args.original_title
        if args.title:
            where['title'] = args.title
        if args.director:
            where['director'] = args.director
        if args.cast:
            where['cast'] = args.cast
        if args.year:
            where['year'] = str(args.year)
        if args.seen:
            where['seen'] = args.seen
        if args.loaned:
            where['loaned'] = args.loaned
        if args.number:
            where['number'] = args.number
        # if args.runtime:
        #     where['runtime'] = args.runtime
        if args.rating:
            where['rating'] = args.rating
        if where:
            con_search_movie(griffith_obj, where, sort)
        if shell:
            run_shell(griffith_obj)


def con_search_movie(self, where, sort=None):
    # for search function
    from sqlalchemy import select
    import db
    mt = db.metadata.tables['movies'].columns
    columns = (mt.number, mt.title, mt.o_title, mt.director, mt.year)

    sort_columns = []
    if sort:
        for i in sort.split(','):
            if i in db.metadata.tables['movies'].columns:
                sort_columns.append(mt[i])
    else:
        sort_columns = [mt.number]

    statement = select(*columns).order_by(*sort_columns)

    for i in where:
        if i in ('seen', 'loaned'):    # force boolean
            if where[i].lower() in ('0', 'no', 'n'):
                where[i] = False
            else:
                where[i] = True
        if i in ('year', 'number', 'runtime', 'seen', 'loaned', 'rating'):
            statement = statement.where(mt[i] == where[i])
        else:
            statement = statement.where(mt[i].like('%' + where[i] + '%'))

    movies = self.db.session.execute(statement).fetchall()
    if not movies:
        print(_("No movie found"))
    else:
        for movie in movies:
            print("\033[31;1m[%s]\033[0m\t\033[38m%s\033[0m (\033[35m%s\033[0m)"
                  ", %s - \033[32m%s\033[0m" %
                  (movie.number, movie.title, movie.o_title, movie.year,
                   movie.director))
    sys.exit()


def check_dependencies():
    os_type = None
    if sys.version.rfind('Debian'):
        os_type = 'debian'

    (missing, extra) = gutils.get_dependencies()

    def __print_missing(modules):
        missing_modules = ''
        for i in modules:
            if i['version'] is False or (not isinstance(i['version'], bool)
                                         and i['version'].startswith('-')):
                tmp = None
                if os_type is not None:
                    if os_type == 'debian' and 'debian' in i:
                        tmp = "\n%s package" % i['debian']
                        if 'debian_req' in i and i['debian_req'] is not None:
                            tmp += ("\n\tminimum required package version: %s"
                                    % i['debian_req'])
                if tmp is None:
                    tmp = "\n%s module" % i['module']
                    if 'module_req' in i and i['module_req'] is not None:
                        tmp += ("\n\tminimum required module version: %s"
                                % i['module_req'])
                    if 'url' in i:
                        tmp += "\n\tURL: %s" % i['url']
                if i['version'] is not False and i['version'].startswith('-'):
                    tmp += "\n\tavailable module version: %s" % i['version'][1:]
                if tmp is not None:
                    missing_modules += tmp
        if not missing_modules:
            return None
        else:
            return missing_modules

    tmp = __print_missing(missing)
    if tmp:
        print('Dependencies missing:')
        print('====================')
        print(tmp)
    tmp = __print_missing(extra)
    if tmp:
        print('\n\nOptional dependencies missing:')
        print('=============================')
        print(tmp, "\n")


def show_dependencies():
    (missing, extra) = gutils.get_dependencies()
    for i in missing:
        print("%(module)s :: %(version)s" % i)
    for i in extra:
        print("%(module)s :: %(version)s" % i)


def run_shell(self):
    import sqlalchemy as sa
    from sqlalchemy.orm import sessionmaker
    import db

    # settings
    banner = """
Griffith Interactive Shell


Available variables:
* sess - database session (try f.e. `sess.bind.echo = True`)
* mem_sess - in memory session (playground)
* sa - SQLAlchemy module
* orm - SQLAlchemy's orm module
* db - ORM stuff (f.e. db.Movie class)
* gsql - GriffithSQL instance


Examples:
>>> movie = sess.query(db.Movie).first()
>>> print movie.title

>>> mem_movie = mem_sess.merge(movie)
>>> mem_movie.seen = False
>>> mem_sess.add(mem_movie)
>>> mem_sess.commit()

>>> seen_movies = sess.query(db.Movie).filter_by(seen=True)
>>> for movie in seen_movies:
        print(movie.title, movie.loaned)

>>> person = sess.query(db.Person).first()
>>> print("%s has %d movies loaned" % (person.name, person.loaned_movies_count))

>>> exit()
"""

    # prepare local environment ###

    # create a playground in memory
    mem_engine = sa.create_engine('sqlite:///:memory:')
    db.metadata.create_all(bind=mem_engine)  # create tables
    mem_session = sessionmaker(bind=mem_engine)
    mem_session = mem_session()

    locs = {
        'mem_sess': mem_session,
        'meta': db.metadata,
        'db': db,
        'sa': sa,
        'orm': sa.orm,
        'gsql': self.db,
        'sess': self.db.session,
        # 'griffith': self,
        }
    # exec 'from db import *' in locs

    # prepare the shell ###
    try:
        # FIXME: this whole block likely requires some review
        # try to use IPython if possible
        try:
            # >= 0.11
            from IPython.frontend.terminal.embed import InteractiveShellEmbed
            shell = InteractiveShellEmbed(banner2=banner)
        except ImportError:
            from IPython.frontend.terminal.embed import InteractiveShellEmbed
            # < 0.11
            from IPython.Shell import IPShellEmbed
            shell = IPShellEmbed(argv=self.args)
            shell.set_banner(shell.IP.BANNER + banner)
        shell(local_ns=locs, global_ns={})
    except ImportError:
        log.debug('IPython is not available')
        import code
        shell = code.InteractiveConsole(locals=locs)
        try:
            import readline
        except ImportError:
            log.debug('readline is not available')
        shell.interact(banner)
    sys.exit(0)
