-- 1. Écrire une fonction nb_femmes_v1 (sans utiliser de curseur) qui retourne le nombre d’athlètes féminines concourant pour le pays dont le nom est passé en paramètre.
CREATE OR REPLACE FUNCTION nb_femmes_v1(nom_pays pays.nom%TYPE)
RETURNS INT AS $$ 
BEGIN
    return (
        SELECT COUNT(*)
        FROM athlete a
        INNER JOIN pays p ON p.code = a.pays
        WHERE p.nom = nom_pays AND a.sexe = 'F'
    );
END;
$$ LANGUAGE plpgsql;

-- 2. Même question, mais la fonction nb_femmes_v2 devra utiliser un curseur.
CREATE OR REPLACE FUNCTION nb_femmes_v2(nom_pays pays.nom%TYPE)
RETURNS INT AS $$ 
DECLARE
    curs cursor FOR SELECT a.pays, a.paysNaiss
        FROM athlete a
        INNER JOIN pays p ON p.code = a.pays
        WHERE p.nom = nom_pays AND sexe = 'F';
    rec athlete.code%TYPE;
    n INT := 0;
BEGIN
    open curs;
    fetch curs into rec;
    while found loop
        n = n + 1;
        fetch curs into rec;
    end loop;
    close curs;
    return n;
END;
$$ LANGUAGE plpgsql;

-- 3. Vérifier que vos deux fonctions retournent bien le même résultat. Vous pouvez par exemple tester avec la France.
SELECT nb_femmes_v1('France'), nb_femmes_v2('France');

-- 4. Modifier les deux fonctions précédentes pour ne compter que les athlètes féminines qui concourent pour un pays différent de leur pays de naissance.
CREATE OR REPLACE FUNCTION nb_femmes_v1(nom_pays pays.nom%TYPE)
RETURNS INT AS $$ 
BEGIN
    return (
        SELECT COUNT(*)
        FROM athlete a
        INNER JOIN pays p ON p.code = a.pays
        WHERE p.nom = nom_pays AND a.sexe = 'F' AND a.pays != a.paysNaiss
    );
END;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION nb_femmes_v2(nom_pays pays.nom%TYPE)
RETURNS INT AS $$ 
DECLARE
    curs cursor FOR SELECT a.pays, a.paysNaiss
        FROM athlete a
        INNER JOIN pays p ON p.code = a.pays
        WHERE p.nom = nom_pays AND sexe = 'F';
    pays athlete.pays%TYPE;
    paysNaiss athlete.paysNaiss%TYPE;
    n INT := 0;
BEGIN
    open curs;
    fetch curs into pays, paysNaiss;
    while found loop
        if pays != paysNaiss then
            n = n + 1;
        end if;
        fetch curs into pays, paysNaiss;
    end loop;
    close curs;
    return n;
END;
$$ LANGUAGE plpgsql;

-- 5. Vérifier que vos deux fonctions retournent bien le même résultat.
SELECT nb_femmes_v1('France'), nb_femmes_v2('France');

-- 6. Ecrire un bloc PL/pgSQL anonyme permettant de mettre à jour le pays de naissance des athlètes pour lequel l’information n’est pas connue, en indiquant comme pays celui pour lequel ils concourent. Afficher un message à chaque modification.
DO $$
DECLARE
    curs cursor FOR SELECT pays
        FROM athlete
        WHERE paysNaiss IS NULL
        FOR UPDATE;
    athlete_pays athlete.pays%TYPE;
BEGIN
    open curs;
    fetch curs into athlete_pays;
    while found loop
        UPDATE athlete SET paysNaiss = athlete_pays WHERE CURRENT OF curs;
        raise notice 'update';
        fetch curs into athlete_pays;
    end loop;
    close curs;
END;
$$ LANGUAGE plpgsql;

-- 7. Pour pouvoir gérer l’affectation des chambres dans le village olympique, l’organisation a besoin de connaître les dates de présence de chaque athlète. Créer une table Present(#athlete, dateArrivee, dateDepart).
DROP TABLE IF EXISTS present;
CREATE TABLE IF NOT EXISTS present (
    athlete CHAR(6) PRIMARY KEY REFERENCES athlete(code),
    dateArrivee DATE NOT NULL,  
    dateDepart DATE NOT NULL
);

-- 8. Ecrire un bloc PL/pgSQL anonyme permettant de remplir la table Present
INSERT INTO present
SELECT
    a.code,
    (MIN(e.dateE) - 14) dateArrivee,
    LEAST(
        '2021-08-11',
        MAX(e.dateE) + 1 + (
            CASE WHEN COUNT(r.medaille) >= 3 THEN 5
                WHEN COUNT(r.medaille) >= 1 THEN 3
                ELSE 0
            END
        )
    ) dateDepart
FROM athlete a
INNER JOIN pratiquer p ON p.athlete = a.code
INNER JOIN epreuve e ON e.discipl = p.discipl
LEFT JOIN resultat r ON r.athlete = a.code
GROUP BY a.code;

-- Version bloc anonyme
/*DO $$
DECLARE
    curs cursor FOR SELECT a.code, MIN(e.dateE), MAX(e.dateE), COUNT(r.medaille)
        FROM athlete a
        INNER JOIN pratiquer p ON p.athlete = a.code
        INNER JOIN epreuve e ON e.discipl = p.discipl
        LEFT JOIN resultat r ON r.athlete = a.code
        GROUP BY a.code;
    code_athlete athlete.code%TYPE;
    debut_epreuves DATE;
    fin_epreuves DATE;
    nb_medailles INT;

    date_arrivee DATE;
    date_depart DATE;
BEGIN
    open curs;
    fetch curs into code_athlete, debut_epreuves, fin_epreuves, nb_medailles;
    while found loop
        date_arrivee = debut_epreuves - 14;
        if nb_medailles >= 3 then
            date_depart := fin_epreuves + 6;
        elsif nb_medailles >= 1 then
            date_depart := fin_epreuves + 4;
        else
            date_depart := fin_epreuves + 1;
        end if;
        if date_depart > '2021-08-11' then
            date_depart := '2021-08-11';
        end if;
        INSERT INTO present VALUES(code_athlete, date_arrivee, date_depart);
        fetch curs into code_athlete, debut_epreuves, fin_epreuves, nb_medailles;
    end loop;
    close curs;
END;
$$ LANGUAGE plpgsql;*/

-- 9. Ecrire une fonction nbAth_jour qui prend en paramètre le nom d’un pays, un sexe et une date et retourne le nombre d’athlètes du pays et du sexe donnés présents au village olympique à la date donnée.
CREATE OR REPLACE FUNCTION nbAth_jour(nom_pays pays.nom%TYPE, sexe_athlete athlete.sexe%TYPE, date_presence DATE)
RETURNS INT AS $$ 
BEGIN
    return (
        SELECT COUNT(*) FROM present p
        INNER JOIN athlete a ON a.code = p.athlete
        WHERE a.pays = (SELECT code FROM pays WHERE nom = nom_pays)
            AND sexe = sexe_athlete
            AND date_presence BETWEEN dateArrivee AND dateDepart
    );
END;
$$ LANGUAGE plpgsql;

-- 10. Utiliser la fonction nbAth_jour pour savoir combien de chambres sont nécessaires pour la délégation française le 6 août 2021, sachant que les athlètes sont deux par chambre et qu’il n’y a pas de chambre mixte.
SELECT CEIL(nbAth_jour('France', 'F', '2021-08-06')::FLOAT / 2) + CEIL(nbAth_jour('France', 'M', '2021-08-06')::FLOAT / 2);

-- 11. Un virus circule. Ecrire une fonction confiner qui prend un paramètre (une date) et qui permet de repousser d’une semaine, si besoin, le départ des athlètes qui étaient présents au village olympique à la date donnée. La fonction devra retourner le nombre d’athlètes impactés
DROP FUNCTION confiner;
CREATE OR REPLACE FUNCTION confiner(date_confinement DATE)
RETURNS INT AS $$
DECLARE
    curs cursor FOR SELECT dateDepart
        FROM present
        WHERE date_confinement BETWEEN dateArrivee AND dateDepart
        FOR UPDATE;
    date_depart present.dateDepart%TYPE;
    nb INT := 0;
BEGIN
    open curs;
    fetch curs into date_depart;
    while found loop
        -- UPDATE present SET dateDepart = GREATEST(date_confinement + 7, dateDepart) WHERE CURRENT OF curs;
        if (date_confinement + 7) > date_depart then
            UPDATE present SET dateDepart = (date_confinement + 7) WHERE CURRENT OF curs;
            nb = nb + 1;
        end if;
        fetch curs into date_depart;
    end loop;
    close curs;
    return nb;
END;
$$ LANGUAGE plpgsql;

SELECT confiner('2021-07-27');

-- 12. Executer le script jo.sql pour annuler les modifications réalisées pendant ce tp.
\i jo.sql
