#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
try:
    import os
    import sys
    import time
    import smtplib
    from email.message import EmailMessage
    from optparse import OptionParser, OptionGroup
    import dbm
    import hashlib
    import configparser
    import math
    # from pkg_resources import resource_filename
    # from shutil import copyfile
except ImportError:
    print('Python3 try again')
    exit()

# print (5368709120 >> 10)  # 5242880 kilo Bytes (kB)
# print (5368709120 >> 20 ) # 5120 Mega Bytes(MB)
# print (5368709120 >> 30 ) # 5 Giga Bytes(GB)

class Kindle():
    debug = False
    db = {}
    path = None
    books = []
    kindle = set()
    # extention='.mobi'

    def __init__(self):

        self.config = configparser.ConfigParser()

        cfg = os.path.expanduser('~/.kindle/smtp.ini')
        cfgdir = os.path.expanduser(os.path.dirname(cfg))
        if not os.path.isdir(cfgdir):
            os.makedirs(os.path.expanduser(cfgdir))
        if not os.path.isfile(cfg):
            tmpfile = open(os.path.expanduser(cfg), "w").write(
                """[default]
smtp=smtp-mail.outlook.com:587
username=netkiller@msn.com
password=
tls=True
"""
                )
        self.config.read(cfg)

        usage = "usage: %prog [option] your@kindle.cn"
        self.parser = OptionParser(
            usage, version="%prog 1.0.0", description='Kindle book push')

        self.parser.add_option("-p", "--path", dest='path', default='Book',
                               help="The path of library", metavar='/var/book/')
        self.parser.add_option(
            '-f', '--force', action="store_true", default=False, help="force sendmail")
        self.parser.add_option("-b", "--book", dest='book', default=None,
                               help="book path", metavar='/path/to/book.mobi')
        self.parser.add_option("-g", "--group", dest='group', default='kindle',
                               help="User group", metavar='{kindle|phone|ipad|email|other}')
        self.parser.add_option('-a', '--all', action="store_true",
                               default=False, help="Push all of books to friends")
        self.parser.add_option('-n', '--netkiller', action="store_true",
                               default=False, help="Push books to mine<netkiller@kindle.cn>")
        self.parser.add_option('-e', '--ext', dest='ext', default='.mobi',
                               help="file extention name, default: .mobi", metavar='{mobi|pdf}')

        group = OptionGroup(self.parser, 'Database', '')
        group.add_option('-l', '--library', dest='library',
                         action="store_true", default=False, help="list library")
        group.add_option('-u', '--user', action="store_true",
                         default=False, help="list kindle users")
        group.add_option('-s', '--bibliography', action="store_true",
                         default=False, help="list the user's bibliography")
        self.parser.add_option_group(group)

        group = OptionGroup(self.parser, 'Advanced', '')
        group.add_option('', '--smtp', dest='smtp', default='default',
                         help="smtp server default: msn", metavar='|'.join(self.config.sections()))
        group.add_option('', '--size', dest='size', type='int',
                         default=40, help="file size (MB)", metavar='40')
        group.add_option('-k', '--azw3', action="store_true",
                         default=False, help="azw3 file first")
        group.add_option("-o", "--offset", dest='offset',
                         default='0', help="Index offset number", metavar='10')
        group.add_option("-D", "--date", dest='date', default=None,
                         help="from date", metavar='2019-01-01')
        self.parser.add_option_group(group)

        self.parser.add_option(
            '-d', '--debug', action="store_true", default=False, help="debug mode")

        (self.options, self.args) = self.parser.parse_args()

        self.smtp = self.config.get(self.options.smtp, 'smtp')
        self.username = self.config.get(self.options.smtp, 'username')
        self.password = self.config.get(self.options.smtp, 'password')
        self.tls = self.config.get(self.options.smtp, 'tls')

    def udb(self):
        if not os.path.exists(self.group):
            os.mkdir(self.group)
        for mail in self.kindle:
            self.db[mail] = dbm.open(self.group+'/'+mail, 'c')

    def scanbook(self, path):
        with os.scandir(path) as it:
            for entry in it:
                # print(entry)
                if entry.name.startswith('.'):
                    continue
                elif entry.is_file() and entry.name.endswith(self.options.ext):
                    # print(entry.name)
                    if self.options.date:
                        stat = os.stat(entry.path)
                        timeArray = time.strptime(
                            self.options.date, "%Y-%m-%d %H:%M:%S")
                        timestamp = int(time.mktime(timeArray))
                        if stat.st_ctime < timestamp:
                            continue

                    if self.options.ext == '.mobi' and self.options.azw3 and os.path.exists(entry.path.replace('.mobi', '.azw3')):
                        print('AZW3: ' + entry.name)
                        continue

                    size = os.path.getsize(entry.path) 
                    if (size >> 20 ) > self.options.size:
                        print('SKIP: ' + entry.path +
                        ' (' + self.size(size) + ')')
                        continue

                    self.books.append(entry.path)
                elif entry.is_dir():
                    self.scanbook(entry)

    def save(self, email, filepath):
        name = os.path.basename(filepath)
        key = self.digest(name)

        if not self.options.force:
            if key in self.db[email]:
                return

        if self.options.debug:
            print('KEY: ' + name + ' = ' + key)

        print('SEND: ' + email + ' => ' + filepath +
                ' (' + self.size(os.path.getsize(filepath)) + ')')
        status = self.sendmail(email, filepath)
        if status:
            # for mail in group:
            self.db[email][key] = name
        else:
            sys.exit()

    def pushbook(self, filepath=None):
        emails = self.kindle
        if self.options.debug:
            print('SMTP: '+self.options.smtp)
            print('Library: ' + self.path)
            print('Group: ' + self.group)
            print('Kindle: ' + ','.join(self.kindle))            
            print('-----')
            print(emails)
            print('-----')

        if filepath and os.path.exists(filepath):
            for email in emails:
                self.save(email, filepath)
        else:
            skip = 0
            self.scanbook(self.path)
            for book in self.books:
                skip = skip+1
                if skip <= int(self.options.offset):
                    continue
                # print(skip)
                for email in emails:
                    self.save(email, book)

   
    def sendmail(self, to, file):

        ext = os.path.splitext(file)[1]

        if ext == '.mobi':
            # mime = mimetypes.types_map[self.options.ext]
            mime = 'application/x-mobipocket-ebook'
        elif ext == '.epub':
            mime = 'application/epub+zip '
        elif ext == '.azw3':
            mime = 'application/octet-stream'
        else:
            mime = 'application/octet-stream'

        maintype, subtype = mime.split("/", 1)

        # Send the email via our own SMTP server.
        try:
            msg = EmailMessage()
            msg['From'] = self.username
            msg['To'] = to
            msg['Subject'] = os.path.basename(file).replace(
                ' ', '').replace(self.options.ext, '')[:20]
            # msg['Date'] = email.utils.localtime()
            msg.preamble = 'kindle book'
            msg.set_content(msg['Subject'])

            if self.options.debug:
                print('MIME: ' + mime, maintype, subtype)
                print('Title: ' + msg['Subject'])
                # return False

            with open(file, 'rb') as fp:
                # msg.add_attachment(fp.read(), maintype='application', subtype='x-mobipocket-ebook', filename=os.path.basename(file))
                msg.add_attachment(fp.read(), maintype, subtype,
                                   filename=os.path.basename(file))

            with smtplib.SMTP(self.smtp) as s:
                if self.options.debug:
                    # print(msg)
                    s.set_debuglevel(1)
                if self.tls:
                    s.starttls()
                if self.password:
                    s.login(self.username, self.password)
                s.send_message(msg)
                # s.quit()
                return True
        except smtplib.SMTPException as err:
            print('SMTP: ' + str(err))
            return False
            

    def digest(self, string):
        m = hashlib.md5(string.encode(encoding='utf8'))
        return m.hexdigest()

    def library(self):
        self.scanbook(self.path)
        n = 1
        for book in self.books:
            stat = os.stat(book)
            # print(stat)
            timestamp = stat.st_ctime
            time_local = time.localtime(timestamp)
            dt = time.strftime("%Y-%m-%d %H:%M:%S", time_local)

            bookname = "%d\t%s\t%s(%s)\t%s" % (n, dt, str(stat.st_size), self.size(
                stat.st_size), book.replace(self.path, ''))

            print(bookname)

            n = n+1

    def userlist(self):
        self.friends()
        for kindle in self.kindle:
            print(kindle)

    def bibliography(self, user):
        with dbm.open(self.group+'/'+user, 'r') as db:
            n = 0
            for key in db.keys():
                n = n+1
                print(str(n) + "\t" + key.decode("utf-8") +
                      ':'+db[key].decode("utf-8"))

    def friends(self):
        with os.scandir(self.group) as users:
            for user in users:
                # print(user)
                if user.name == 'netkiller@kindle.cn.db':
                    pass
                elif user.name.endswith('@kindle.cn.db') or user.name.endswith('@kindle.com.db'):
                    self.kindle.add(user.name.replace('.db', ''))
                else:
                    self.kindle.add(user.name)
        self.kindle = sorted(self.kindle)

    def size(self, size):
        'Return the given bytes as a human friendly KB, MB, GB, or TB string'
        if (size == 0):
            return '0B'
        suffixes = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
        i = int(math.floor(math.log(size, 1024)))
        p = math.pow(1024, i)
        s = round(size/p, 2)
        return '%s %s' % (s, suffixes[i])

    def usage(self):
        self.parser.print_help()
        print("\n  Homepage: http://www.netkiller.cn Author: Neo <netkiller@msn.com>")
        sys.exit(1)

    def main(self):
        try:
            if self.options.debug:
                print("===================================")
                print(self.options, self.args)
                print("===================================")
                print(self.db)
                print("===================================")
                for item in self.config[self.options.smtp]:
                    print(item + '='+self.config.get(self.options.smtp, item))

            if self.options.path:
                self.path = self.options.path

            if self.options.group:
                self.group = os.path.expanduser('~/.kindle/')+self.options.group
                if not os.path.isdir(self.group):
                    os.makedirs(os.path.expanduser(self.group))

            if self.options.library:
                self.library()
                exit()
            elif self.options.user:
                self.userlist()
                exit()

            if self.args:
                if self.args[0] == 'netkiller@kindle.cn':
                    self.options.azw3 = True
                self.kindle = set(self.args[0].lower().split(','))
            else:
                if self.options.all:
                    self.friends()
                elif self.options.netkiller:
                    self.options.azw3 = True
                    self.kindle.add('netkiller@kindle.cn')
                else:
                    self.usage()

            if self.options.bibliography:
                self.bibliography(self.args[0])
                exit()

            if self.kindle:
                self.udb()
            else:
                self.usage()

            if self.options.book:
                self.pushbook(self.options.book)
            else:
                self.pushbook()

        except Exception as err:
            print("%s %s" % (err, self.__class__.__name__))
            sys.exit(1)


if __name__ == '__main__':
    try:
        kindle = Kindle()
        kindle.main()
    except KeyboardInterrupt:
        print("Crtl+C Pressed. Shutting down.")
