/* Chrysalide - Outil d'analyse de fichiers binaires
 * conv.c - substitutions de valeurs depuis un contenu binaire
 *
 * Copyright (C) 2014 Cyrille Bagard
 *
 *  This file is part of Chrysalide.
 *
 *  OpenIDA is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  OpenIDA is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
 */


#include "conv.h"


#include <assert.h>
#include <ctype.h>
#include <malloc.h>
#include <stdbool.h>
#include <string.h>


#include "helpers.h"
#include "qckcall.h"



/* ---------------------------- CONVERSION DES ARGUMENTS ---------------------------- */


/* Fonction de conversion */
struct _conv_func
{
    bool declared;                          /* Expression déjà déclarée ?  */
    bool defined;                           /* Expression déjà définie ?   */

    char *dest;                             /* Variable de destination     */

    bool is_expr;                           /* Choix du contenu réel       */

    union
    {
        arg_expr_t *expr;                   /* Valeur expressive directe   */

        struct
        {
            char *name;                     /* Fonction de conversion      */
            arg_list_t *args;               /* Liste des arguments         */

        };

    };

};



/* ---------------------------- ENSEMBLES DE CONVERSIONS ---------------------------- */


/* Liste des fonctions de conversions présentes */
struct _conv_list
{
    conv_func **functions;                 /* Fonctions de conversion     */
    size_t func_count;                     /* Nombre de ces fonctions     */

};



/* ---------------------------------------------------------------------------------- */
/*                              CONVERSION DES ARGUMENTS                              */
/* ---------------------------------------------------------------------------------- */


/******************************************************************************
*                                                                             *
*  Paramètres  : dest = désignation de la variable de destination.            *
*                expr = expression dont la valeur est à assigner.             *
*                                                                             *
*  Description : Définit une conversion à partir d'une simple expression.     *
*                                                                             *
*  Retour      : Structure mise en place.                                     *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

conv_func *make_conv_from_expr(char *dest, arg_expr_t *expr)
{
    conv_func *result;                      /* Conversion à retourner      */

    result = (conv_func *)calloc(1, sizeof(conv_func));

    result->dest = make_string_lower(dest);

    result->is_expr = true;
    result->expr = expr;

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : dest = désignation de la variable de destination.            *
*                func = nom de la fonction assurant le calcul de valeur.      *
*                args = argument(s) à fournir à cette fonction.               *
*                                                                             *
*  Description : Définit une conversion à partir d'une function à appeler.    *
*                                                                             *
*  Retour      : Structure mise en place.                                     *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

conv_func *make_conv_from_func(char *dest, char *func, arg_list_t *args)
{
    conv_func *result;                      /* Conversion à retourner      */

    result = (conv_func *)calloc(1, sizeof(conv_func));

    result->dest = make_string_lower(dest);

    result->is_expr = false;
    result->name = func;
    result->args = args;

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : func = éléments de conversion à supprimer de la mémoire.     *
*                                                                             *
*  Description : Libère de la mémoire une conversion enregistrée.             *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void delete_conv_func(conv_func *func)
{
    if (func->is_expr)
        delete_arg_expr(func->expr);

    else
    {
        free(func->name);
        delete_arg_list(func->args);
    }

    free(func);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : func = fonction de conversion à consulter.                   *
*                                                                             *
*  Description : Indique la variable de destination d'une conversion.         *
*                                                                             *
*  Retour      : Désignation humaine de la variable de destination.           *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

const char *get_conv_dest_name(const conv_func *func)
{
    return func->dest;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : func = fonction de conversion à consulter.                   *
*                                                                             *
*  Description : Indique la nature d'une conversion : fonction ou expression ?*
*                                                                             *
*  Retour      : Indication sur la constitution interne de la conversion.     *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool is_conv_func_expression(const conv_func *func)
{
    return func->is_expr;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : func = fonction de conversion à consulter.                   *
*                bits = gestionnaire des bits d'encodage.                     *
*                list = liste de l'ensemble des fonctions de conversion.      *
*                size = taille déterminée avec précision. [OUT]               *
*                                                                             *
*  Description : Détermine la taille en bits du résultat d'une fonction.      *
*                                                                             *
*  Retour      : true si la taille a pu être déterminée, false sinon.         *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool compute_conv_func_size(const conv_func *func, const coding_bits *bits, const conv_list *list, unsigned int *size)
{
    bool result;                            /* Bilan à retourner           */

    result = func->is_expr;

    if (result)
        result = compute_arg_expr_size(func->expr, bits, list, size);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : func = fonction de conversion à manipuler.                   *
*                bits = gestionnaire des bits d'encodage.                     *
*                list = liste de l'ensemble des fonctions de conversion.      *
*                                                                             *
*  Description : Marque les champs utilisés par une fonction de conversion.   *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool mark_conv_func(conv_func *func, const coding_bits *bits, const conv_list *list)
{
    bool result;                            /* Bilan à remonter            */

    if (func->is_expr)
        result = ensure_arg_expr_content_fully_marked(func->expr, bits, list);
    else
        result = ensure_arg_list_content_fully_marked(func->args, bits, list);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : func = fonction de conversion à manipuler.                   *
*                fd   = descripteur d'un flux ouvert en écriture.             *
*                bits = gestionnaire des bits d'encodage.                     *
*                list = liste de l'ensemble des fonctions de conversion.      *
*                wide = taille des mots décodés.                              *
*                                                                             *
*  Description : Déclare les variables associées à une fonction de conversion.*
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool declare_conv_func(conv_func *func, int fd, const coding_bits *bits, const conv_list *list, unsigned int wide)
{
    bool result;                            /* Bilan à remonter            */

    /* Si la fonction a déjà été définie lors d'un précédent besoin... */
    if (func->declared) return true;

    if (func->is_expr)
        result = ensure_arg_expr_content_fully_declared(func->expr, fd, bits, list, wide);

    else
        result = ensure_arg_list_content_fully_declared(func->args, fd, bits, list, wide);

    func->declared = result;

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : func     = fonction de conversion à manipuler.               *
*                last     = précise si la conversion est la dernière.         *
*                internal = indique le type de manipulation finale.           *
*                fd       = descripteur d'un flux ouvert en écriture.         *
*                arch     = architecture visée par l'opération globale.       *
*                bits     = gestionnaire des bits d'encodage.                 *
*                list     = liste de l'ensemble des fonctions de conversion.  *
*                pp       = pré-processeur pour les échanges de chaînes.      *
*                                                                             *
*  Description : Définit les variables associées à une fonction de conversion.*
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool define_conv_func(conv_func *func, bool last, bool internal, int fd, const char *arch, const coding_bits *bits, const conv_list *list, const pre_processor *pp)
{
    bool result;                            /* Bilan à remonter            */
    const char *callable;                   /* Fonction à appeler          */

    /* Si la fonction a déjà été définie lors d'un précédent besoin... */
    if (func->defined) return true;

    if (func->is_expr)
        result = ensure_arg_expr_content_fully_defined(func->expr, fd, arch, bits, list, pp);
    else
        result = ensure_arg_list_content_fully_defined(func->args, fd, arch, bits, list, pp);

    /* Nom de la fonction effectivement appelée */

    if (!func->is_expr)
    {
        callable = find_macro(pp, func->name);

        if (callable == NULL)
            callable = func->name;

    }
    else callable = NULL;

    if (last && callable == NULL)
    {
        fprintf(stderr, "Error: expected function to store '%s'.\n", func->dest);
        return false;
    }

    /* Dernier niveau : la variable de destination est imposée ! */
    if (last)
    {
        /* Si l'on doit manipuler une propriété d'instructon... */
        if (internal)
            result = checked_call_instr_func(callable, func->args, fd, bits, list, pp);

        /* Si on doit constituer un opérande à ajouter... */
        else
        {
            if (strchr(callable, '(') == NULL)
                dprintf(fd, "\t\top = %s(", callable);
            else
                dprintf(fd, "\t\top = %s", callable);

            result &= define_arg_list(func->args, fd, bits, list);

            dprintf(fd, ");\n");

        }

    }

    /* On constitue une variable intermédiaire, dont on peut conserver le nom ! */
    else
    {
        dprintf(fd, "\t\tval_%s = ", func->dest);

        if (func->is_expr)
            result &= define_arg_expr(func->expr, fd, bits, list);

        else
        {
            dprintf(fd, "%s(", callable);

            result = define_arg_list(func->args, fd, bits, list);

            dprintf(fd, ")");

        }

        dprintf(fd, ";\n");

    }

    func->defined = result;

    return result;

}



/* ---------------------------------------------------------------------------------- */
/*                              ENSEMBLES DE CONVERSIONS                              */
/* ---------------------------------------------------------------------------------- */


/******************************************************************************
*                                                                             *
*  Paramètres  : -                                                            *
*                                                                             *
*  Description : Crée un nouvelle liste vierge de fonctions de conversion.    *
*                                                                             *
*  Retour      : Nouvelle structure prête à emploi.                           *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

conv_list *create_conv_list(void)
{
    conv_list *result;                       /* Définition vierge à renvoyer*/

    result = (conv_list *)calloc(1, sizeof(conv_list));

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : list = ensemble de fonctions de conversion à supprimer.      *
*                                                                             *
*  Description : Supprime de la mémoire une de fonctions de conversion.       *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void delete_conv_list(conv_list *list)
{
    size_t i;                               /* Boucle de parcours          */

    for (i = 0; i < list->func_count; i++)
        delete_conv_func(list->functions[i]);

    if (list->functions != NULL)
        free(list->functions);

    free(list);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : list = liste de fonctions de conversion à compléter.         *
*                func = nom de la fonction assurant le calcul de valeur.      *
*                                                                             *
*  Description : Enregistre une function de conversion du brut à l'utile.     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void register_conversion(conv_list *list, conv_func *func)
{
    list->functions = (conv_func **)realloc(list->functions, ++list->func_count * sizeof(conv_func *));

    list->functions[list->func_count - 1] = func;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : list = liste de fonctions de conversion à consulter.         *
*                name = désignation humaine du champ à retrouver.             *
*                                                                             *
*  Description : Recherche un résultat précis dans une liste de fonctions.    *
*                                                                             *
*  Retour      : Structure associée au résulat trouvé ou NULL en cas d'échec. *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

conv_func *find_named_conv_in_list(const conv_list *list, const char *name)
{
    conv_func *result;                      /* Fonction à retourner        */
    size_t i;                               /* Boucle de parcours          */
    const char *dest;                       /* Nom de variable existante   */

    result = NULL;

    for (i = 0; i < list->func_count && result == NULL; i++)
    {
        dest = get_conv_dest_name(list->functions[i]);

        if (strcmp(dest, name) == 0)
            result = list->functions[i];

    }

    return result;

}