/* TP6
	Objectifs :: cf., tp6.h
*/

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

#include "tp6.h"

// constant esymbolique pour traces
#define DEBUG 0

// constantes pour le blanc et le noir
const char blanc = ' ';
const char noir = '#';

// ______________________________________________
// methodes internes de lecture de fichier

// fin de lecture d'une ligne
// - préconditions : flux fic_in != NULL
static void dessin_next_line(FILE* f);

// clot la lecture d'une image (=> le fichier output est clot et le pointeur correspondant est mis à jour à stdout)
// - préconditions : dessin créé et initialisé en l'adresse passée en paramètre
static void dessin_lecture_clore(FILE** fic_out, s_dessin* dessin);

// lecture d'une instruction de dimensions
// - crée un dessin et en renvoie l'adresse
// - le statut d'échec / de réussite est renseigné dans *statut
// - préconditions : flux fic_in != NULL
static s_dessin* dessin_lire_T(FILE* fic_in, int* statut);

// lecture d'une instruction de nouvelle image
// - renvoie le nouveau fichier output
// - le statut d'échec / de réussite est renseigné dans *statut
// - préconditions : flux fic_in != NULL
static FILE* dessin_lire_S(FILE* fic_in, int* statut);

// lecture d'une instruction de point
// - préconditions : dessin créé et initialisé en l'adresse passée en paramètre, flux fic_in != NULL
static int dessin_lire_P(FILE* fic_in, s_dessin* dessin);

// lecture d'une instruction de rectangle
// - préconditions : dessin créé et initialisé en l'adresse passée en paramètre, flux fic_in != NULL
static int dessin_lire_R(FILE* fic_in, s_dessin* dessin);

// ______________________________________________
// fonctions de manipulation d'un s_dessin

// _________
// construction / destruction

// constructeur
// - crée un objet de type s_dessin
// - intialise cet objet
// - renvoie l'adresse de l'objet créé (NULL si pb rencontré)
// - précondition : l, h >= 1 
s_dessin* dessin_construire(unsigned int l, unsigned int h)
{
	s_dessin* dessin = NULL;

	// allocation mémoire
	dessin = (s_dessin*) malloc ( 1 * sizeof(s_dessin) );

	// si l'allocation a réussi, on initialise de dessin
	if (dessin)
	{
		dessin->l = l;
		dessin->h = h;

		dessin->pixel = (char*) malloc (l*h*sizeof(char));

		// si l'allocation a échoué, on détruit le dessin
		if (dessin->pixel == NULL)
		{
			free(dessin);
			dessin = NULL;
		}
		// sinon, on initialise le dessin (hyp : on écrit sur fond blanc)
		else
			dessin_efface(dessin);
	}

	return dessin;
}

// destructeur
// - détruit un objet de type s_dessin dont l'adresse est passée en paramètre
void dessin_detruire(s_dessin* dessin)
{
	if (dessin != NULL)
	{
		if (dessin->pixel != NULL)
			free(dessin->pixel);

		free(dessin);
	}
}

// _________
// accesseurs en lecture / ecriture

// trace un point aux coordonnées (x,y) dans le dessin dont l'adresse est passée en paramètre
// - précondition : dessin créé et initialisé en l'adresse passée en paramètre 
// - renvoie STATUT_OK en cas de réussite, STATUT_KO_COORD en cas d'échec (coordonnées erronées) 
int dessin_point(s_dessin* dessin, unsigned int x, unsigned int y, char c)
{
	int statut = STATUT_OK;

	if (x < dessin->l && y < dessin->h)
		dessin->pixel[GET_COORD(x, y, dessin->h)] = c;
	else
		statut = STATUT_KO_COORD;

	return statut;
}

// trace un trait horizontal de (x1,y) à (x2,y) dans le dessin dont l'adresse est passée en paramètre
// - précondition : dessin créé et initialisé en l'adresse passée en paramètre 
// - renvoie STATUT_OK en cas de réussite, STATUT_KO_COORD en cas d'échec (coordonnées erronées) 
int dessin_trait(s_dessin* dessin, unsigned int x1, unsigned int y, unsigned int x2, char c)
{
	int statut = STATUT_OK;

	if (x2 < dessin->l && y < dessin->h && x1 <= x2)
	{
		int x;
		for (x = x1 ; x <= x2 ; x++)
			dessin_point(dessin, x, y, c);
	}
	else
		statut = STATUT_KO_COORD;

	return statut;
}

// trace un rectangle de coins nord-ouest (x1,y1) et sud-est (x2,y2) dans le dessin dont l'adresse est passée en paramètre
// - précondition : dessin créé et initialisé en l'adresse passée en paramètre 
// - renvoie STATUT_OK en cas de réussite, STATUT_KO_COORD en cas d'échec (coordonnées erronées) 
int dessin_rectangle(s_dessin* dessin, unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, char c)
{
	int statut = STATUT_OK;

	if (x2 < dessin->l && y2 < dessin->h && x1 <= x2 && y1 <= y2)
	{
		// bords horizontaux
		dessin_trait(dessin, x1, y1, x2, c);
		dessin_trait(dessin, x1, y2, x2, c);

		// bords verticaux
		{
			int y;
			for (y = y1 ; y <= y2 ; y++)
			{
				dessin_point(dessin, x1, y, c);
				dessin_point(dessin, x2, y, c);
			}
		}
	}
	else
		statut = STATUT_KO_COORD;

	return statut;
}

// affichage d'un dessin
// - préconditions : dessin créé et initialisé en l'adresse passée en paramètre, flux f != NULL
void dessin_affiche(s_dessin* dessin, FILE* f)
{
	int x,y;

	for (y = 0 ; y < dessin->h ; y++)
	{
		for (x = 0 ; x < dessin->l ; x++)
			fprintf(f, "%c", dessin->pixel[GET_COORD(x, y, dessin->h)]);

		fprintf(f, "\n");
	}

	fprintf(f, "\n");
}

// fonction permettant d'effacer un dessin
// - préconditions : dessin créé et initialisé en l'adresse passée en paramètre
void dessin_efface(s_dessin* dessin)
{
	int i;

	for (i = 0 ; i < dessin->l * dessin->h ; i++)
		dessin->pixel[i] = blanc;
}

