import mailbox, os.path, re, sys
import buffer, default, window
from mode import Fundamental
from mode.mutt import MuttGrammar
from lex import Grammar, PatternRule, RegionRule, PatternMatchRule
from point import Point
from buffer import Buffer
from method import Method, Argument, arg
import util

cont_re = re.compile(r' *\n *|  +')

class LineGrammar(Grammar):
    rules = [PatternRule(name=r'data', pattern=r'^.*\n$')]

class MailMsgGrammar(Grammar):
    rules = [
        PatternRule(name=r'mail_pgp', pattern=r'^-----BEGIN PGP SIGNED MESSAGE-----\n$'),
        RegionRule(r'mail_signature', r'^-----BEGIN PGP SIGNATURE-----\n$', LineGrammar, r'^-----END PGP SIGNATURE-----\n$'),
        PatternRule(name=r'mail_header', pattern=r'^(?:From|To|Cc|Bcc|Subject|Reply-To|In-Reply-To|Delivered-To|Date):'),
        PatternRule(name=r'mail_quoteb', pattern=r'^ *(?:(?: *>){3})*(?: *>){2}.*$'),
        PatternRule(name=r'mail_quotea', pattern=r'^ *(?:(?: *>){3})*(?: *>){1}.*$'),
        PatternRule(name=r'mail_quotec', pattern=r'^ *(?:(?: *>){3})*(?: *>){3}.*$'),
        PatternRule(name=r'mail_email', pattern=r'(?:^|(?<=[ :]))<?[^<>@\n ]+@(?:[^<>@\.\n ]+\.)*[^<>@\.\n ]+>?'),
        PatternRule(name=r'mail_url', pattern=r'(?:^|(?<= ))(?:http|https|ftp|sftp|file|smtp|smtps|torrent|news|jabber|irc|telnet)://(?:[^\.\n ]+\.)*[^\.\n ]+'),
    ]

class MboxMsgBuffer(Buffer):
    btype = 'mboxmsg'
    def __init__(self, path, lineno, msg):
        Buffer.__init__(self)
        self.path   = os.path.realpath(path)
        self.base   = os.path.basename(self.path)
        self.lineno = lineno
        self.msg    = msg
    def changed(self): return False
    def readonly(self): return True
    def path_exists(self): raise Exception
    def _make_path(self, name): raise Exception
    def save(self, force=False): raise Exception("can't save an mbox message")
    def save_as(self, path): raise Exception("can't save an mbox message")
    def name(self): return 'mbox:%s:%s' % (self.base, self.lineno)
    def _get_msg_lines(self, msg):
        if msg.is_multipart():
            lines = []
            for msg2 in msg.get_payload():
                lines.extend(self._get_msg_lines(msg2))
            return lines
        elif msg.get_content_type() == 'text/plain':
            return msg.get_payload().split('\n')
        else:
            return []
    def _get_lines(self):
        d = {}
        for name in ('delivered-to', 'to', 'from', 'subject', 'date'):
            s = self.msg.get(name, '')
            d[name] = cont_re.sub(' ', s.replace('\t', '    '))
        lines = [
            'Delivered-To: ' + d['delivered-to'],
            'To: ' + d['to'],
            'From: ' + d['from'],
            'Subject: ' + d['subject'],
            'Date: ' + d['date'],
            '',
        ]
        
        lines.extend(self._get_msg_lines(self.msg))
        return lines
    def open(self):
        self.lines = self._get_lines()
    def reload(self):
        lines = self._get_lines()
        self.set_lines(lines, force=True)

class MboxListBuffer(Buffer):
    btype   = 'mboxlist'
    reverse = True
    format  = '%(pos)4d %(replied)1.1s %(month)3.3s %(day)2.2s %(fromname)-15.15s %(size)6.6s %(subject)s'

    from_re1 = re.compile(r'^ *" *([^"]+) *" *< *(.+) *> *$')
    from_re2 = re.compile(r'^ *([^" ].*?) *< *(.+) *> *$')
    from_re3 = re.compile(r'^ *([^\'" ]+) *$')
    from_re4 = re.compile(r'^ *([^\'" ]+) *\(([^\)]+)\) *$')

    date_re1 = re.compile(r'^(Sun|Mon|Tue|Wed|Thu|Fri|Sat), (\d{1,2}) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d{4}) (\d{2}):(\d{2}):(\d{2}) ([^ ]+)(?: .*)?$')
    def __init__(self, path):
        Buffer.__init__(self)
        self.path = os.path.realpath(path)
        self.base = os.path.basename(self.path)
        self.size = 0
    def changed(self): return False
    def readonly(self): return True
    def path_exists(self): raise Exception
    def _make_path(self, name): raise Exception
    def save(self, force=False): raise Exception("can't save an mbox")
    def save_as(self, path): raise Exception("can't save an mbox")
    def name(self): return 'mbox:%s' % (self.base)
    def open(self):
        self.lines = self._get_lines()
    def reload(self):
        lines = self._get_lines()
        self.set_lines(lines, force=True)

    def _parse_msg_from(self, msg):
        m = self.from_re1.match(msg['from'])
        if m: return m.groups()
        m = self.from_re2.match(msg['from'])
        if m: return m.groups()
        m = self.from_re3.match(msg['from'])
        if m: return m.group(1), m.group(1)
        m = self.from_re4.match(msg['from'])
        if m: return m.group(2), m.group(1)
        return 'unknown', 'unknown'
    def _parse_date(self, msg):
        fields = ['', '', '', '', '', '', '', '']
        m = self.date_re1.match(msg['date'])
        if m:
            fields = list(m.groups())
            fields[1] = '%02d' % int(fields[1])
        return fields

    def _create_msg_dict(self, pos, msg):
        d = {}
        for key in list(msg.keys()):
            d[key.lower()] = msg[key]

        d['fromname'], d['fromaddr'] = self._parse_msg_from(msg)
        d['dow'], d['day'], d['month'], d['year'], \
            d['hour'], d['min'], d['sec'], d['tz'] = self._parse_date(msg)

        if 'A' in msg.get_flags():
            d['replied'] = 'r'
        else:
            d['replied'] = ' '

        d['pos'] = pos
        d['flags'] = ''.join(msg.get_flags())
        d['size'] = len(str(msg))
        for key in d:
            if type(d[key]) == type(''):
                d[key] = d[key].replace('\t', '    ')
                d[key] = cont_re.sub(' ', d[key])
        return d

    def _get_lines(self):
        f = open(self.path, 'r')
        self.size = len(f.read())
        f.close()
        if sys.version_info[1] == 4:
            fp = open(self.path, 'rb')
            self.mbox = mailbox.UnixMailbox(fp)
        else:
            self.mbox = mailbox.mbox(self.path)
        lines = []
        pos = 1
        msgs = list(self.mbox)
        if self.reverse:
            msgs.reverse()
        for msg in msgs:
            d = self._create_msg_dict(pos, msg)
            s = self.format % d
            lines.append(s.rstrip())
            pos += 1
        return lines

class MboxRefresh(Method):
    def _execute(self, w, **vargs):
        w.buffer.reload()
class MboxReadMsg(Method):
    def _execute(self, w, **vargs):
        b = w.buffer
        if w.buffer.reverse:
            i = len(b.mbox) - 1 - w.cursor.y
        else:
            i = w.cursor.y
        b = MboxMsgBuffer(b.path, i, b.mbox[i])
        b.open()
        window.Window(b, w.application, height=0, width=0, mode_name='mboxmsg')
        w.application.add_buffer(b)
        w.application.switch_buffer(b)

class MboxOpenPath(Method):
    args = [arg('mbox', dt="path", p="Open Mbox: ", dv=default.path_dirname,
                ld=True, h="mbox to open")]
    def _execute(self, w, **vargs):
        path = util.expand_tilde(vargs['mbox'])
        path = os.path.abspath(os.path.realpath(path))
        b = MboxListBuffer(path)
        b.open()
        window.Window(b, w.application, height=0, width=0, mode_name='mbox')
        w.application.add_buffer(b)
        w.application.switch_buffer(b)

class MailListGrammar(Grammar):
    rules = [
        PatternMatchRule(
            r'x', r'^( *)([0-9]+)( )(.)( )(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)( +)([0-9]+)( +)(.{16})( +)([0-9]+)( +)(.+)$',
            r'spaces', r'index', r'spaces', r'flag', r'spaces', r'month',
            r'spaces', r'day', r'spaces', r'sender', r'spaces', r'size',
            r'spaces', r'subject'
        ),
    ]

class MboxMsg(Fundamental):
    name   = 'MboxMsg'
    colors = {
        'mail_pgp':             ('red', 'default', 'bold'),
        'mail_signature.start': ('red', 'default', 'bold'),
        'mail_signature.data':  ('red', 'default', 'bold'),
        'mail_signature.null':  ('red', 'default', 'bold'),
        'mail_signature.end':   ('red', 'default', 'bold'),
        'mail_header':          ('green', 'default', 'bold'),
        'mail_email':           ('cyan', 'default', 'bold'),
        'mail_url':             ('cyan', 'default', 'bold'),
        'mail_quotea':          ('yellow', 'default', 'bold'),
        'mail_quoteb':          ('cyan', 'default', 'bold'),
        'mail_quotec':          ('magenta', 'default', 'bold'),
    }
    actions  = []
    grammar = MailMsgGrammar
    def __init__(self, w):
        Fundamental.__init__(self, w)

class Mbox(Fundamental):
    name    = 'Mbox'
    grammar = MailListGrammar
    actions = [MboxRefresh, MboxOpenPath, MboxReadMsg]
    colors  = {
        'index':   ('default', 'default', 'bold'),
        'flag':    ('yellow', 'default', 'bold'),
        'month':   ('green', 'default', 'bold'),
        'dow':     ('green', 'default', 'bold'),
        'day':     ('green', 'default', 'bold'),
        'sender':  ('default', 'default', 'bold'),
        'size':    ('cyan', 'default', 'bold'),
        'subject': ('default', 'default', 'bold'),
    }
    def __init__(self, w):
        Fundamental.__init__(self, w)
        self.add_bindings('mbox-refresh', ('C-c r',))
        self.add_bindings('mbox-read-msg', ('RETURN',))

def install(*args):
    Mbox.install(*args)
    MboxMsg.install(*args)