/*
 * Pro*C
 * TP6 - Exercice 1
 *
 * Auteur : Damien NGUYEN
 * Groupe : 3
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

EXEC SQL INCLUDE SQLCA.H;
EXEC SQL INCLUDE SQLDA.H;
EXEC SQL INCLUDE ORACA.H;

#define CLEAR       system("clear")

/********** Constantes **********/
#define SQL_COMMIT      1
#define STOP            1
#define CONTINUE        0
#define SQL_ROLLBACK    0
#define SQL_SUCCESS     0
#define NOT_FOUND       1403

/********** Liste des fonctions **********/
void connexion(void);
void erreur_sql(int arret);
void deconnexion(int validation);
void enregistrer(void);
void modifier(void);
void supprimer(void);
void toutAfficher(void);
void afficherLocataires(void);
void afficherEnCours(void);
void enregistrerBidon(void);
void modifierTousLesNumerosDepartement(void);
void afficherCertainNombreDImmeubles(void);
void mettreFinLocationNULL(void);
void selectSansINTO(void);
void afficherPaquetsDeCinq (void);

/*
 * Fonction de connexion à Oracle.
 *
 * Les identifiants sont rentrés en dur pour ne pas avoir à les retaper
 * à la main à chaque test, mais dans l'idéal, on demande à l'utilisateur
 * de les rentrer et on concatène les identifiants à uid.arr.
 */
void connexion(void) {
    VARCHAR uid[50];
    char identifiants[30] = "danguyen1/danguyen1@kirov";
    printf("Connexion avec les identifiants suivants : %s .\n", identifiants);
    strcpy(uid.arr, identifiants);
    uid.len = strlen(uid.arr);

    EXEC SQL CONNECT :uid;
    if (sqlca.sqlcode == SQL_SUCCESS) {
        printf("Connexion réussie avec succès !\n\n");
    }
    else {
        printf("Connexion échouée !\n\n");
        exit(EXIT_FAILURE);
    }
}

/*
 * Fonction qui affiche les code et message d'erreur SQL.
 *
 * Paramètres :
 *      arret       STOP(1) pour quitter, n'importe quoi pour continuer
 */
void erreur_sql(int arret) {
    printf("Code d'erreur : %d.\n", sqlca.sqlcode);
    printf("Message erreur : %.*s.\n", sqlca.sqlerrm.sqlerrml, sqlca.sqlerrm.sqlerrmc);
    if (arret == STOP) {
        deconnexion(SQL_ROLLBACK);
        exit(EXIT_FAILURE);
    }
}

/*
 * Fonction de déconnexion.
 *
 * Paramètres :
 *      validation  SQL_COMMIT(1) pour COMMIT, n'importe quoi pour ROLLBACK
 */
void deconnexion(int validation) {
    if (validation == SQL_COMMIT) {
        EXEC SQL COMMIT WORK RELEASE;
    }
    else {
        EXEC SQL ROLLBACK WORK RELEASE;
    }
    printf("Déconnexion réussie, travail %s.\n", validation == SQL_COMMIT ? "enregistré" : "annulé");
}

/*
 * Fonction d'enregistrement d'un nouvel immeuble.
 */
void enregistrer(void) {
    char deptimm[3], noimm[5];
    VARCHAR adrimm[81];

    printf("Saisir le numéro d'immeuble : ");
    scanf("%s%*c", noimm);
    printf("Saisir la nouvelle adresse de l'immeuble : ");
    fgets(adrimm.arr, sizeof (adrimm.arr), stdin);
    if (adrimm.arr[strlen(adrimm.arr) - 1] == '\n') {
        adrimm.arr[strlen(adrimm.arr) - 1] = '\0';
    }
    adrimm.len = strlen(adrimm.arr);
    printf("Saisir le nouveau numéro de département : ");
    scanf("%s%*c", deptimm);

    EXEC SQL
        INSERT INTO TIMM2018
        VALUES(:noimm, :adrimm, :deptimm);
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }

    EXEC SQL COMMIT;
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }

    printf("Enregistré avec succès.\n");
}

/*
 * Fonction de modification d'un immeuble existant.
 * Techniquement, il est uniquement possible de changer l'adresse
 * et le département.
 */
void modifier(void) {
    char deptimm[3], noimm[5];
    VARCHAR adrimm[80];

    printf("Saisir le numéro d'immeuble : ");
    scanf("%s%*c", noimm);
    printf("Saisir la nouvelle adresse de l'immeuble : ");
    fgets(adrimm.arr, sizeof (adrimm.arr), stdin);
    if (adrimm.arr[strlen(adrimm.arr) - 1] == '\n') {
        adrimm.arr[strlen(adrimm.arr) - 1] = '\0';
    }
    adrimm.len = strlen(adrimm.arr);
    printf("Saisir le nouveau numéro de département : ");
    scanf("%s%*c", deptimm);

    EXEC SQL
        UPDATE TIMM2018
        SET ADRIMM = :adrimm, DEPTIMM = :deptimm
        WHERE NOIMM = :noimm;
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }

    EXEC SQL COMMIT;
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }

    printf("Modifié avec succès.\n");
}

/*
 * Fonction de suppression d'un immeuble existant.
 * On supprime toutes les clés étrangères des autres tables avant.
 */
void supprimer(void) {
    char noimm[5];

    printf("Saisir le numéro d'immeuble : ");
    scanf("%s%*c", noimm);

    EXEC SQL
        DELETE FROM TLOCATION2018
        WHERE REFLOG IN (
            SELECT REFLOG
            FROM TLOGT2018
            WHERE NOIMM = :noimm
        );
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }

    EXEC SQL
        DELETE FROM TLOGT2018
        WHERE NOIMM = :noimm;
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }

    EXEC SQL
        DELETE FROM TIMM2018
        WHERE NOIMM = :noimm;
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }

    EXEC SQL COMMIT;
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }

    printf("Supprimé avec succès.\n");
}

/*
 * Fonction qui affiche tous les logemens gérés par l'agence.
 */
void toutAfficher(void) {
    char deptimm[5], reflog[5];
    int cpt = 1, superf;
    float loyer;
    VARCHAR adrimm[81];

    EXEC SQL
        DECLARE clog CURSOR FOR
            SELECT DEPTIMM, ADRIMM, REFLOG, SUPERF, LOYER
            FROM TIMM2018 i, TLOGT2018 l
            WHERE i.NOIMM = l.NOIMM
            ORDER BY DEPTIMM, ADRIMM ASC;
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }

    EXEC SQL
        OPEN clog;
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }

    EXEC SQL
        FETCH clog INTO :deptimm, :adrimm, :reflog, :superf, :loyer;
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }
    if (sqlca.sqlcode == NOT_FOUND) {
        printf("Aucun logement trouvé !\n");
    }

    while (sqlca.sqlcode != NOT_FOUND) {
        printf("----- Logement %d -----\n", cpt);
        printf("Département : %s\n", deptimm);
        printf("Adresse : %.*s\n", adrimm.len, adrimm.arr);
        printf("Référence logement : %s\n", reflog);
        printf("Superficie : %d\n", superf);
        printf("Loyer : %.2f euros/mois\n\n", loyer);
        EXEC SQL
            FETCH clog INTO :deptimm, :adrimm, :reflog, :superf, :loyer;
        if (sqlca.sqlcode < SQL_SUCCESS) {
            erreur_sql(STOP);
        }
        ++cpt;
    }
    
    EXEC SQL CLOSE clog;
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }
}

/*
 * Fonction qui affiche les clients louant ou ayant loué un logement saisi.
 */
void afficherLocataires(void) {
    char reflog[5], nocli[20][7], finloc[20][11];
    int i;
    short indicateur[20];
    VARCHAR nomcli[20][21];

    printf("Saisir le numéro de logement voulu : ");
    scanf("%s%*c", reflog);

    EXEC SQL
        SELECT NOCLI, NOMCLI, FINLOC
        INTO :nocli, :nomcli, :finloc:indicateur
        FROM TLOCATION2018 l, TCLIENT2018 c
        WHERE REFLOG = :reflog
        AND l.NOLOC = c.NOCLI;
     if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }

    if (sqlca.sqlerrd[2] > 0) {
        for (i = 0; i < sqlca.sqlerrd[2]; ++i) {
            printf("Logement %s:\t%.*s(%s)\t=>\t%s\n", reflog, nomcli[i].len, nomcli[i].arr, nocli[i], indicateur[i] == -1 ? "occupé" : "libre");
        }
    }
    else {
        printf("Aucun locataire ou ancien locataire.\n");
    } 
}

/*
 * Focntion qui affiche les logements en cours.
 */
void afficherEnCours(void) {
    char deptimm[3], noprop[7], noloc[7];
    int cpt = 1;
    VARCHAR adrimm[81], nomprop[21], nomloc[21];

    EXEC SQL
        DECLARE cloc CURSOR FOR
            SELECT ADRIMM, DEPTIMM, NOPROP, NOLOC
            FROM TIMM2018 i, TLOGT2018 l
            WHERE NOLOC IS NOT NULL
            AND i.NOIMM = l.NOIMM;
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }

    EXEC SQL
        OPEN cloc;

    EXEC SQL FETCH cloc INTO :adrimm, :deptimm, :noprop, :noloc;
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }
    if (sqlca.sqlcode == NOT_FOUND) {
        printf("Aucun logement trouvé !\n");
    }

    while (sqlca.sqlcode != NOT_FOUND) {
        EXEC SQL
            SELECT NOMCLI
            INTO :nomprop
            FROM TCLIENT2018
            WHERE NOCLI = :noprop;
        EXEC SQL
            SELECT NOMCLI
            INTO :nomloc
            FROM TCLIENT2018
            WHERE NOCLI = :noloc;

        printf("----- Logement %d -----\n", cpt);
        printf("Propriétaire : %.*s\n", nomprop.len, nomprop.arr);
        printf("Locataire : %.*s\n", nomprop.len, nomprop.arr);
        printf("Adresse : %.*s (%s)\n", adrimm.len, adrimm.arr, deptimm);

        EXEC SQL FETCH cloc INTO :adrimm, :deptimm, :noprop, :noloc;
        if (sqlca.sqlcode < SQL_SUCCESS) {
            erreur_sql(STOP);
        }
        ++cpt;
    }

    EXEC SQL CLOSE cloc;
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }
}

/*
 * Fonction qui enregistre plein de lignes bidons.
 */
void enregistrerBidon(void) {
    char buffer[5], deptimm[3], noimm[5];
    int i;
    VARCHAR adrimm[81];

    strcpy(adrimm.arr, "Adresse bidon");
    adrimm.len = strlen(adrimm.arr);

    strcpy(deptimm, "12");

    for (i = 4; i < 50; ++i) {
        if (i < 10) {
            strcpy(noimm, "I00");
        }
        else {
            strcpy(noimm, "I0");
        }
        /* Stocke l'entier dans buffer pour pouvoir le concaténer à noimm. */
        snprintf(buffer, sizeof buffer, "%d", i);
        strcat(noimm, buffer);

        EXEC SQL
            INSERT INTO TIMM2018
            VALUES(:noimm, :adrimm, :deptimm);
        if (sqlca.sqlcode < SQL_SUCCESS) {
            erreur_sql(STOP);
        }

        EXEC SQL COMMIT;
        if (sqlca.sqlcode < SQL_SUCCESS) {
            erreur_sql(STOP);
        }

        printf("I00%d nregistré avec succès.\n", i);
    }
}

/*
 * Fonction qui modifie la totalité des numéros département à 01.
 */
void modifierTousLesNumerosDepartement(void) {
    EXEC SQL BEGIN DECLARE SECTION;
        int nbMax;
    EXEC SQL END DECLARE SECTION;

    nbMax = 9;
    EXEC SQL
        FOR :nbMax
        UPDATE TIMM2018
        SET deptimm = '01';
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }

    printf("%d lignes ont été mises à jour.\n", nbMax);
}

/*
 * Fonction qui affiche un certain nombre de lignes avec un curseur.
 */
void afficherCertainNombreDImmeubles(void) {
    int i;

    EXEC SQL BEGIN DECLARE SECTION;
        int nbLignes;
        char noimm[10][5], deptimm[10][3];
        VARCHAR adrimm[10][81];
    EXEC SQL END DECLARE SECTION;

    printf("Combien de lignes voulez-vous afficher (1-9) ? ");
    scanf("%d%*c", &nbLignes);

    EXEC SQL
        DECLARE cursor_imm CURSOR FOR
            SELECT noimm, adrimm, deptimm
            FROM TIMM2018
            ORDER BY NOIMM ASC;
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }

    EXEC SQL OPEN cursor_imm;
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }

    EXEC SQL FOR :nbLignes
        FETCH cursor_imm INTO :noimm, :adrimm, :deptimm;
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }

    printf("Liste des %d premiers immeubles:\n", nbLignes);
    for (i = 0; i < nbLignes; ++i) {
        printf("\t%s : %.*s (%s)\n", noimm[i], adrimm[i].len, adrimm[i].arr, deptimm[i]);
    }

    EXEC SQL CLOSE cursor_imm;
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }
}

/*
 * Fonction qui met la fin de location à NULL pour une location
 * saisie.
 */
void mettreFinLocationNULL(void) {
    int i;
    char reflog[5], finloc[11];
    short indicateur, newIndicateur = -1;

    printf("Saisir la référence logement : ");
    scanf("%s%*c", reflog);

    EXEC SQL
        UPDATE TLOCATION2018
        SET finloc = :finloc INDICATOR :newIndicateur // mot clé INDICATOR pas obligatoire
        WHERE reflog = :reflog;
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }

    EXEC SQL COMMIT;
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }

    printf("La ligne a été modifiée.\n");
}

/*
 * Fonction qui permet de voir que SELECT sans INTO compile, n'affiche
 * rien, et donc ne sert pas à grand chose.
 */
void selectSansINTO(void) {
    EXEC SQL
        SELECT COUNT(*) FROM TLOCATION2018;
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }
    printf("Succès !\n");
}

/*
 * Affiche les immeubles par paquets de 5 à chaque fois qu'on appuie sur
 * entrée.
 */
void afficherPaquetsDeCinq (void) {
    int i;

    EXEC SQL BEGIN DECLARE SECTION;
        int nbLignes = 50;
        char noimm[50][5], deptimm[50][3];
        VARCHAR adrimm[50][81];
    EXEC SQL END DECLARE SECTION;

    EXEC SQL
        DECLARE c_imm CURSOR FOR
            SELECT noimm, adrimm, deptimm
            FROM TIMM2018
            ORDER BY NOIMM ASC;
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }

    EXEC SQL OPEN c_imm;
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }

    nbLignes = 50;
    EXEC SQL FOR :nbLignes
        FETCH c_imm INTO :noimm, :adrimm, :deptimm;
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }

    for (i = 1; i < nbLignes; ++i) {
        printf("\t%s : %.*s (%s)\n", noimm[i], adrimm[i].len, adrimm[i].arr, deptimm[i]);
        if ((i % 5) == 0) {
            printf("\nEntrée pour les 5 prochains.\n");
            scanf("%*c");
        }
    }

    EXEC SQL CLOSE c_imm;
    if (sqlca.sqlcode < SQL_SUCCESS) {
        erreur_sql(STOP);
    }
}

/*
 * Affiche un menu avec différents choix possibles.
 *
 * Retourne :
 *      le choix qui va être utilisé dans le switch du main
 */
int menu(void) {
    int choix;

    printf("Vous voulez :\n");
    printf("\t1  - saisir un nouvel immeuble ?\n");
    printf("\t2  - modifier un immeuble existant ?\n");
    printf("\t3  - supprimer un immeuble existant ?\n");
    printf("\t4  - afficher tous les logements ?\n");
    printf("\t5  - affichier les disponibilités selon le logement\n");
    printf("\t6  - afficher les logements en cours de location\n");
    printf("\t7  - enregistrer des immeubles bidons\n");
    printf("\t8  - mettre tous les numéros département à une valeur donnée\n");
    printf("\t9  - afficher un certain nombre d'immeubles avec un curseur\n");
    printf("\t10 - mettre une fin de location à NULL\n");
    printf("\t11 - tester le SELECT sans INTO qui n'affiche rien\n");
    printf("\t12 - afficher les immeubles par paquets de 5\n");
    printf("Choix : ");
    scanf("%d%*c", &choix);

    return choix;
}

/*
 * Fonction principale.
 *
 * Paramètres :
 *      argc        Le nombre d'arguments
 *      argv        Le tableau d'arguments
 *
 * Retourne :
 *      le code de retour défini dans stdlib.h
 *      (EXIT_SUCCESS = 0)
 */
int main(int argc, char const *argv[]) {
    int choix, validation;

    CLEAR;

    connexion();

    choix = menu();
    do {
        switch (choix) {
            case 1:
                enregistrer();
                break;
            case 2:
                modifier();
                break;
            case 3:
                supprimer();
                break;
            case 4:
                toutAfficher();
                break;
            case 5:
                afficherLocataires();
                break;
            case 6:
                afficherEnCours();
                break;
            case 7:
                enregistrerBidon();
                break;
            case 8:
                modifierTousLesNumerosDepartement();
                break;
            case 9:
                afficherCertainNombreDImmeubles();
                break;
            case 10:
                mettreFinLocationNULL();
                break;
            case 11:
                selectSansINTO();
                break;
            case 12:
                afficherPaquetsDeCinq();
                break;
            default:
                printf("Le choix est incorrect, recommencez : ");
                scanf("%d%*c", &choix);
                CLEAR;
                break;
        }
    } while (choix < 1 || choix > 12);

    printf("Enregistrer le travail ? (1 = oui, * = non)\n");
    scanf("%d%*c", &validation);
    deconnexion(validation);

    return EXIT_SUCCESS;
}

/****************************************************************************************************
Cas où on affiche tout
****************************************************************************************************
danguyen1@iutclinfa1910:~/S2/Database/pr4$ ./ex4

Connexion avec les identifiants suivants : danguyen1/danguyen1@kirov .
Connexion réussie avec succès !

Vous voulez :
    1 - saisir un nouvel immeuble ?
    2 - modifier un immeuble existant ?
    3 - supprimer un immeuble existant ?
    4 - afficher tous les logements ?
    5 - affichier les disponibilités selon le logement
    6 - afficher les logements en cours de location
Choix : 4
----- Logement 1 -----
Département : 03  
Adresse : Bd de Courtais Montlucon
Référence logement : L003
Superficie : 60
Loyer : 320.00 euros/mois

----- Logement 2 -----
Département : 63  
Adresse : Bd Lafayette Clermont Fd
Référence logement : L002
Superficie : 60
Loyer : 400.00 euros/mois

----- Logement 3 -----
Département : 63  
Adresse : Bd Lafayette Clermont Fd
Référence logement : L005
Superficie : 90
Loyer : 470.00 euros/mois

----- Logement 4 -----
Département : 63  
Adresse : Bd Lafayette Clermont Fd
Référence logement : L004
Superficie : 100
Loyer : 700.00 euros/mois

----- Logement 5 -----
Département : 63  
Adresse : Bd Lafayette Clermont Fd
Référence logement : L001
Superficie : 90
Loyer : 450.00 euros/mois

Enregistrer le travail ? (1 = oui, * = non)
2
Déconnexion réussie, travail annulé.
****************************************************************************************************/

/****************************************************************************************************
Cas où on affiche les disponibilités selon le logement saisi
****************************************************************************************************
danguyen1@iutclinfa1910:~/S2/Database/pr4$ ./ex4

Connexion avec les identifiants suivants : danguyen1/danguyen1@kirov .
Connexion réussie avec succès !

Vous voulez :
    1 - saisir un nouvel immeuble ?
    2 - modifier un immeuble existant ?
    3 - supprimer un immeuble existant ?
    4 - afficher tous les logements ?
    5 - affichier les disponibilités selon le logement
    6 - afficher les logements en cours de location
Choix : 5
Saisir le numéro de logement voulu : L002
Logement L002:  Martin(CL0002)  =>  libre
Logement L002:  Durand(CL0004)  =>  libre
Logement L002:  Martin(CL0002)  =>  libre
Logement L002:  Martin(CL0002)  =>  occupé
Enregistrer le travail ? (1 = oui, * = non)
2
Déconnexion réussie, travail annulé.
****************************************************************************************************/

/****************************************************************************************************
Cas où on affiche les disponibilités selon le logement saisi et où il n'y a pas de locataire
****************************************************************************************************
danguyen1@iutclinfa1910:~/S2/Database/pr4$ ./ex4

Connexion avec les identifiants suivants : danguyen1/danguyen1@kirov .
Connexion réussie avec succès !

Vous voulez :
    1 - saisir un nouvel immeuble ?
    2 - modifier un immeuble existant ?
    3 - supprimer un immeuble existant ?
    4 - afficher tous les logements ?
    5 - affichier les disponibilités selon le logement
    6 - afficher les logements en cours de location
Choix : 5
Saisir le numéro de logement voulu : L001
Aucun locataire ou ancien locataire.
Enregistrer le travail ? (1 = oui, * = non)
2
Déconnexion réussie, travail annulé.
****************************************************************************************************/

/****************************************************************************************************
Cas où on affiche l'ensemble des locations en cours
****************************************************************************************************
danguyen1@iutclinfa1910:~/S2/Database/pr4$ ./ex4

Connexion avec les identifiants suivants : danguyen1/danguyen1@kirov .
Connexion réussie avec succès !

Vous voulez :
    1 - saisir un nouvel immeuble ?
    2 - modifier un immeuble existant ?
    3 - supprimer un immeuble existant ?
    4 - afficher tous les logements ?
    5 - affichier les disponibilités selon le logement
    6 - afficher les logements en cours de location
Choix : 6
----- Logement 1 -----
Propriétaire : Dupond
Locataire : Dupond
Adresse : Bd Lafayette Clermont Fd (63)
----- Logement 2 -----
Propriétaire : Thibaut
Locataire : Thibaut
Adresse : Bd de Courtais Montlucon (03)
----- Logement 3 -----
Propriétaire : Thibaut
Locataire : Thibaut
Adresse : Bd Lafayette Clermont Fd (63)
Enregistrer le travail ? (1 = oui, * = non)
2
Déconnexion réussie, travail annulé.
****************************************************************************************************/
