# -*- coding: utf-8 -*-
# mnotify.py
# Copyleft (c) 2013 maker <maker@python.it>
# Released under Beerware License
#
# based on:
# anotify.py
# Copyright (c) 2012 magnific0 <jacco.geul@gmail.com>
#
# based on:
# growl.py
# Copyright (c) 2011 Sorin Ionescu <sorin.ionescu@gmail.com>


# -----------------------------------------------------------------------------
# Imports
# -----------------------------------------------------------------------------
import re
import os
import subprocess
from email.mime.text import MIMEText
#import smtplib

import weechat

SCRIPT_NAME = 'mnotify'
SCRIPT_AUTHOR = 'maker'
SCRIPT_VERSION = '0.3'
SCRIPT_LICENSE = 'Beerware License'
SCRIPT_DESC = 'Sends mail notifications upon events.'

# -----------------------------------------------------------------------------
# Settings
# -----------------------------------------------------------------------------
SETTINGS = {
    'show_public_message': 'off',
    'show_private_message': 'on',
    'show_public_action_message': 'off',
    'show_private_action_message': 'on',
    'show_notice_message': 'off',
    'show_invite_message': 'on',
    'show_highlighted_message': 'on',
    'show_server': 'on',
    'show_channel_topic': 'on',
    'show_dcc': 'on',
    'show_upgrade_ended': 'on',
    'sticky': 'off',
    'sticky_away': 'on',
    'sendmail': 'msmtp',
    'email_to': '',
    'email_from': 'irc <irc@localhost>'
}



# -----------------------------------------------------------------------------
# Globals
# -----------------------------------------------------------------------------
TAGGED_MESSAGES = {
    'public message or action': set(['irc_privmsg', 'notify_message']),
    'private message or action': set(['irc_privmsg', 'notify_private']),
    'notice message': set(['irc_notice', 'notify_private']),
    'invite message': set(['irc_invite', 'notify_highlight']),
    'channel topic': set(['irc_topic', ]),
    #'away status': set(['away_info', ]),
}


UNTAGGED_MESSAGES = {
    'away status':
        re.compile(r'^You ((\w+).){2,3}marked as being away', re.UNICODE),
    'dcc chat request':
        re.compile(r'^xfer: incoming chat request from (\w+)', re.UNICODE),
    'dcc chat closed':
        re.compile(r'^xfer: chat closed with (\w+)', re.UNICODE),
    'dcc get request':
        re.compile(
            r'^xfer: incoming file from (\w+) [^:]+: ((?:,\w|[^,])+),',
            re.UNICODE),
    'dcc get completed':
        re.compile(r'^xfer: file ([^\s]+) received from \w+: OK', re.UNICODE),
    'dcc get failed':
        re.compile(
            r'^xfer: file ([^\s]+) received from \w+: FAILED',
            re.UNICODE),
    'dcc send completed':
        re.compile(r'^xfer: file ([^\s]+) sent to \w+: OK', re.UNICODE),
    'dcc send failed':
        re.compile(r'^xfer: file ([^\s]+) sent to \w+: FAILED', re.UNICODE),
}


DISPATCH_TABLE = {
    'away status': 'set_away_status',
    'public message or action': 'notify_public_message_or_action',
    'private message or action': 'notify_private_message_or_action',
    'notice message': 'notify_notice_message',
    'invite message': 'notify_invite_message',
    'channel topic': 'notify_channel_topic',
    'dcc chat request': 'notify_dcc_chat_request',
    'dcc chat closed': 'notify_dcc_chat_closed',
    'dcc get request': 'notify_dcc_get_request',
    'dcc get completed': 'notify_dcc_get_completed',
    'dcc get failed': 'notify_dcc_get_failed',
    'dcc send completed': 'notify_dcc_send_completed',
    'dcc send failed': 'notify_dcc_send_failed',
}


STATE = {
    'icon': None,
    'is_away': False
}


# -----------------------------------------------------------------------------
# Notifiers
# -----------------------------------------------------------------------------
def cb_irc_server_connected(data, signal, signal_data):
    '''Notify when connected to IRC server.'''
    if weechat.config_get_plugin('show_server') == 'on':
        a_notify(
            'Server',
            'Server Connected',
            'Connected to network {0}.'.format(signal_data))
    return weechat.WEECHAT_RC_OK


def cb_irc_server_disconnected(data, signal, signal_data):
    '''Notify when disconnected to IRC server.'''
    if weechat.config_get_plugin('show_server') == 'on':
        a_notify(
            'Server',
            'Server Disconnected',
            'Disconnected from network {0}.'.format(signal_data))
    return weechat.WEECHAT_RC_OK


def cb_notify_upgrade_ended(data, signal, signal_data):
    '''Notify on end of WeeChat upgrade.'''
    if weechat.config_get_plugin('show_upgrade_ended') == 'on':
        a_notify(
            'WeeChat',
            'WeeChat Upgraded',
            'WeeChat has been upgraded.')
    return weechat.WEECHAT_RC_OK


def notify_highlighted_message(buffername, prefix, message):
    '''Notify on highlighted message.'''
    if weechat.config_get_plugin("show_highlighted_message") == "on":
        a_notify(
            'Highlight',
            'Highlighted on {0} by {1}'.format(buffername, prefix),
            "{0}: {1}".format(prefix, message),
        )


def notify_public_message_or_action(buffername, prefix, message, highlighted):
    '''Notify on public message or action.'''
    if prefix == ' *':
        regex = re.compile(r'^(\w+) (.+)$', re.UNICODE)
        match = regex.match(message)
        if match:
            prefix = match.group(1)
            message = match.group(2)
            notify_public_action_message(buffername, prefix,
                                         message, highlighted)
    else:
        if highlighted:
            notify_highlighted_message(buffername, prefix, message)
        elif weechat.config_get_plugin("show_public_message") == "on":
            a_notify(
                'Public',
                'Public Message on {0}'.format(buffername),
                '{0}: {1}'.format(prefix, message))


def notify_private_message_or_action(buffername, prefix, message, highlighted):
    '''Notify on private message or action.'''
    regex = re.compile(r'^CTCP_MESSAGE.+?ACTION (.+)$', re.UNICODE)
    match = regex.match(message)
    if match:
        notify_private_action_message(buffername, prefix,
                                      match.group(1), highlighted)
    else:
        if prefix == ' *':
            regex = re.compile(r'^(\w+) (.+)$', re.UNICODE)
            match = regex.match(message)
            if match:
                prefix = match.group(1)
                message = match.group(2)
                notify_private_action_message(buffername, prefix,
                                              message, highlighted)
        else:
            if highlighted:
                notify_highlighted_message(buffername, prefix, message)
            elif weechat.config_get_plugin("show_private_message") == "on":
                a_notify(
                    'Private',
                    'Private Message',
                    '{0}: {1}'.format(prefix, message))


def notify_public_action_message(buffername, prefix, message, highlighted):
    '''Notify on public action message.'''
    if highlighted:
        notify_highlighted_message(buffername, prefix, message)
    elif weechat.config_get_plugin("show_public_action_message") == "on":
        a_notify(
            'Action',
            'Public Action Message on {0}'.format(buffername),
            '{0}: {1}'.format(prefix, message),
        )


def notify_private_action_message(buffername, prefix, message, highlighted):
    '''Notify on private action message.'''
    if highlighted:
        notify_highlighted_message(buffername, prefix, message)
    elif weechat.config_get_plugin("show_private_action_message") == "on":
        a_notify(
            'Action',
            'Private Action Message',
            '{0}: {1}'.format(prefix, message),
        )

def notify_notice_message(buffername, prefix, message, highlighted):
    '''Notify on notice message.'''
    regex = re.compile(r'^([^\s]*) [^:]*: (.+)$', re.UNICODE)
    match = regex.match(message)
    if match:
        prefix = match.group(1)
        message = match.group(2)
        if highlighted:
            notify_highlighted_message(buffername, prefix, message)
        elif weechat.config_get_plugin("show_notice_message") == "on":
            a_notify(
                'Notice',
                'Notice Message',
                '{0}: {1}'.format(prefix, message))


def notify_invite_message(buffername, prefix, message, highlighted):
    '''Notify on channel invitation message.'''
    if weechat.config_get_plugin("show_invite_message") == "on":
        regex = re.compile(
            r'^You have been invited to ([^\s]+) by ([^\s]+)$', re.UNICODE)
        match = regex.match(message)
        if match:
            channel = match.group(1)
            nick = match.group(2)
            a_notify(
                'Invite',
                'Channel Invitation',
                '{0} has invited you to join {1}.'.format(nick, channel))


def notify_channel_topic(buffername, prefix, message, highlighted):
    '''Notify on channel topic change.'''
    if weechat.config_get_plugin("show_channel_topic") == "on":
        regex = re.compile(
            r'^\w+ has (?:changed|unset) topic for ([^\s]+)' +
                '(?:(?: from "(?:.+)")? to "(.+)")?',
            re.UNICODE)
        match = regex.match(message)
        if match:
            channel = match.group(1)
            topic = match.group(2) or ''
            a_notify(
                'Channel',
                'Channel Topic on {0}'.format(buffername),
                "{0}: {1}".format(channel, topic))


def notify_dcc_chat_request(match):
    '''Notify on DCC chat request.'''
    if weechat.config_get_plugin("show_dcc") == "on":
        nick = match.group(1)
        a_notify(
            'DCC',
            'Direct Chat Request',
            '{0} wants to chat directly.'.format(nick))


def notify_dcc_chat_closed(match):
    '''Notify on DCC chat termination.'''
    if weechat.config_get_plugin("show_dcc") == "on":
        nick = match.group(1)
        a_notify(
            'DCC',
            'Direct Chat Ended',
            'Direct chat with {0} has ended.'.format(nick))


def notify_dcc_get_request(match):
    'Notify on DCC get request.'
    if weechat.config_get_plugin("show_dcc") == "on":
        nick = match.group(1)
        file_name = match.group(2)
        a_notify(
            'DCC',
            'File Transfer Request',
            '{0} wants to send you {1}.'.format(nick, file_name))


def notify_dcc_get_completed(match):
    'Notify on DCC get completion.'
    if weechat.config_get_plugin("show_dcc") == "on":
        file_name = match.group(1)
        a_notify('DCC', 'Download Complete', file_name)


def notify_dcc_get_failed(match):
    'Notify on DCC get failure.'
    if weechat.config_get_plugin("show_dcc") == "on":
        file_name = match.group(1)
        a_notify('DCC', 'Download Failed', file_name)


def notify_dcc_send_completed(match):
    'Notify on DCC send completion.'
    if weechat.config_get_plugin("show_dcc") == "on":
        file_name = match.group(1)
        a_notify('DCC', 'Upload Complete', file_name)


def notify_dcc_send_failed(match):
    'Notify on DCC send failure.'
    if weechat.config_get_plugin("show_dcc") == "on":
        file_name = match.group(1)
        a_notify('DCC', 'Upload Failed', file_name)


# -----------------------------------------------------------------------------
# Utility
# -----------------------------------------------------------------------------
def set_away_status(match):
    status = match.group(1)
    if status == 'been ':
        STATE['is_away'] = True
    if status == 'longer ':
        STATE['is_away'] = False


def cb_process_message(
    data,
    wbuffer,
    date,
    tags,
    displayed,
    highlight,
    prefix,
    message
):
    '''Delegates incoming messages to appropriate handlers.'''
    tags = set(tags.split(','))
    functions = globals()
    is_public_message = tags.issuperset(
        TAGGED_MESSAGES['public message or action'])
    buffer_name = weechat.buffer_get_string(wbuffer, 'name')
    dcc_buffer_regex = re.compile(r'^irc_dcc\.', re.UNICODE)
    dcc_buffer_match = dcc_buffer_regex.match(buffer_name)
    highlighted = False
    if int(highlight):
        highlighted = True
    # Private DCC message identifies itself as public.
    if is_public_message and dcc_buffer_match:
        notify_private_message_or_action(buffer_name, prefix,
                                         message, highlighted)
        return weechat.WEECHAT_RC_OK
    # Pass identified, untagged message to its designated function.
    for key, value in UNTAGGED_MESSAGES.items():
        match = value.match(message)
        if match:
            functions[DISPATCH_TABLE[key]](match)
            return weechat.WEECHAT_RC_OK
    # Pass identified, tagged message to its designated function.
    for key, value in TAGGED_MESSAGES.items():
        if tags.issuperset(value):
            functions[DISPATCH_TABLE[key]](buffer_name, prefix,
                                           message, highlighted)
            return weechat.WEECHAT_RC_OK
    return weechat.WEECHAT_RC_OK


def a_notify(notification, subject, message):
    if STATE['is_away'] and weechat.config_get_plugin('sticky_away') == 'off':
        return
    if not STATE['is_away'] and weechat.config_get_plugin('sticky') == 'off':
        return

    msg = MIMEText(message)
    msg['From'] = weechat.config_get_plugin('email_from')
    msg['To'] = weechat.config_get_plugin('email_to')
    msg['Subject'] = subject

    p = subprocess.Popen(
        [weechat.config_get_plugin('sendmail'),
         weechat.config_get_plugin('email_to')],
        stdin=subprocess.PIPE,

    )
    p.communicate(input=str(msg))


# -----------------------------------------------------------------------------
# Main
# -----------------------------------------------------------------------------
def main():
    '''Sets up WeeChat notifications.'''
    # Initialize options.
    for option, value in SETTINGS.items():
        if not weechat.config_is_set_plugin(option):
            weechat.config_set_plugin(option, value)
    # Initialize.
    notifications = [
        'Public',
        'Private',
        'Action',
        'Notice',
        'Invite',
        'Highlight',
        'Server',
        'Channel',
        'DCC',
        'WeeChat'
    ]
    # Register hooks.
    weechat.hook_signal(
        'irc_server_connected',
        'cb_irc_server_connected',
        '')
    weechat.hook_signal(
        'irc_server_disconnected',
        'cb_irc_server_disconnected',
        '')
    weechat.hook_signal('upgrade_ended', 'cb_upgrade_ended', '')
    weechat.hook_print('', '', '', 1, 'cb_process_message', '')


if __name__ == '__main__' and weechat.register(
    SCRIPT_NAME,
    SCRIPT_AUTHOR,
    SCRIPT_VERSION,
    SCRIPT_LICENSE,
    SCRIPT_DESC,
    '',
    ''
):
    main()
