"""Tools to interact with SF Bug Tracker"""

import formatter
import getpass
import htmllib
import httplib
import urllib
import urlparse

try:
    import cPickle
except ImportError:
    import pickle
else:
    pickle = cPickle

import pg
import login
import sflib
import sfdb

BUG_SUMMARY_URL = "http://sourceforge.net/bugs/?group_id=5470&set=custom&_assigned_to=0&_status=1&_category=100&_bug_group=100&SUBMIT=Browse"
BUG_LOAD_URL = "http://sourceforge.net/bugs/index.php?group_id=5470&func=detailbug&bug_id=%s"
BUG_POST_URL = "http://sourceforge.net/bugs/index.php"
VERBOSE = 1

class BugParser(htmllib.HTMLParser):
    __super_init = htmllib.HTMLParser.__init__

    VERBOSE = 0

    def __init__(self, formatter, verbose=None):
        self.__super_init(formatter)
        self.fields = {}
        self.current_field = None
        self.current_field_value = None
        self.summary = None
        self.bug_id = None
        if verbose is not None:
            self.VERBOSE = verbose
        
    def start_select(self, attrs):
        if self.VERBOSE:
            print "SELECT", attrs
        for k, v in attrs:
            if k == 'name':
                self.current_field = v
        assert self.current_field is not None
    
    def end_select(self):
        self.current_field = None

    def start_option(self, attrs):
        if self.VERBOSE:
            print "OPTION", attrs
        assert self.current_field_value is None
        selected = 0
        for k, v in attrs:
            if k == 'value':
                value = v
            if k == 'selected':
                selected = 1
        if selected:
            self.current_field_value = value
            self.save_bgn()

    def end_option(self):
        if self.current_field_value is not None:
            label = self.save_end()
            self.fields[self.current_field] = (label,
                                               self.current_field_value)
            self.current_field_value = None

    def start_input(self, attrs):
        summary = 0
        bug_id = 0
        value = None
        for k, v in attrs:
            if k == 'name' and v == 'summary':
                summary = 1
            elif k == 'value':
                value = v
            elif k == 'name' and v == 'bug_id':
                bug_id = 1
        if summary:
            assert self.summary is None
            assert value is not None
            self.summary = value
        if bug_id:
            assert self.bug_id is None
            assert value is not None
            self.bug_id = value

def get_bug_urls():
    urls = []
    p = sflib.SummaryParser(BUG_SUMMARY_URL, ('detailbug',))
    p.load(BUG_SUMMARY_URL)
    for path, query, frag in p.hrefs:
        url = urlparse.urljoin(BUG_SUMMARY_URL, path)
        urls.append(url)
    if VERBOSE:
        print "%d bugs found" % len(urls)
    return urls

def http_get(url, session):
    try:
        f = session.get(url)
        buf = f.read()
        f.close()
    except httplib.HTTPException, err:
        print "Error occurred loading %s" % url
        print err
        print
        return None
    return buf

def load_bug(url, session):
    for i in range(3):
        buf = http_get(url, session)
        if buf is not None:
            break
        time.sleep(10)
    else:
        raise RuntimeError, "could not load %s" % url
    p = BugParser(formatter.NullFormatter())
    p.feed(buf)
    return p.bug_id, p.summary, p.fields

# a list of fields in the bug submission form that are currently
# ignored and what value should be used for them on submission  
_ignored_fields = [("canned_response", "100"),
                   ("dependent_on_task[]", "100"),
                   ("dependent_on_bug[]", "100"),
                   ]

def update_bug(bug_info, session):
    """Update SF bug tracker with new bug_info

    bug_info is a dictionary returned from the local database
    session is the login session returned by login.Login
    """
    form = {'func': 'postmodbug',
            'group_id': '5470',
            'submit': 'Submit Changes',
            }
    for k, v in bug_info.items():
        form[k] = str(v)
    # fill in blank values for everything else
    for k, v in _ignored_fields:
        form[k] = v
    body = urllib.urlencode(form)
    headers = {'Content-Type': 'application/x-www-form-urlencoded',
               }
    return session.get(BUG_POST_URL, "POST", headers, body)

def make_bug_urls(bugs):
    return [BUG_LOAD_URL % bug for bug in bugs]

def main(bugs=None):
    session = login.Login("jhylton", getpass.getpass())
    db = sfdb.BugDBInterface(pg.connect())
    if bugs:
        urls = make_bug_urls(bugs)
    else:
        urls = get_bug_urls()
    for url in urls:
        if VERBOSE:
            print url
        bug_id, summary, fields = load_bug(url, session)
        if VERBOSE:
            print bug_id, summary
        print
        if db.exists(bug_id):
            db.update(bug_id, summary, fields)
        else:
            db.insert(bug_id, summary, fields)

if __name__ == "__main__":
    import sys
    if len(sys.argv) > 1:
        main(sys.argv[1:])
    else:
        main()
