#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libxml/parser.h>
#include <libxml/xpath.h>

 
#include "manipXML.h"
 
/**
 * Libération de la mémoire pour la structure xmlConfig_t
 **/
void free_config(xmlConfig_t *conf) {
    xmlXPathFreeContext(conf->ctxt);
    xmlFreeDoc(conf->doc);
    free(conf->fichier);
    free(conf);
}
 
/**
 * Initialisation et chargement du fichier XML en mémoire
 **/
xmlConfig_t *load_config(const char *fichier) {
    xmlConfig_t *conf, null = { 0 };
 
    if ((conf = malloc(sizeof *conf)) == NULL) {
        return NULL;
    }
    memcpy(conf, &null, sizeof(*conf));

    // Copie du nom du fichier
    if ((conf->fichier = strdup(fichier)) == NULL) {
        free_config(conf);
        return NULL;
    }

    // Création de l'arbre DOM à partir du fichier XML
    xmlKeepBlanksDefault(0);
    if ((conf->doc = xmlParseFile(conf->fichier)) == NULL) {
        free_config(conf);
        return NULL;
    }

    // Récupération de la racine
    conf->racine = xmlDocGetRootElement(conf->doc);

    // Initialisation de l'environnement XPath
    xmlXPathInit();

    // Création d'un contexte pour les requêtes XPath
    conf->ctxt = xmlXPathNewContext(conf->doc);
    if (conf->ctxt == NULL) {
        free_config(conf);
        return NULL;
    }
    return conf;
}
 
/**
 * Fonction privée retournant l'élément <directive> via XPath correspondant
 * à l'attribut nom
 **/
static xmlNodePtr _get_node_by_xpath(xmlConfig_t *conf, const char *directive) {
    xmlXPathObjectPtr xpathRes = NULL;
    xmlNodePtr n = NULL;
    char *path;
 
    //asprintf(&path, "/configuration/directive[@nom=\"%s\"]", directive);
    asprintf(&path, "%s", directive);

    // Evaluation de l'expression XPath
    xpathRes = xmlXPathEvalExpression(path, conf->ctxt);
    if (xpathRes == NULL) {
        fprintf(stderr, "Erreur sur l'expression XPath\n");
        return(NULL);
    }
    free(path);

    if( xpathRes->nodesetval == NULL ) {
        fprintf(stderr, "Erreur sur l'expression XPath\n");
        return(NULL);
    }

    if (xpathRes && xpathRes->type == XPATH_NODESET && xpathRes->nodesetval->nodeNr == 1) {
        n = xpathRes->nodesetval->nodeTab[0];
    }

    xmlXPathFreeObject(xpathRes);
 
    return n;
}
 
 
/**
 * Modifie la valeur d'une directive si celle-ci est présente dans le fichier XML
 **/
int set_config(xmlConfig_t *conf, tabRecherche_t *p_info) {

    xmlNodePtr n = _get_node_by_xpath(conf, p_info->target);
 
    if (n == NULL) {
	printf( "La directive n'existe pas\n" );
	return(1);
    }
    else { // La directive existe : modification
	if( !strcmp( p_info->info, "") )
        	xmlNodeSetContent(n, p_info->tabValeur[0]);
	else
		xmlSetProp(n, p_info->info, p_info->tabValeur[0]);
    }
    return (xmlSaveFormatFile(conf->fichier, conf->doc, 1) > 0 ? 1 : 0);
}


/**
 * Affichage de la valeur d'une directive
 **/
int print_config( xmlConfig_t *conf, 
		   tabRecherche_t *p_info) {

    xmlNodePtr p_xmlNode;
    char	valeur[100];
    xmlChar 	*val = NULL;

    p_xmlNode = _get_node_by_xpath(conf, p_info->target);

    if (p_xmlNode == NULL) {
	printf("\nErreur\n");
        return(1);
    }

    val = xmlNodeGetContent(p_xmlNode);
    if( val == NULL )
	return(1);

    if( !strcmp(val, "") )
	 val = xmlGetProp(p_xmlNode,p_info->info);
 
    if( !strcmp( p_info->tabValeur[0], "") )
    	printf("Valeur de '%s' : #%s# \n",  p_info->target, val );

    return(0);
}
 

static void UsageProgramme( void )
{

    fprintf( stdout,"\n");
    fprintf( stdout," \tusage: manipXML.c -v <VM> -e <element> -t <target> \n");

    fprintf( stdout,"\t\t -v => nom du fichier xml a traiter \n");
    fprintf( stdout,"\t\t -t => arborescence de la cible  \n" );
    fprintf( stdout,"\t\t options : \n");
    fprintf( stdout,"\t\t -i => element de la cible si besoin \n" );
    fprintf( stdout,"\t\t -a => valeur de l'element a modifier  \n" );
    fprintf( stdout,"\t\t       le fichier en entree est modifie  \n" );
    fprintf( stdout,"\n");
    fprintf( stdout,"\texemple: manipXML.c -v MaVM.xml -t \"/domain/devices/disk[2]/target\" -i dev -a \"xvda\" \n");
    fprintf( stdout,"\n");

    return;
}


static int RecupererArguments(
    int                 argc,
    char                *argv[],
    tabRecherche_t      *p_info )
{
    int         id;


    /* Extrait le nom du fichier scenario des parametres*/
    if( ( argc < 4 ) || ( argc > 12 ) )
    {
        UsageProgramme( );
        return( 1 );
    }

    memset( (void *) p_info, 0x00, sizeof( tabRecherche_t ));

    /* distribution des arguments dans la structure des criteres */
    for( id = 1; id < argc; id++ )
    {
        if( !strcmp( argv[id], "-v" ) )
                strcpy( p_info->NomVm, argv[id+1] );

        else if( !strcmp( argv[id], "-t" ) )
	{
                strcpy( p_info->target, argv[id+1] );
	}

        else if( !strcmp( argv[id], "-i" ) )
                strcpy( p_info->info, argv[id+1] );

        else if( !strcmp( argv[id], "-a" ) )
        {
                strcpy( p_info->tabValeur[0], argv[id+1] );
                 p_info->nbrValeur++;
        }

        else
        {
            UsageProgramme( );
            return( 1 );
        }
        id++;
    }

    return(0);
}


int main (int argc, char **argv)
{

    int		Ret=0;
    xmlConfig_t *conf;
    tabRecherche_t       TabInfo;
 
   // Recuperation des arguments
   Ret = RecupererArguments( argc, argv, &TabInfo );
   if ( Ret != 0 )
        exit(1);

    // Chargement du fichier et initialisation
    conf = load_config( TabInfo.NomVm );
    if( conf == NULL )
    {
	printf( "Erreur\n" );
	exit (1);
    }
 
    // Récupération de la valeur des directives
    Ret = print_config(conf, &TabInfo ); 
    if ( Ret != 0 )
        exit(1);

 
    if ( strcmp( TabInfo.tabValeur[0], "" ) != 0 )
    {
    	// Modification de directives
    	Ret = set_config(conf, &TabInfo );
        if ( Ret != 0 )
            exit(1);
    }
 
    // Libération de la mémoire
    free_config(conf);
 
    exit(0);
}

