#!/usr/bin/python

# pylint: disable=missing-docstring

from __future__ import with_statement

import ConfigParser
import nntplib
import quopri
import re
import socket
import time
import urllib2


try:
    from ncpp_local import process_patch
except ImportError:
    # I've seen this error happen multiple times at 6AM on Sundays,
    # coinciding with the AFS restart. Wait five minutes for the
    # servers to come back.
    time.sleep(300)
    from ncpp_local import process_patch

STATEFILE = '/mit/ncurses/auto/state.ini'
STATE = ConfigParser.ConfigParser()
STATE.read(STATEFILE)
NC_STATE = dict(STATE.items('ncurses'))

## Patch processing
RE_SUBJECT = re.compile(r'(?:ANN: )([^ ]*ncurses-\d+.\d+(?:-\d{8})?(?:.patch.gz)?)')
RE_PATCH = re.compile(r' (\S*ncurses-\d+.\d+(?:-\d{8})?.patch.gz)')
RE_VERS_PATCH = re.compile(r'(?:ANN: )?ncurses-%s-\d{8}(?:.patch.gz)?' % NC_STATE['version'])

def is_patch(subject):
    if RE_SUBJECT.match(subject):
        if RE_VERS_PATCH.match(subject):
            return True
        print "Unexpected version number in subject:\n%s\n" % subject
    return False

def find_patch_uri(body):
    body = quopri.decodestring('\n'.join(body)).split('\n')
    return [line.group(0) for line in map(RE_PATCH.match, body) if line]

## NNTP
def response_okay(resp):
    # As per http://www.faqs.org/rfcs/rfc977.html
    # 2xx - Command ok
    return resp[0] == '2'

def get_subjects(svr, group, last_seen, **_kw):
    resp, _count, first, last, _name = svr.group(group)
    if not response_okay(resp):
        print "Unexpected response [%s]" % resp
    elif int(first) < int(last_seen) < int(last):
        resp, subjects = svr.xhdr('subject', '%i-%s' % (int(last_seen)+1, last))
        if not response_okay(resp):
            print "Unexpected response [%s]" % resp
        return subjects
    return None

def process_patch_articles(svr, articles):
    for msg_idx in (article[0] for article in articles if is_patch(article[1])):
        resp, msg_idx, _msgid, body = svr.body(msg_idx)
        if response_okay(resp):
            uris = find_patch_uri(body)
            if len(uris) != 1:
                # Unexpected URI count, send mail.
                print "Unexpected number of URIs: %s" % uris
                return -1
            try:
                ret = process_patch(uris[0].strip())
                if ret != 0:
                    return ret
            except urllib2.URLError as err:
                print "URLError: %s" % err.reason
                print "URL was: %s" % uris[0].strip()
    return 0


## High-level
def do_connect():
    try_count = 0
    while True:
        try:
            svr = nntplib.NNTP(NC_STATE['server'])
            return svr
        except (socket.error, socket.gaierror, nntplib.NNTPError, nntplib.NNTPTemporaryError):
            if try_count < 9:
                try_count += 1
                time.sleep(30)
            else:
                raise # Giving up

def main():
    svr = do_connect()
    subjects = get_subjects(svr, **NC_STATE)
    if subjects:
        new_last = subjects[-1][0]
        STATE.set('ncurses', 'last_seen', new_last)
        ret = process_patch_articles(svr, subjects)
        STATE.set('ncurses', 'stop', ret)
        with open(STATEFILE, 'wb') as state_fh:
            STATE.write(state_fh)

if NC_STATE['stop'] == '0':
    main()
