import csv
import pickle
import datetime
import os
import urllib.request, urllib.parse, urllib.error

from django.core.cache import cache
from django.urls import reverse
from django.http import HttpResponseRedirect, HttpResponse, Http404
from django.db.models import Q
from django.db.models.aggregates import Max
from django.shortcuts import render, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.utils.html import mark_safe
from django.conf import settings

from coredata.models import Member, CourseOffering, Person, Semester, Role

from courselib.auth import ForbiddenResponse, NotFoundResponse, is_course_student_by_slug
from courselib.auth import is_course_staff_by_slug, requires_course_staff_by_slug
from courselib.search import find_member
from forum.models import Forum

from grades.models import all_activities_filter
from grades.models import Activity, NumericActivity, LetterActivity, CalNumericActivity, GradeHistory
from grades.models import NumericGrade, LetterGrade
from grades.models import CalLetterActivity, ACTIVITY_TYPES, FLAGS
from grades.models import neaten_activity_positions
from grades.forms import NumericActivityForm, LetterActivityForm, CalNumericActivityForm, MessageForm
from grades.forms import ActivityFormEntry, FormulaFormEntry, StudentSearchForm, FORMTYPE
from grades.forms import GROUP_STATUS_MAP, CourseConfigForm, CalLetterActivityForm, CutoffForm
from grades.formulas import EvalException, activities_dictionary, eval_parse
from grades.utils import reorder_course_activities
from grades.utils import ORDER_TYPE, FormulaTesterActivityEntry, FakeActivity, FakeEvalActivity
from grades.utils import generate_numeric_activity_stat,generate_letter_activity_stat
from grades.utils import ValidationError, calculate_numeric_grade, calculate_letter_grade

from marking.models import get_group_mark, StudentActivityMark, GroupActivityMark, ActivityComponent

from groups.models import GroupMember, add_activity_to_group
from quizzes.models import Quiz

from submission.models import SubmissionComponent, GroupSubmission, StudentSubmission, SubmissionInfo, select_all_submitted_components, select_all_components

from log.models import LogEntry
from pages.models import Page, ACL_ROLES
from dashboard.models import UserConfig, NewsItem
from dashboard.photos import pre_fetch_photos, photo_for_view
from discuss import activity as discuss_activity


FROMPAGE = {'course': 'course', 'activityinfo': 'activityinfo', 'activityinfo_group' : 'activityinfo_group'}

# Only for display purpose.
ACTIVITY_TYPE = {'NG': 'Numeric Graded', 'LG': 'Letter Graded',
                 'CNG': 'Calculated Numeric Graded', 'CLG': 'Calculated Letter Graded'}

@login_required
def course_info(request, course_slug):
    if is_course_student_by_slug(request, course_slug):
        return _course_info_student(request, course_slug)
    elif is_course_staff_by_slug(request, course_slug):
        return _course_info_staff(request, course_slug)
    else:
        return ForbiddenResponse(request)



@requires_course_staff_by_slug
def reorder_activity(request, course_slug):
    """
    Ajax way to reorder activity.
    This ajax view function is called in the course_info page.
    """
    course = get_object_or_404(CourseOffering, slug=course_slug)
    if request.method == 'POST':
        neaten_activity_positions(course)
        # find the activities in question
        id_up = request.POST.get('id_up') 
        id_down = request.POST.get('id_down')
        if id_up == None or id_down == None:                      
            return ForbiddenResponse(request)
        # swap the position of the two activities
        activity_up = get_object_or_404(Activity, id=id_up, offering__slug=course_slug)
        activity_down = get_object_or_404(Activity, id=id_down, offering__slug=course_slug)

        activity_up.position, activity_down.position = activity_down.position, activity_up.position
        activity_up.save()
        activity_down.save()        
        
        return HttpResponse("Order updated!")
    return ForbiddenResponse(request)

def _course_info_staff(request, course_slug):
    """
    Course front page
    """
    course = get_object_or_404(CourseOffering, slug=course_slug)
    member = Member.objects.get(offering=course, person__userid=request.user.username, role__in=['INST','TA','APPR'])
    activities = all_activities_filter(offering=course)
    any_group = True in [a.group for a in activities]
    try:
        forum = Forum.objects.get(offering=course)
        forum_enabled = forum.enabled
    except Forum.DoesNotExist:
        forum_enabled = False

    # Non Ajax way to reorder activity, please also see reorder_activity view function for ajax way to reorder
    order = None  
    act = None  
    if 'order' in request.GET:  
        order = request.GET['order']  
    if 'act' in request.GET:  
        act = request.GET['act']  
    if order and act:  
        reorder_course_activities(activities, act, order)  
        return HttpResponseRedirect(reverse('offering:course_info', kwargs={'course_slug': course_slug}))  


    # Todo: is the activity type necessary?
    a
ctivities_info = []
    total_percent = 0
    for activity in activities:
        if activity.percent:
            total_percent += activity.percent

        if isinstance(activity, NumericActivity):
            activities_info.append({'activity':activity, 'type':ACTIVITY_TYPE['NG']})            
        elif isinstance(activity, LetterActivity):
            activities_info.append({'activity':activi
ty, 'type':ACTIVITY_TYPE['LG']})

    if len(activities) == 0:
        num_pages = Page.objects.filter(offering=course)
        if num_pages == 0:
            messages.info(request, "Students won't see this course in their menu on the front page. As soon as some activities or pages have been added, they will see a link to the course info page.")
    
    discussion_activity = False
    if course.discussion:
        discussion_activity = discuss_activity.recent_activity(member)

    # advertise combined offering if applicable.
    offer_combined = course.joint_with() and len(activities) == 0
    
    context = {'course': course, 'member': member, 'activities_info': activities_info, 'from_page': FROMPAGE['course'],
               'order_type': ORDER_TYPE, 'any_group': any_group, 'total_percent': total_percent, 'discussion_activity': discussion_activity,
               'offer_combined': offer_combined, 'forum_enabled': forum_enabled}
    return render(request, "grades/course_info_staff.html", context)


@requires_course_staff_by_slug
def course_config(request, course_slug):
    from forum.models import Forum
    course = get_object_or_404(CourseOffering, slug=course_slug)
    try:
        forum = Forum.objects.get(offering=course)
    except Forum.DoesNotExist:
        forum = Forum(offering=course)
        forum.enabled = False

    if request.method=="POST":
        form = CourseConfigForm(request.POST)
        if form.is_valid():
            course.set_url(form.cleaned_data['url'])
            course.set_taemail(form.cleaned_data['taemail'])
            #if course.uses_svn():
            #    course.set_indiv_svn(form.cleaned_data['indiv_svn'])
            #    course.set_instr_rw_svn(form.cleaned_data['instr_rw_svn'])
            course.set_group_min(form.cleaned_data['group_min'])
            course.set_group_max(form.cleaned_data['group_max'])
            course.save()
            forum.enabled = form.cleaned_data['forum']
            forum.identity = form.cleaned_data['forum_identity']
            forum.save()
            messages.success(request, 'Course config updated')

            #LOG EVENT#
            l = LogEntry(userid=request.user.username,
                  description=("updated config for %s") % (course),
                  related_object=course)
            l.save()

            return HttpResponseRedirect(reverse('offering:course_info', kwargs={'course_slug': course_slug}))
    else:
        form = CourseConfigForm({'url': course.url(), 'taemail': course.taemail(), 'forum': forum.enabled, 'forum_identity':