/*
 * Soubor:  my_xml.c
 * Datum:   06.08.2012
 * Autor:   Stanislav Bárta, xbarta29@stud.fit.vutbr.cz
 * Projekt: IM modul
 * Popis:   Obsahuje funkce pro praci s xml, ktere jsou potreba k analyze xmpp protokolu
 */

#include "my_xml.h"

/*
 * zpracuje xml strom zadanym v jednom retezci a rozdeli ho podle jednotlivych polozek
 */
struct xml *analyza_stanzy(char *stanza)
{
	// struktura ktera se bude vracet naplnena daty
	struct xml *zpracovano = (struct xml *) malloc(sizeof(struct xml));
	// ukazatel kam se aktualne ukladaji synovske uzly
	struct xml *aktualni = zpracovano;
	// pridani prvniho "prazdneho" elementu do vysledne struktury
	novy_element(aktualni, zpracovano, "_");

	// pomocne ukazatele pro ziskani jednotlivych elementu
	char *pomocny_ukazatel = stanza;
	char *element;

	// index zacatku a konce elementu pro jeho vykopirovani
	int zacatek_elementu = 0;
	int konec_elementu = 0;
	// indikace jestli se aktualne nacita element nebo uz se jedna o atributy
	int ctu_element = 0;

	char *atribut;
	int jsem_v_elementu = 0;
	int zacatek_atributu = 0;
	int konec_atributu = 0;
	int ctu_atribut = 0;

	int ctu_obsah = 0;
	int zacatek_obsahu = 0;
	int konec_obsahu = 0;

	// projiti celym xml po znacich
	for(int i = 0; i < delka_retezce(stanza); i++)
	{
		if(stanza[i] == '<')
		{
			// narazilo se na zacatek dalsiho elementu a cteme obsah, tak obsah ulozime
			if(ctu_obsah)
			{
				konec_obsahu = i;
				kopie_retezce(pomocny_ukazatel+zacatek_obsahu, &(aktualni->obsah), konec_obsahu-zacatek_obsahu);
				ctu_obsah = 0;
			}

			// dalsi element je oteviraci (pripadne jednoradkovy) a nejedna se o hlavickovi element xml
			if(stanza[i+1] != '/' && stanza[i+1] != '?')
			{
				zacatek_elementu = i+1;
				ctu_element = 1;
				jsem_v_elementu = 1;
			}
			// element je ukoncujici takze jako aktualni element je nastaven jeho predchudce
			else if(stanza[i+1] == '/')
			{
				aktualni = aktualni->predchudce;
			}
		}
		// dosli jsme k ukoncujicimu znaku
		else if(stanza[i] == '>')
		{
			jsem_v_elementu = 0;

			// cteme atribut
			if(ctu_atribut)
			{
				// posledni atribut jednoradkoveho elementu
				if(stanza[i-1] == '/')
				{
					konec_atributu = i-1;
				}
				// posledni element oteviraciho elementu
				else
				{
					konec_atributu = i;
				}

				ctu_atribut = 0;
				if(konec_atributu-zacatek_atributu)
				{
					kopie_retezce(pomocny_ukazatel+zacatek_atributu, &atribut, konec_atributu-zacatek_atributu);
					pridej_atribut(aktualni, atribut);
				}
			}

			else if(ctu_element)
			{
				// pokud jeste cteme element a jedna se o jednoradkovi element, vynechame koncovy znak '/'
				if(stanza[i-1] == '/')
				{
					konec_elementu = i-1;
				}
				// cteme element a narazili jsme na konec.
				else
				{
					// kontrola jestli ma element nejaky obsah
					if(stanza[i+1] != '<' && stanza[i+1] != '\n')
					{
						zacatek_obsahu = i+1;
						ctu_obsah = 1;
					}
					konec_elementu = i;
				}
				ctu_element = 0;

				kopie_retezce(pomocny_ukazatel+zacatek_elementu, &element, konec_elementu-zacatek_elementu);
				pridej_potomka(aktualni, element);

				// pridali jsme otevirajiciho potomka takze je aktualni
				if(stanza[i-1] != '/' && !porovnani_retezce(element, "stream:stream"))
				{
					aktualni = aktualni->potomci[aktualni->pocet_potomku - 1];
				}
			}
			// cetli jsme atributy jednoradkoveho elementu. aktualni je jeho predchudce
			else if(!ctu_element && stanza[i-1] == '/')
			{
				aktualni = aktualni->predchudce;
			}
			//cetli jsme atributy a za elementem nasteduje obsah
			else if(!ctu_element && stanza[i-1] != '/')
			{
				if(stanza[i+1] != '<' && stanza[i+1] != '\n')
				{
					zacatek_obsahu = i+1;
					ctu_obsah = 1;
				}
			}

		}
		else if(stanza[i] == ' ')
		{
			// narazili jsme na mezeru pri cteni elementu, tak ho prestaneme cist a ulozime
			if(ctu_element)
			{
				konec_elementu = i;
				ctu_element = 0;
				kopie_retezce(pomocny_ukazatel+zacatek_elementu, &element, konec_elementu-zacatek_elementu);
				pridej_potomka(aktualni, element);
				// aktualni je prave pridany
				if(!porovnani_retezce(element, "stream:stream"))
				{
					aktualni = aktualni->potomci[aktualni->pocet_potomku - 1];
				}
			}

			// cteme atribut je jiz ukoncen
			if(ctu_atribut && jsem_v_elementu && (stanza[i-1] == '"' || stanza[i-1] == '\''))
			{
				konec_atributu = i;
				kopie_retezce(pomocny_ukazatel+zacatek_atributu, &atribut, konec_atributu-zacatek_atributu);
				pridej_atribut(aktualni, atribut);
				zacatek_atributu = i+1; // zacatek dalsiho atributu
			}
			// jsme v elementu ale jeste jsme atribut cist nezacali, takze musime zacit ve cteni
			else if(jsem_v_elementu && !ctu_atribut)
			{
				zacatek_atributu = i+1;
				ctu_atribut = 1;
			}
		}
	}

	return zpracovano;
}

/*
 * inicializace noveho xml elementu a nastaveni jeho jmena
 * nastavi si sveho predchudce pro pozdejsi navrat k nemu
 */
void novy_element(struct xml *xml_struktura, struct xml *predchudce, char *jmeno)
{
	xml_struktura->element = jmeno;
	xml_struktura->pocet_atributu = 0;
	xml_struktura->atributy = NULL;
	xml_struktura->obsah = NULL;
	xml_struktura->predchudce = predchudce;
	xml_struktura->pocet_potomku = 0;
	xml_struktura->potomci = NULL;
}

/*
 * pridani noveho potomka do seznamu potomku
 * v pripade prvniho potomka je seznam vytvoren
 */
void pridej_potomka(struct xml *xml_struktura, char *jmeno)
{
	xml_struktura->pocet_potomku++;

	// alokace mista v seznamu
	if(xml_struktura->potomci == NULL)
	{
		xml_struktura->potomci = (struct xml **) malloc(sizeof(struct xml*));
	}
	else
	{
		xml_struktura->potomci = (struct xml **) realloc(xml_struktura->potomci ,(xml_struktura->pocet_potomku)*sizeof(struct xml*));
	}

	// alokace mista pro novy element
	xml_struktura->potomci[xml_struktura->pocet_potomku - 1] = NULL;
	xml_struktura->potomci[xml_struktura->pocet_potomku - 1] = (struct xml *) malloc(sizeof(struct xml));
	// pridani noveho elementu do seznamu
	novy_element(xml_struktura->potomci[xml_struktura->pocet_potomku - 1], xml_struktura, jmeno);
}

/*
 * pridani noveho atributu do seznamu atributu
 * pro kazdy novy atribut je potreba alokovat nove misto
 */
void pridej_atribut(struct xml *xml_struktura, char *atribut)
{
	// zvysime pocet atributu v seznamu
	xml_struktura->pocet_atributu++;

	// pokud jeste seznam nebyl vytvoren alokujeme misto pro novy prvek
	if(xml_struktura->atributy == NULL)
	{
		xml_struktura->atributy = (char **) malloc(sizeof(char *));
	}
	// seznam uz byl vytvoren a proto je potreba prealokovat jiz jednou alokovanou pamet
	else
	{
		xml_struktura->atributy = (char **) realloc(xml_struktura->atributy ,(xml_struktura->pocet_atributu)*sizeof(char *));
	}

	xml_struktura->atributy[xml_struktura->pocet_atributu - 1] = atribut;
}

/*
 * porovna typ atributu se zadanym vzorem
 * vraci 1 v pripade shody jinak 0
 */
int porovnani_typu_atributu(char *atribut, char *vzor)
{
	// pomocny ukazatel kam se ulozi pouze prvni cast atributu pred znakem '=' urcujici typ
	char *vysledny_typ;

	for(int i = 0; i < delka_retezce(atribut); i++)
	{
		if(atribut[i] == '=')
		{
			kopie_retezce(atribut, &vysledny_typ, i);
			break;
		}
	}

	// porovnani ziskaneho typu
	int vysledek = porovnani_retezce(vysledny_typ, vzor);
	// uvolneni pomocneho retezce
	free(vysledny_typ);
	//navraceni vysledku porovnani
	return vysledek;
}

/*
 * porovna hodnotu atributu se zadanym vzorem
 * vraci 1 v pripade shody jinak 0
 */
int porovnani_hodnoty_atributu(char *atribut, char *vzor)
{
	// pomocny ukazatel kam se ulozi pouze druha cast atributu za znakem '=' kde je ulozena jeho hodnota
	char *vysledna_hodnota;
	int zacatek;
	for(int i = 0; i < delka_retezce(atribut); i++)
	{
		if(atribut[i] == '=')
		{
			zacatek = i+2; //ignorovani zacinajicich uvozovek ve kterych je hodnota
			break;
		}
	}
	kopie_retezce(atribut+zacatek, &vysledna_hodnota, delka_retezce(atribut)-(zacatek+1)); // +1 pro odstraneni koncovych uvozovek
	// porovnani ziskane hodnoty se zadanym vzorem
	int vysledek = porovnani_retezce(vysledna_hodnota, vzor);
	// uvolneni pomocne promenne
	free(vysledna_hodnota);
	// navraceni vysledku
	return vysledek;
}

/*
 * ze zadaneho atributu vrati pouze cast obsahujici jeho hodnotu
 */
char *ziskej_hodnotu_atributu(char *atribut)
{
	char *vysledna_hodnota;
	int zacatek;
	for(int i = 0; i < delka_retezce(atribut); i++)
	{
		if(atribut[i] == '=')
		{
			zacatek = i+2; //ignorovani zacinajicich uvozovek ve kterych je hodnota
			break;
		}
	}
	kopie_retezce(atribut+zacatek, &vysledna_hodnota, delka_retezce(atribut)-(zacatek+1)); // +1 pro odstraneni koncovych uvozovek

	return vysledna_hodnota;
}

/*
 * smaze vsechny atributy v seznamu a pote i samotny seznam
 */
void smaz_atributy(struct xml *xml)
{
	for(int i = 0 ; i < xml->pocet_atributu; i++)
	{
		free(xml->atributy[i]);
	}
	free(xml->atributy);
}

/*
 * uvolni veskerou pamet naalokovanou pro xml strukturu
 */
void uvolneni_xml(struct xml *xml)
{
	// postupne projde vsechny potomky uzlu
	for(int i = 0 ; i < xml->pocet_potomku; i++)
	{
		// pokud jiz potomek nema zadne potomky je mozne ho smazat
		if(xml->potomci[i]->pocet_potomku == 0)
		{
			// smazani elementu
			smaz_atributy(xml->potomci[i]);
			// smazani nazvu uzlu
			free(xml->potomci[i]->element);
			// kontorla jestli mel uzel nejaky obsah. v opacnem pripade neni co uvolnovat
			if(xml->potomci[i]->obsah != NULL)
			{
				free(xml->potomci[i]->obsah);
			}
			// smazani ukazatele na aktualniho potomka
			free(xml->potomci[i]);
		}
		// potomek ma jeste potomky ktere je potreba smazat drive aby se neztratil ukazatel na ne
		else
		{
			uvolneni_xml(xml->potomci[i]);
			// smazani ukazatele na aktualniho potomka
			free(xml->potomci[i]);
		}
	}

	if(!porovnani_retezce(xml->element, "_\0"))
	{
		free(xml->element);
	}

	smaz_atributy(xml);
	if(xml->obsah != NULL)
	{
		free(xml->obsah);
	}
	if(xml->pocet_potomku)
	{
		free(xml->potomci);
	}
}

