/**************************************************************************
COLLISIONS DECOR ET SCROLLING:
            Un sprite (vaisseau spatial) se déplace vers la souris (cliquer)
            Le déplacement se fait sur un décor plus grand que l'écran,
            donc l'écran réel "bouge" sur ce décor (en fait ce sont les
            coordonnées d'affichage du décor qui sont modifiées à l'écran...)

            Les deux variables à surveiller de près dans ce programme
            sont screenx et screeny qui sont les coordonnées de l'écran
            (fenêtre visible) dans le repère du décor.
            Les coordonnées acteur sont aussi dans ce repère du décor.
            En fait on ne passe en repère écran qu'avec ce qui est
            sur le buffer ou en rapport avec l'écran :

            Il faut modifier le blit du décors sur le buffer
            Il faut modifier le versSourisActeur
            (car la souris est dans le repère écran)
            Il faut modifier l'affichage du vaisseau sur le buffer
            Il faut bien sûr prendre en compte la position de
            l'acteur par rapport à l'écran réel pour ne pas
            le perdre de vu : le scrolling doit suivre l'acteur.

            Les collisions sont gérées comme pour le programme 2_1
            sur une carte de collision

**************************************************************************/

#include <allegro.h>


/****************************/
/*     STRUCTURE ACTEUR     */
/****************************/

// données personnelles de chaque acteur qui se déplace
typedef struct acteur
{
    int x, y;          // coordonnée (du coin sup. gauche)
    int precx, precy;  // coordonnées précédent le déplacement
    int dx, dy;        // vecteur deplacement
    int tx,ty;         // tailles : horizontal/vertical
    BITMAP *img;       // image de l'acteur
} t_acteur;


/****************************/
/*     FONCTIONs UTILEs     */
/****************************/

// Fonction détectant un contact avec couleur sur
// l'ensemble des pixels du rectangle encadrant l'acteur
// Retourne le nombre de pixels du bord en contact avec couleur
//  ce qui peut s'interpréter comme un booléen au niveau de l'appelant
//  ou être utilisé pour évaluer quantitativement la longueur du contact.
int contactBordActeur(t_acteur *acteur, BITMAP *bmp, int couleur){
    int ncontact;
    int xb,yb;

    ncontact=0;

    // Bords gauches et droits
    for (yb=0;yb<acteur->ty;yb++){
        if ( getpixel(bmp, acteur->x, acteur->y+yb) == couleur )
            ncontact++;
        if ( getpixel(bmp, acteur->x+acteur->tx, acteur->y+yb) == couleur )
            ncontact++;
    }

    for (xb=0;xb<acteur->tx;xb++){
        if ( getpixel(bmp, acteur->x+xb, acteur->y) == couleur )
            ncontact++;
        if ( getpixel(bmp, acteur->x+xb, acteur->y+acteur->ty) == couleur )
            ncontact++;
    }

    return ncontact;
}

// Donner à l'acteur un déplacement pour qu'il se rapproche de la souris
void versSourisActeur(t_acteur *acteur,int screenx,int screeny){
    // vecteur déplacement = vecteur centre_vaisseau->souris
    acteur->dx = mouse_x+screenx - (acteur->x + acteur->tx/2);
    acteur->dy = mouse_y+screeny - (acteur->y + acteur->ty/2);
    // mais il faut limiter la vitesse : borner les valeurs obtenues
    if (acteur->dx>5 ) acteur->dx= 5;
    if (acteur->dx<-5) acteur->dx=-5;
    if (acteur->dy>5 ) acteur->dy= 5;
    if (acteur->dy<-5) acteur->dy=-5;
}


// Chargement "sécurisé" d'une image :
// interrompt le programme avec un message si problème...
BITMAP * load_bitmap_check(char *nomImage){
    BITMAP *bmp;
    bmp=load_bitmap(nomImage,NULL);
    if (!bmp)
    {
        allegro_message("pas pu trouver %s",nomImage);
        exit(EXIT_FAILURE);
    }
    return bmp;
}


/******************************************/
/* PROGRAMME PRINCIPAL                    */
/* initialisation puis boucle de jeu      */
/******************************************/

int main()
{

    t_acteur *acteur;    // Un acteur (à créer)
    BITMAP *page;        // BITMAP buffer d'affichage
    BITMAP *decor;       // Image de fond, seulement pour visualiser
    BITMAP *murs;        // Image de fond, seulement pour les collisions
    int screenx,screeny; // Position de l'écran réel dans le repère du décor...

    // Lancer allegro et le mode graphique
    allegro_init();
    install_keyboard();
    install_mouse();

    set_color_depth(desktop_color_depth());
    if (set_gfx_mode(GFX_AUTODETECT_WINDOWED,800,600,0,0)!=0)
    {
        allegro_message("prb gfx mode");
        allegro_exit();
        exit(EXIT_FAILURE);
    }

    // Montrer la souris à l'écran
    show_mouse(screen);

    // CREATION DU BUFFER D'AFFICHAGE à la taille de l'écran
    page=create_bitmap(SCREEN_W,SCREEN_H);
    clear_bitmap(page);

    // charger image de fond
    decor=load_bitmap_check("images/grandfond_decor.bmp");

    // charger image de murs
    murs=load_bitmap_check("images/grandfond_murs.bmp");

    // Allocation et initialisation des paramètres de l'acteur :
    acteur = (t_acteur *)malloc(1*sizeof(t_acteur));
    acteur->tx = 99;    acteur->ty = 38;
    acteur->x = 300;     acteur->y = 260;
    acteur->dx = 0;      acteur->dy = 0;
    acteur->img = load_bitmap_check("images/spaceship_small.bmp");

    // Initialisation du scrolling à gauche du décor
    screenx=0;
    screeny=0;

    // Boucle de jeu
    while (!key[KEY_ESC])
    {
        // GESTION DU SCROLLING (suivi caméra)
        // ICI ON NE GERE QUE SUR L'AXE HORIZONTAL : A COMPLETER SI NECESSAIRE

        // Si le bord droit de l'acteur est à plus de 3/4 de largeur écran
        // ( en coordonnées écran donc ) alors récaler scrolling en 3/4
        if ( acteur->x + acteur->tx - screenx > 3*SCREEN_W/4 )
            screenx = acteur->x + acteur->tx - 3*SCREEN_W/4;

        // Si le bord gauche de l'acteur est à moins de 1/4 de largeur écran
        // ( en coordonnées écran donc ) alors récaler scrolling en 1/4
        if ( acteur->x - screenx < SCREEN_W/4 )
            screenx = acteur->x - SCREEN_W/4;

        // Bloquer le scrolling si il est ammené trop loin !
        if ( screenx < 0 ) screenx=0;
        if ( screenx > decor->w - SCREEN_W ) screenx=decor->w - SCREEN_W;


        // EFFACER BUFFER EN APPLIQUANT UNE PARTIE DU DECOR (TAILLE DE L'ECRAN)
        blit(decor,page,screenx,screeny,0,0,SCREEN_W,SCREEN_H);

        // GESTION INTERFACE si cliquer alors le vaisseau part vers la souris
        if (mouse_b&1)
            versSourisActeur(acteur,screenx,screeny);

        // ENREGISTRER POSITION ACTUELLE
        acteur->precx = acteur->x;
        acteur->precy = acteur->y;

        // DETERMINER NOUVELLE POSITION
        acteur->x += acteur->dx;  // équivalent à acteur->x = acteur->x + acteur->dx;
        acteur->y += acteur->dy;  // équivalent à acteur->y = acteur->y + acteur->dy;

        // SI COLLISION AVEC LE DECOR ALORS ON REVIENT SUR LA POSITION PRECEDENT LE DEPLACEMENT
        //  ici on regard uniquement sur la carte de collision (murs) invisible pour l'utilisateur
        if ( contactBordActeur(acteur, murs, makecol(255,255,255) ) )
        {
            acteur->x = acteur->precx;
            acteur->y = acteur->precy;

            // Souvent on accroche en dx ou dy mais on souhaite pouvoir "glisser" sur l'autre axe
            // donc on va essayer de voir si sur un seul axe ça ne passe pas...
            acteur->x += acteur->dx;       // juste dx ?
            if ( contactBordActeur(acteur, murs, makecol(255,255,255) ) )
                acteur->x = acteur->precx; // ça ne passe toujours pas
            acteur->y += acteur->dy;       // juste dy ?
            if ( contactBordActeur(acteur, murs, makecol(255,255,255) ) )
                acteur->y = acteur->precy; // ça ne passe toujours pas
        }

        // AFFICHAGE NOUVELLEs POSITIONs SUR LE BUFFER
        draw_sprite(page, acteur->img, acteur->x - screenx, acteur->y - screeny);

        // AFFICHAGE DU BUFFER MIS A JOUR A L'ECRAN
        blit(page,screen,0,0,0,0,SCREEN_W,SCREEN_H);

        // ON FAIT UNE PETITE PAUSE
        rest(10);
    }

    return 0;
}
END_OF_MAIN();