import re
import urllib2
from time import strptime, strftime


class CourseStringMapper:
    @classmethod
    def widget(cls):
        from django import forms
        w = forms.CharField(
            required=False,
            widget=forms.TextInput,
            help_text=(
                """
Alternate to choosing a group.
Enter the section key from the directory of courses.
e.g. 20101SCNC1000F001 (which is yyyytddddnnnnLsss where """
                """y=year, t=term (with "1" for Spring and"""
                """"3" for Fall), """
                """d=dept, n=course number,
L=course letter, s=section).  You can also enter the course-"""
                """string in the format the group names appear for another """
                """course (e.g. "t1.y2010.s001.cf1000.scnc.st.course:"""
                """columbia.edu" ).
<br /><br />
This will create a group that automatically ties registered students """
                """to the class upon login.
<br /><br />
If the course is not found in the """
                """<a href="http://www.columbia.edu/cu/bulletin/uwb/">"""
                """Directory of Classes</a>, then it will still create"""
                """a course."""
                """  This is a GOOD WAY to create a course that is not"""
                """associated """
                """with a directory.  Alternatively, click the (+) """
                """signs next """
                """to "Group" and "Faculty group" fields and create groups """
                """manually, before clicking save.
<br />
If you accidentally, entered the wrong course string, blank out """
                """the groups (course and faculty) and type in """
                """the string again.
""")
        )

        def clean(sectionkey):
            if sectionkey:
                class_info = SectionkeyTemplate.to_dict(sectionkey)
                if not class_info:
                    raise forms.ValidationError('Invalid course string.')
                else:
                    return sectionkey
        w.clean = clean
        return w

    @classmethod
    def get_groups(cls, sectionkey):
        from django.contrib.auth.models import Group
        section_dict = SectionkeyTemplate.to_dict(sectionkey)
        if not section_dict:
            section_dict = WindTemplate.to_dict(sectionkey)

        if section_dict:
            stud_grp_name = WindTemplate.to_string(section_dict)
            fac_grp_name = WindTemplate.to_string(
                dict(section_dict, member='fc'))

            stud_grp, created = Group.objects.get_or_create(name=stud_grp_name)
            fac_grp, created = Group.objects.get_or_create(name=fac_grp_name)
            return stud_grp, fac_grp
        else:
            return None, None

    @classmethod
    def get_course_info(cls, course_dict):
        directory_link = DirectoryLinkTemplate.to_string(course_dict)
        response = urllib2.urlopen(directory_link).read()
        return DirectoryPageTemplate.to_dict(response)

    @classmethod
    def on_create(cls, course):
        course_dict = WindTemplate.to_dict(course.group.name)
        from courseaffils.models import CourseInfo

        info, created = CourseInfo.objects.get_or_create(course=course)
        if course_dict:
            info.year = int(course_dict['year'])
            info.term = int(course_dict['term'])
        try:
            info_from_web = cls.get_course_info(
                WindTemplate.to_dict(course.group.name))

            for val in info_from_web:
                if val == 'days':
                    info.days = info_from_web[val]
                elif val in ('starttime', 'endtime'):
                    tm = strptime(info_from_web[val], '%I:%M%p')
                    setattr(info, val, strftime('%H:%M', tm))
                else:
                    course.coursedetails_set.get_or_create(
                        name=val,
                        value=info_from_web[val],
                        course=course)
        except:
            # oh well, couldn't get extra data.
            # maybe because it's a fake course string or not a proper course
            pass
        info.save()

    @classmethod
    def course_slug(cls, course, attempt=0):
        "returns a slug for the course, with higher resolution for attempt > 0"
        class_info = WindTemplate.to_dict(course.group.name)
        slug = None
        if class_info and 'number' in class_info:
            attempts = {
                0: "CU%s%s" % (class_info['dept'],
                               class_info['number'], ),
                1: "CU%s%s_%s" % (class_info['dept'],
                                  class_info['number'],
                                  class_info['section'],),
            }
            slug = attempts[attempt]
        else:
            slug = re.sub(' ', '_', course.title)
        return re.sub('\W', '', slug)


class WindTemplate:
    example = 't3.y2007.s001.cw3956.engl.fc.course:columbia.edu'

    @staticmethod
    def to_string(cdict):
        cdict['member'] = cdict.get('member', 'st')
        return 't%s.y%s.s%s.c%s%s.%s.%s.course:columbia.edu' % (
            cdict['term'],
            cdict['year'],
            cdict['section'],
            cdict['letter'].lower(),
            cdict['number'],
            cdict['dept'].lower(),
            cdict['member'].lower(),
        )

    @staticmethod
    def to_dict(wind_string):
        wind_match = re.match(
            ('t(?P<term>\d).y(?P<year>\d{4}).s(?P<section>\d{3})'
             '.c(?P<letter>\w)(?P<number>\w{4})'
             '.(?P<dept>[\w&]{4}).(?P<member>\w\w).course:columbia.edu'),
            wind_string)
        if wind_match:
            return wind_match.groupdict()


class SectionkeyTemplate:
    example = '20101SCNC1000F001'  # and 20103MIMD036PN004

    @staticmethod
    def to_dict(sectionkey):
        key_match = re.match(
            ('(?P<year>\d{4})(?P<term>\d)(?P<dept>[\w&]{4})'
             '(?P<number>\w{4})(?P<letter>\w)(?P<section>\d{3})'),
            sectionkey)
        if key_match:
            return key_match.groupdict()


class DirectoryLinkTemplate:
    example = ('http://www.columbia.edu/cu/bulletin/'
               'uwb/subj/SCNC/C1100-20101-003/')

    @staticmethod
    def to_string(cdict):
        return (
            'http://www.columbia.edu/cu/bulletin/'
            'uwb/subj/%s/%s%s-%s%s-%s/') % (
                cdict['dept'].upper(),
                cdict['letter'].upper(),
                cdict['number'],
                cdict['year'],
                cdict['term'],
                cdict['section'], )


class AffilTemplate:
    example = 'CUcourse_SDEVW2300_001_2011_1'

    @staticmethod
    def to_dict(affil_string):
        affil_match = re.match(
            ('CUcourse_(?P<dept>[\w&]{4})(?P<letter>\w)'
             '(?P<number>\w{4})_(?P<section>\d{3})_'
             '(?P<year>\d{4})_(?P<term>\d)'),
            affil_string)
        if affil_match:
            return affil_match.groupdict()


class DirectoryPageTemplate:
    @staticmethod
    def to_dict(html_page):
        ret_val = {}
        look_for = (
            'Call Number</td>[^>]*>(?P<call_number>\d*)</td>',
            'Location</td>[^>]*>(?P<times>[^<]*)<br>(?P<location>[^<]*)</td>',
            'Points</td>[^>]*>(?P<points>[\d\-.]*)</td>',
            'Campus</td>[^>]*>(?P<campus>[\w\s]*)</td>',
            'Instructor</td>[^>]*>(?P<instructor>[^<]*)</td>',
            # 'approvals_required', 'type', 'enrollment',
            # 'max_enrollment', 'status',
            # 'title', 'open_to', 'note', 'sectionkey',
        )
        for reg in look_for:
            m = re.search(reg, html_page)
            if m:
                ret_val.update(m.groupdict())

        if 'times' in ret_val:
            m = re.match(
                ('(?P<days>\w*)\s*(?P<starttime>[\d:apm]*)\-'
                 '(?P<endtime>[\d:apm]*)'),
                ret_val['times'])
            if m:
                ret_val.update(m.groupdict())

        return ret_val


class HashTagTemplate:
    example = '#CUviet1201'

    @staticmethod
    def to_string(cdict):
        return '#CU%s%s' % (
            cdict['dept'].lower(),
            cdict['number'],
        )
