# Custom 
from dbmodel import *
from grader.valuation import *

# External
from imgui_bundle import (
        imgui,
        immapp,
        hello_imgui,
        imgui_md,
        implot,
        implot3d,
        immvision,
        ImVec2,
        ImVec4,
        im_file_dialog
)
from PIL import ImageColor
import numpy as np
from numpy.typing import NDArray

# Built In
from typing import List, Any

@immapp.static(inited=False)
def select_class(app_state: AppState) -> None:
    statics = select_class

    if not app_state.current_class_id:
        imgui.text("No class found in Database")
        return

    if not statics.inited:
        statics.select = 0 
        statics.classes = Class.select()
        statics.labels = [c.name for c in statics.classes]
        statics.inited = True
    
    changed, statics.select = imgui.combo("Classes", statics.select, statics.labels)
    if changed:
        app_state.current_class_id = statics.classes[statics.select].id

@immapp.static(inited=False)
def select_student(app_state: AppState) -> None:
    statics = select_student
    
    if not app_state.current_class_id:
        return

    if not statics.inited:
        statics.select = 0 
        statics.students = Student.select().where(Student.class_id == app_state.current_class_id)
        statics.inited = True

    if not statics.students:
        imgui.text("No Studends found")
        return 

    for n, student in enumerate(statics.students, start=1):
        display = f"{n}. {student.prename} {student.surname}"
        _, clicked = imgui.selectable(display, statics.select == n-1)
        if clicked:
            statics.select = n-1
            app_state.current_student_id = statics.students[statics.select].id


@immapp.static(inited=False)
def student_list(app_state: AppState) -> None:
    statics = student_list
    
    select_class(app_state)
    imgui.separator()
    select_student(app_state)


def plot_bar_line_percentage(data: np.array, labels: list, avg: float) -> None:
    if not data.size > 0:
        imgui.text("No Data available")
        return 

    name = hash(avg)

    if avg.is_integer():
        avg = int(avg)
    avg = np.ones(len(data)) * avg

    w, h = imgui.get_window_size()
    implot.push_colormap(implot.Colormap_.hot.value)
    if implot.begin_plot(f"Performance##{name}", ImVec2(-1, h*0.4), implot.Flags_.no_mouse_text.value | implot.Flags_.no_inputs.value):
        implot.setup_axes("Lectures", "Percentage")
        implot.setup_axes_limits(-1, len(data), 0, 110)
        implot.push_style_var(implot.StyleVar_.fill_alpha.value, 0.6)
        implot.push_style_var(implot.StyleVar_.line_weight.value, 3)
        implot.setup_axis_ticks(implot.ImAxis_.x1.value, 0, len(labels), len(labels), [" " for _ in labels], False)
        implot.plot_bars("Submissions", data)
        implot.plot_line("Average", avg)
        
        implot.push_style_color(implot.Col_.inlay_text, ImVec4(190,190,40,255)/255)
        for x_pos, label in enumerate(labels):
            y_pos = 50
            implot.plot_text(label, x_pos, y_pos//2, ImVec2(0,0), implot.TextFlags_.vertical.value)
        implot.pop_style_color()

        implot.pop_style_var()
        implot.end_plot()

COLOR_TEXT_PASSED = tuple([e/255 for e in ImageColor.getcolor("#1AFE49","RGBA")])
COLOR_TEXT_FAILED = tuple([e/255 for e in ImageColor.getcolor("#FF124F","RGBA")])
COLOR_TEXT_PROJECT = tuple([e/255 for e in ImageColor.getcolor("#0A9CF5","RGBA")])
@immapp.static(inited=False)
def student_graph(app_state: AppState) -> None:
    statics = student_graph
    if not statics.inited:
        statics.id = -1
        statics.student = None
        statics.group = None
        statics.lectures = None
        statics.points = None
        statics.sub_points = None
        statics.subs_data = None 
        statics.subs_labels = None 
        statics.max_points = None 
        statics.avg = None 
        statics.inited = True

    if id != app_state.current_student_id:
        statics.id = app_state.current_student_id
    
        if not app_state.current_student_id:
            imgui.text("No Students in Database")
            return
        statics.student = Student.get_by_id(app_state.current_student_id)
        submissions = Submission.select().where(Submission.student_id == statics.student.id)

        statics.group = Group.get_by_id(statics.student.group_id)
        statics.lectures = [Lecture.get_by_id(sub.lecture_id) for sub in submissions] 
        statics.max_points = np.sum([l.points for l in statics.lectures])
        statics.sub_points = [sub.points for sub in submissions]
        statics.points = np.sum(statics.sub_points)
        if statics.points.is_integer():
            statics.points = int(statics.points)
        #statics.grader = get_grader("Oberstufe")
        statics.grader = get_grader(statics.student.grader)

        statics.subs_data = np.array([p/mp.points for p, mp in zip(statics.sub_points, statics.lectures)], dtype=np.float32)*100
        statics.subs_labels = [f"{l.title} {int(points) if points.is_integer() else points}/{l.points}" for l, points in zip(statics.lectures, statics.sub_points)]
        statics.avg = statics.points/statics.max_points*100

    w, h = imgui.get_window_size()
    imgui_md.render(f"# {statics.student.prename} {statics.student.surname} ({statics.group.name})")
    imgui_md.render(f"### {statics.points}/{statics.max_points} ({statics.student.grader})")
    imgui.text(" ")
    imgui.progress_bar(statics.points/statics.max_points, ImVec2(w*0.9, h*0.05), f"{statics.points}/{statics.max_points} {statics.points/statics.max_points:.1%}")
    plot_bar_line_percentage(statics.subs_data, statics.subs_labels, statics.avg)
    imgui.separator()
    imgui.text_colored(COLOR_TEXT_PROJECT, f"{statics.group.name}: {statics.group.project}")
    for n, data in enumerate(zip(statics.lectures, statics.sub_points), start=1):
        lecture, points = data 
        COLOR = statics.grader.get_grade_color(points, lecture.points)
        imgui.text_colored(COLOR, f"{n}. {lecture.title} {points}/{lecture.points} ({statics.grader.get_grade(points, lecture.points)}) ")

@immapp.static(inited=False)       
def sex_graph(app_state: AppState) -> None:
    statics = sex_graph
   
    if db.is_closed():
        imgui.text("No Database loaded")
        return

    if not statics.inited:
        statics.max_points = None 
        statics.male_points = None
        statics.female_points = None
        statics.male_percentage = None 
        statics.female_percentage = None 
        statics.male_labels = None
        statics.female_labels = None
        statics.lectures = None
        statics.class_id = -1
        statics.state = 0
        statics.inited = True
   
    if not app_state.current_class_id:
        imgui.text("No Class found")
        return

    if statics.class_id != app_state.current_class_id:
        statics.class_id = app_state.current_class_id
        lectures = Lecture.select().where(Lecture.class_id == statics.class_id)
        statics.lectures = lectures

        statics.max_points = np.empty(len(lectures))
        statics.male_points = np.empty(len(lectures))
        statics.female_points = np.empty(len(lectures))
        for n, lecture in enumerate(lectures):
            statics.max_points[n] = lecture.points # Acc points 

            m_count = 0 
            f_count = 0 
            m_points = 0 
            f_points = 0 

            for sub in Submission.select().where(Submission.lecture_id == lecture.id):
                if Student.get_by_id(sub.student_id).sex == 'Male':
                    m_points += sub.points 
                    m_count += 1 
                else:
                    f_points += sub.points
                    f_count += 1 
            statics.male_points[n] = m_points/m_count/lecture.points
            statics.female_points[n] = f_points/f_count/lecture.points

        statics.male_percentage = np.sum(statics.male_points)/len(statics.male_points)
        statics.female_percentage = np.sum(statics.female_points)/len(statics.female_points)

        statics.male_labels = [f"{l.title} | {points:.1%}" for l, points in zip(lectures, statics.male_points)] 
        statics.female_labels = [f"{l.title} | {points:.1%}" for l, points in zip(lectures, statics.female_points)]

    w, h = imgui.get_window_size()
    if statics.state == 0:
        imgui_md.render("# Male")
        imgui.progress_bar(statics.male_percentage, ImVec2(w*0.9, h*0.05), f"{statics.male_percentage:.1%} n={len(Student.select().where(Student.sex == 'Male'))}")
        plot_bar_line_percentage(statics.male_points*100, statics.male_labels, statics.male_percentage*100)

        imgui_md.render("# Female")
        imgui.progress_bar(statics.female_percentage, ImVec2(w*0.9, h*0.05), f"{statics.female_percentage:.1%} n={len(Student.select().where(Student.sex == 'Female'))}")
        plot_bar_line_percentage(statics.female_points*100, statics.female_labels, statics.female_percentage*100)

    if statics.state == 1:
        male_xs = np.arange(1, len(statics.male_points)+1, dtype=np.float32) 
        male_ys = male_xs*0
        male_zs = np.array(statics.male_points*100, dtype=np.float32)
        
        female_xs = np.arange(1, len(statics.female_points)+1, dtype=np.float32)
        female_ys = np.ones(len(statics.female_points), dtype=np.float32) 
        female_zs = np.array(statics.female_points*100, dtype=np.float32)

        if implot3d.begin_plot("3D Gender Plot", ImVec2(w*0.9, h*0.9)):
            implot3d.setup_axes("Lecture", "Gender", "Percentage")
            implot3d.setup_box_scale(1.1, 1.1, 1.1)
            implot3d.setup_axes_limits(0, len(statics.male_points)+1, -0.2, 1.2, 0, 110)

            implot3d.set_next_marker_style(implot3d.Marker_.square.value)
            implot3d.plot_line("Male", male_xs, male_ys, male_zs)
            
            implot3d.set_next_marker_style(implot3d.Marker_.circle.value)
            implot3d.plot_line("Female", female_xs, female_ys, female_zs)

            for n, l in enumerate(statics.lectures, start=1):
                implot3d.plot_text(l.title, n, np.clip(n/len(statics.lectures)), 50, np.pi/4)

            implot3d.end_plot()
    
    if imgui.button("Change 2D/3D"):
        statics.state = not statics.state



COLOR_TEXT_FIRST = tuple([e/255 for e in ImageColor.getcolor("#FFCF40","RGBA")])
COLOR_TEXT_SECOND = tuple([e/255 for e in ImageColor.getcolor("#A5A9B4","RGBA")])
COLOR_TEXT_THIRD = tuple([e/255 for e in ImageColor.getcolor("#97564A","RGBA")])
COLOR_TEXT_ELSE = tuple([e/255 for e in ImageColor.getcolor("#85EBD9","RGBA")])
@immapp.static(inited=False)
def ranking() -> None:
    statics = ranking

    if not statics.inited:
        statics.id = -1
        statics.rank = None
        statics.state = 1
        statics.inited = True
    
    imgui_md.render("# Student Ranking")

    if not app_state.current_class_id:
        imgui.text("No class selected")
        return 
    
    if statics.id != app_state.current_class_id:
        statics.id = app_state.current_class_id

        students = Student.select().where(Student.class_id == statics.id)
        max_points = np.sum([l.points for l in Lecture.select().where(Lecture.class_id == statics.id)])

        statics.rank = list()
        for student in students:
            points = list()
            for sub in Submission.select().where(Submission.student_id == student.id):
                points.append(sub.points)
            statics.rank.append((student, sum(points)/max_points))
        statics.rank = sorted(statics.rank, key=lambda item: item[1], reverse=True)
    
    if statics.state == 0:
        for n, data in enumerate(statics.rank, start=1):
            student, points = data
            display = f"{n}. {student.prename} {student.surname} {points:.1%}"
            COLOR = COLOR_TEXT_ELSE
            if n == 1:
                COLOR = COLOR_TEXT_FIRST
            if n == 2:
                COLOR = COLOR_TEXT_SECOND
            if n == 3:
                COLOR = COLOR_TEXT_THIRD
            imgui.text_colored(COLOR, display)

    if statics.state == 1:
        w, h = imgui.get_window_size()
        if implot.begin_plot("Ranking", ImVec2(w*0.9, h*0.9)):
            implot.plot_bars("Ranking", np.array([p[1] for p in reversed(statics.rank)])*100, 0.67, 0, implot.BarsFlags_.horizontal.value)
            for n, s in enumerate(reversed(statics.rank)):
                student = s[0]
                implot.plot_text(f"{student.prename} {student.surname}", s[1]*50, n)
            implot.end_plot()

    if imgui.button("Change"):
        statics.state = not statics.state



