#include <ctype.h>
#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 CONTINUE        0
#define DATE_LEN        11
#define DEPT_LEN        3
#define NOM_LEN         21
#define NOT_FOUND       1403
#define REFERENCE_LEN   5
#define SQL_COMMIT      1
#define SQL_ROLLBACK    0
#define SQL_SUCCESS     0
#define STOP            1
#define UID_LEN         30
#define VARCHAR_LEN     51

/********** Liste des fonctions **********/
void connexion(void);
void erreur_sql(int arret);
void deconnexion(int validation);
void q1(void);
int q1bis(void);
void q2(void);
void q3(void);
void q4(void);
void q5(void);
void q6(void);
void q7(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[UID_LEN];
    char identifiants[UID_LEN] = "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 == 1 ? "enregistré" : "annulé");
}

/*
 * Affiche le nombre de produits contenus dans la base (utilisée dans la question 3
 * car pas d'intérêt à être utilisée toute seule).
 *
 * Retourne :
 *      le nombre de produits contenus dans la base
 */
int q1(void) {
    int nbProd;
    EXEC SQL SELECT COUNT(*) INTO nbProd FROM TPRODUIT;
    printf("Il y a %d produits en base.\n", nbProd);
    return nbProd;
}

/*
 * Modifie un produit en affichant ses anciennes valeurs.
 */
void q2(void) {
    char codeRayon[REFERENCE_LEN], dateStock[DATE_LEN], nCodeRayon[REFERENCE_LEN], nDateStock[DATE_LEN], noProd[REFERENCE_LEN];
    int choix, nStock, stock;
    float nPrixV, prixV;
    VARCHAR des[VARCHAR_LEN], nDes[VARCHAR_LEN];

    printf("Saisir numéro produit : ");
    scanf("%s%*c", noProd);

    EXEC SQL
        SELECT des, stock, prixV, codeRayon, dateStock
        INTO :des, :stock, :prixV, :codeRayon, :dateStock
        FROM TPRODUIT
        WHERE noProd = :noProd;

    printf("1 - Désignation : %.*s.\n", des.len, des.arr);
    printf("2 - Stock : %d.\n", stock);
    printf("3 - Prix de vente : %.2f€.\n", prixV);
    printf("4 - Code rayon : %s.\n", codeRayon);
    printf("5 - Date : %s.\n", dateStock);

    while (choix != 9) {
        printf("Que voulez-vous modifier (9: quitter) ?\n");
        scanf("%d%*c", &choix);

        switch (choix) {
            case 1:
                printf("Entrez la nouvelle désignation : ");
                fgets(nDes, sizeof nDes, stdin);
                if (nDes[strlen(nDes) - 1] == '\n') {
                    nDes[strlen(nDes) - 1] = '\0';
                }
                EXEC SQL
                    UPDATE TPRODUIT
                    SET des = :nDes
                    WHERE noProd = :noProd;
                if (sqlca.sqlcode < SQL_SUCCESS) {
                    erreur_sql(STOP);
                }
                break;
            case 2:
                printf("Entrez le nouveau stock : ");
                scanf("%d%*c", &nStock);
                EXEC SQL
                    UPDATE TPRODUIT
                    SET stock = :nStock
                    WHERE noProd = :noProd;
                if (sqlca.sqlcode < SQL_SUCCESS) {
                    erreur_sql(STOP);
                }
                break;
            case 3:
                printf("Entrez le nouveau prix : ");
                scanf("%f%*c", &nPrixV);
                EXEC SQL
                    UPDATE TPRODUIT
                    SET prixV = :nPrixV
                    WHERE noProd = :noProd;
                if (sqlca.sqlcode < SQL_SUCCESS) {
                    erreur_sql(STOP);
                }
                break;
            case 4:
                printf("Entrez le nouveau code rayon : ");
                scanf("%s", &nCodeRayon);
                EXEC SQL
                    UPDATE TPRODUIT
                    SET codeRayon = :nCodeRayon
                    WHERE noProd = :noProd;
                if (sqlca.sqlcode < SQL_SUCCESS) {
                    erreur_sql(STOP);
                }
                break;
            case 5:
                printf("Entrez la date : ");
                scanf("%s", &nDateStock);
                EXEC SQL
                    UPDATE TPRODUIT
                    SET dateStock = :nDateStock
                    WHERE noProd = :noProd;
                if (sqlca.sqlcode < SQL_SUCCESS) {
                    erreur_sql(STOP);
                }
                break;
            default:
                printf("Choix inconnu.\n");
                break;
        }
        EXEC SQL COMMIT;
    }
}

/*
 * Affiche le nombre de produits en base et les affiche.
 */
void q3(void) {
    char noProd[REFERENCE_LEN], codeRayon[REFERENCE_LEN], dateStock[DATE_LEN];
    int cpt = 0, nbProd, stock;
    float prixV;
    VARCHAR des[REFERENCE_LEN1];

    nbProd = q1bis();
    if (nbProd > 0) {
        EXEC SQL
            DECLARE cprod CURSOR FOR
                SELECT noProd, des, stock, prixV, codeRayon, dateStock
                FROM TPRODUIT;
        EXEC SQL OPEN cprod;
        EXEC SQL FETCH cprod INTO :noProd, :des, :stock, :prixV, :codeRayon, :dateStock;

        while (sqlca.sqlcode != NOT_FOUND) {
            printf("----- Produit n°%d -----\n", ++cpt);
            printf("Numéro produit : %s.\n", noProd);
            printf("Désignation : %.*s.\n", des.len, des.arr);
            printf("Stock : %d.\n", stock);
            printf("Prix de vente : %.2f.\n", prixV);
            printf("Code rayon : %s.\n", codeRayon);
            printf("Date stock : %s.\n", dateStock);
            EXEC SQL
                FETCH cprod INTO :noProd, :des, :stock, :prixV, :codeRayon, :dateStock;
            if (sqlca.sqlcode < SQL_SUCCESS) {
                erreur_sql(STOP);
            }
        }
        EXEC SQL CLOSE cprod;
    }
}

/*
 * Ajoute un produit saisi.
 */
void q4(void) {
    char choix, noProd[REFERENCE_LEN], dateStock[DATE_LEN];
    int stock;
    float prixV;
    short indicateurDes = 0, indicateurStock = 0, indicateurPrixV = 0, indicateurCodeRayon = 0, indicateurDateStock = 0;
    VARCHAR des[VARCHAR_LEN];

    printf("Saisir numéro produit : ");
    scanf("%s%*c", noProd);
    printf("Saisir une désignation (O/*) ? ");
    scanf("%c%*c", &choix);
    if (toUpper(choix) == 'O') {
        printf("Saisir la désignation : ");
        fgets(des, sizeof des, stdin);
        if (des[strlen(des) - 1] == '\n') {
            des[strlen(des) - 1] = '\0';
        }
    }
    else {
        indicateurDes = -1;
    }

    printf("Saisir un stock (O/*) ? ");
    scanf("%c%*c", &choix);
    if (toUpper(choix) == 'O') {
        printf("Saisir le stock : ");
        scanf("%d%*c", &stock);
    }
    else {
        indicateurStock = -1;
    }

    printf("Saisir un prix de vente : ");
    scanf("%c%*c", &choix);
    if (toUpper(choix) == 'O') {
        printf("Saisir le prix de vente : ");
        scanf("%f%*c", &prixV);
    }
    else {
        indicateurPrixV = -1;
    }

    printf("Saisir un code rayon (O/*) ? ");
    scanf("%c%*c", &choix);
    if (toUpper(choix) == 'O') {
        printf("Saisir le code rayon : ");
        scanf("%d%*c", codeRayon);
    }
    else {
        indicateurCodeRayon = -1;
    }

    if (indicateurStock == 0) {
        EXEC SQL
        SELECT SYSDATE
        INTO :dateStock
        FROM DUAL;

    }
    else {
        indicateurDateStock = -1;
    }

    EXEC SQL
        INSERT INTO TPRODUIT
        VALUES(
            :noProd,
            :des INDICATOR :indicateurDes,
            :stock INDICATOR :indicateurStock,
            :codeRayon INDICATOR :indicateurCodeRayon,
            :dateStock INDICATOR :indicateurDateStock
        );

    EXEC SQL COMMIT;
}

/*
 * Affiche les produits d'un fournisseur saisi.
 */
void q5(void) {
    char noProd[REFERENCE_LEN], refFourn[REFERENCE_LEN];
    printf("Saisir référence fournisseur : ");
    scanf("%s%*c", refFourn);

    EXEC SQL
        DECLARE cprod CURSOR FOR
            SELECT noProd
            FROM TPRODUITFOURN
            WHERE pf.refFourn = :refFourn;

    EXEC SQL OPEN cprod;

    EXEC SQL FETCH cprod INTO :noProd;
    if (sqlca.sqlcode == NOT_FOUND) {
        printf("Aucun produit trouvé !\n");
    }

    printf("Liste des numéros de produits pour ce fournisseur :\n");
    while (sqlca.sqlcode != NOT_FOUND) {
        printf("\t%s\n", noProd);
        EXEC SQL FETCH cprod INTO :noProd;

    }

    EXEC SQL
        CLOSE cprod;
}

/*
 * Enregistre un fournisseur saisi.
 */
void q6(void) {
    char refFourn[REFERENCE_LEN], dept[DEPT_LEN];
    VARCHAR nom[NOM_LEN];

    printf("Saisir la référence fournisseur : ");
    scanf("%s%*c", refFourn);
    printf("Saisir le département : ");
    scanf("%s%*c", dept);
    printf("Saisir le nom du fournisseur : ");
    fgets(nom, sizeof nom, stdin);
    if (nom[strlen(nom) - 1] == '\n') {
        nom[strlen(nom) - 1] = '\0';
    }

    EXEC SQL
        INSERT INTO TFOURNISSEUR
        VALUES(:refFourn, :nom, :dept);

    EXEC SQL COMMIT;
}

/*
 * Supprime un fournisseur en prenant le soin de supprimer ses produits de la table de liaison.
 */
void q7(void) {
    char refFourn[REFERENCE_LEN];

    printf("Saisir la référence du fournisseur à supprimer : ");
    scanf("%s%*c", refFourn);

    EXEC SQL
        DELETE FROM TPRODUITFOURN
        WHERE refFourn = :refFourn;

    EXEC SQL
        DELETE FROM TFOURNISSEUR
        WHERE refFourn = :refFourn;
}

/*
 * 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 validation;

    CLEAR;

    connexion();

    EXEC SQL
        WHENEVER SQLERROR
        DO erreur_sql(STOP);

    // qX();

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

    return EXIT_SUCCESS;
}
