#
# The program's functions are implemented here. There is no user interaction in this file, therefore no input/print statements. Functions here
# communicate via function parameters, the return statement and raising of exceptions. 
#
from list_repr import *
import random


def generate_list(contestants_list:list[list[int]],n:int)->None:
    for i in range(n):
        contestant=create_contestant(random.randint(0,10),random.randint(0,10),random.randint(0,10))
        contestants_list.append(contestant)

def add_contestant(contestants_list:list[list[int]],grades:list[str])->None:
    """
    :params:    contestants_list - list[list[int,int]]
                grades - list[str]
    :return:    None
    The function checks the validity of the inputed data and throws exceptions accordingly.
    Afterwares the function create a new contestant and add it to the contestants_list
    :syntax: 
    add <P1 score> <P2 score> <P3 score>
    """
    if len(grades)!=3:
        raise ValueError("Invalid number of grades")
    for grade in grades:
        if not grade.isdecimal() or int(grade)>10 or int(grade)<0:
            raise ValueError("Grades must be integers between 0 and 10")
    contestant=create_contestant(int(grades[0]),int(grades[1]),int(grades[2]))
    contestants_list.append(contestant)

def insert_contestant(contestants_list:list[list[int]],command_params:list[str]):
    """
    :params:    contestants_list - list[list[int,int]]
                command_params - list[str]
    :return:    None
    The function checks the validity of the inputed data and throws exceptions accordingly.
    Afterwares the function create a new contestant and add it to the contestants_list at the position indicated by the command_params
    :syntax: insert <P1 score> <P2 score> <P3 score> at <position>
    """
    if len(command_params)!=5:
        raise ValueError("Invalid number of parameters")
    if command_params[3]!="at":
        raise SyntaxError("Command must of format: \'insert <P1 score> <P2 score> <P3 score> at <position>\'")
    for grade in command_params[:3]:
        if not grade.isdecimal() or int(grade)>10 or int(grade)<0:
            raise ValueError("Grades must be integers between 0 and 10")
    if len(contestants_list) < int(command_params[4]):
        raise ValueError("Position out of range")
    contestant=create_contestant(int(command_params[0]),int(command_params[1]),int(command_params[2]))
    contestants_list.insert(int(command_params[4]),contestant)

def list_contestants(contestants_list:list[list[int]],command_params:list[str]):
    if command_params==[]:
        return contestants_list
    if command_params[0]=="sorted":
        return sorted(contestants_list,key=get_average,reverse=True)
    if len(command_params)!=2:
        raise SyntaxError("Invalid number of parameters")
    if not command_params[1].isdecimal() or int(command_params[1])>10 or int(command_params[1])<0:
        raise ValueError("Grades must be integers between 0 and 10")
    if command_params[0]=="<":
       return list(filter(lambda x: get_average(x)<int(command_params[1]),contestants_list))
    if command_params[0]=="=":
        return list(filter(lambda x: get_average(x)==int(command_params[1]),contestants_list))
    if command_params[0]==">":
        return list(filter(lambda x: get_average(x)>int(command_params[1]),contestants_list))
    raise SyntaxError("Invalid parameters for list command")

def top_contestants(contestants_list:list[list[int]],command_params:list[str]):
    if not command_params[0].isdecimal()  or len(contestants_list)<int(command_params[0]):
        raise ValueError("Index of of range")
    if len(command_params)==1:
        return sorted(contestants_list,key=get_average,reverse=True)[:int(command_params[0])]
    elif len(command_params)==2:
        match command_params[1]:
            case "P1":
               return sorted(contestants_list,key=get_p1,reverse=True)[:int(command_params[0])]
            case "P2":
                return sorted(contestants_list,key=get_p2,reverse=True)[:int(command_params[0])]
            case "P3":
                return sorted(contestants_list,key=get_p3,reverse=True)[:int(command_params[0])]
            case _:
                raise ValueError("Argument does not match <P1 | P2 | P3>")
    raise ValueError("Invalid number of parameters")
    
def replace_contestant(contestants_list:list[list[int]],command_params:list[str]):
    """
    :params:    contestants_list - list[list[int,int]]
                command_params - list[str]
    :return:    None
    The function checks the validity of the inputed data and throws exceptions accordingly.
    Afterwares the function replaces the grades of the participant indicated by command_params with 0
    :syntax:
    replace <old score> <P1 | P2 | P3> with <new score>
    """
    if len(command_params)!=4:
        raise ValueError("Invalid number of parameters")
    if command_params[2]!="with":
        raise SyntaxError("Command must be of the form replace <old score> <P1 | P2 | P3> with <new score>")
    if not command_params[0].isdecimal() or int(command_params[0])>=len(contestants_list) or not command_params[3].isdecimal() or int(command_params[3])>10 or int(command_params[3])<0:
        raise ValueError("Grades must be integers between 0 and 10")
    if int(command_params[0])>len(contestants_list)-1:
        raise ValueError("Index out of range")
    match command_params[1]:
        case "P1":
            set_p1(contestants_list[int(command_params[0])],int(command_params[3]))
        case "P2":
            set_p2(contestants_list[int(command_params[0])],int(command_params[3]))
        case "P3":
            set_p3(contestants_list[int(command_params[0])],int(command_params[3]))
        case _:
            raise ValueError("Argument does not match <P1 | P2 | P3>")

def remove_contestants(contestants_list:list[list[int]],command_params:list[str]):
    """
    :params:    contestants_list - list[list[int,int]]
                command_params - list[str]
    :return:    None
    The function checks the validity of the inputed data and throws exceptions accordingly.
    Afterwares the function replaces the grades of the participant indicated by command_params with 0
    :syntax:
    remove <position>
    remove <start position> to <end position>
    remove [ < | = | > ] <score>
    """
    match len(command_params):
        case 1:
            if command_params[0].isdecimal() and len(contestants_list)>int(command_params[0]):
                set_p1(contestants_list[int(command_params[0])],0)
                set_p2(contestants_list[int(command_params[0])],0)
                set_p3(contestants_list[int(command_params[0])],0)
        case 2:
            if command_params[1].isdecimal() and int(command_params[1])<=10 and int(command_params[1])>=0:
                match command_params[0]:
                    case "<":
                        for el in contestants_list:
                            if get_average(el)<int(command_params[1]):
                                set_p1(el,0)
                                set_p2(el,0)
                                set_p3(el,0)
                    case ">":
                        for el in contestants_list:
                            if get_average(el)>int(command_params[1]):
                                set_p1(el,0)
                                set_p2(el,0)
                                set_p3(el,0)
                    case "=":
                        for el in contestants_list:
                            if get_average(el)==int(command_params[1]):
                                set_p1(el,0)
                                set_p2(el,0)
                                set_p3(el,0)
                    case _:
                        raise SyntaxError("Invalid Syntax")
        case 3:
            if command_params[1]=="to":
                if command_params[0].isdecimal() and command_params[2].isdecimal() and int(command_params[0])<len(contestants_list) and int(command_params[2])<len(contestants_list) and int(command_params[0])<int(command_params[2]):
                    for index in range(int(command_params[0]),int(command_params[2])+1):
                        set_p1(contestants_list[index],0)
                        set_p2(contestants_list[index],0)
                        set_p3(contestants_list[index],0)
                else:
                    raise ValueError("Index out of range")
            else:
                raise SyntaxError("Invalid Syntax")
        case _:
            raise SyntaxError("Invalid number of parameters")



if __name__=="__main__":
    from test import test_functions
    test_functions()