/* hb_dict.c

   Copyright (c) 2003-2016 HandBrake Team
   This file is part of the HandBrake source code
   Homepage: <http://handbrake.fr/>.
   It may be used under the terms of the GNU General Public License v2.
   For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
 */

#include "hb.h"
#include "hb_dict.h"

hb_dict_t * hb_dict_init( int alloc )
{
    hb_dict_t * dict = NULL;
    dict = malloc( sizeof( hb_dict_t ) );
    if( !dict )
    {
        hb_log( "ERROR: could not allocate hb_dict_t" );
        return NULL;
    }
    dict->count = 0;
    dict->objects = malloc( alloc * sizeof( hb_dict_entry_t ) );
    if( !dict->objects )
    {
        hb_log( "ERROR: could not allocate hb_dict_t objects" );
        dict->alloc = 0;
    }
    else
    {
        dict->alloc = alloc;
    }
    return dict;
}

void hb_dict_free( hb_dict_t ** dict_ptr )
{
    hb_dict_t * dict = *dict_ptr;
    if( dict )
    {
        if( dict->objects )
        {
            int i;
            for( i = 0; i < dict->count; i++ )
            {
                if( dict->objects[i].key )
                {
                    free( dict->objects[i].key );
                }
                if( dict->objects[i].value )
                {
                    free( dict->objects[i].value );
                }
            }
            free( dict->objects );
        }
        free( *dict_ptr );
        *dict_ptr = NULL;
    }
}

void hb_dict_set( hb_dict_t ** dict_ptr, const char * key, const char * value )
{
    hb_dict_t * dict = *dict_ptr;
    if( !dict )
    {
        hb_log( "hb_dict_set: NULL dictionary" );
        return;
    }
    if( !key || !strlen( key ) )
        return;
    hb_dict_entry_t * entry = hb_dict_get( dict, key );
    if( entry )
    {
        if( entry->value )
        {
            if( value && !strcmp( value, entry->value ) )
                return;
            else
            {
                free( entry->value );
                entry->value = NULL;
            }
        }
        if( value && strlen( value ) )
            entry->value = strdup( value );
    }
    else
    {
        if( dict->alloc <= dict->count )
        {
            hb_dict_entry_t * tmp = NULL;
            tmp = malloc( ( 2 * dict->alloc ) * sizeof( hb_dict_entry_t ) );
            if( !tmp )
            {
                hb_log( "ERROR: could not realloc hb_dict_t objects" );
                return;
            }
            if( dict->objects )
            {
                if( dict->count )
                    memcpy( tmp, dict->objects, dict->count * sizeof( hb_dict_entry_t ) );
                free( dict->objects );
            }
            dict->objects = tmp;
            dict->alloc *= 2;
        }
        dict->objects[dict->count].key = strdup( key );
        if( value && strlen( value ) )
            dict->objects[dict->count].value = strdup( value );
        else
            dict->objects[dict->count].value = NULL;
        dict->count++;
    }
}

void hb_dict_unset( hb_dict_t ** dict_ptr, const char * key )
{
    hb_dict_t * dict = *dict_ptr;
    if( !dict || !dict->objects || !key || !strlen( key ) )
        return;
    int i;
    for( i = 0; i < dict->count; i++ )
        if( !strcmp( key, dict->objects[i].key ) )
        {
            free( dict->objects[i].key );
            if( dict->objects[i].value )
                free( dict->objects[i].value );
            if( i != --dict->count )
                memmove( &dict->objects[i], &dict->objects[i+1],
                         sizeof( hb_dict_entry_t ) * ( dict->count - i ) );
        }
}

hb_dict_entry_t * hb_dict_get( hb_dict_t * dict, const char * key )
{
    if( !dict || !dict->objects || !key || !strlen( key ) )
        return NULL;
    int i;
    for( i = 0; i < dict->count; i++ )
        if( !strcmp( key, dict->objects[i].key ) )
            return &dict->objects[i];
    return NULL;
}

hb_dict_entry_t * hb_dict_next( hb_dict_t * dict, hb_dict_entry_t * previous )
{
    if( dict == NULL || dict->objects == NULL || !dict->count )
        return NULL;
    if( previous == NULL )
        return &dict->objects[0];
    unsigned int prev_index = previous - dict->objects;
    if( prev_index + 1 < dict->count )
            return &dict->objects[prev_index+1];
    return NULL;
}

hb_dict_t * hb_encopts_to_dict( const char * encopts, int encoder )
{
    hb_dict_t * dict = NULL;
    if( encopts && *encopts )
    {
        char *cur_opt, *opts_start, *value;
        const char *name;
        dict = hb_dict_init( 10 );
        if( !dict )
            return NULL;
        cur_opt = opts_start = strdup( encopts );
        if( opts_start )
        {
            while( *cur_opt )
            {
                name = cur_opt;
                cur_opt += strcspn( cur_opt, ":" );
                if( *cur_opt )
                {
                    *cur_opt = 0;
                    cur_opt++;
                }
                value = strchr( name, '=' );
                if( value )
                {
                    *value = 0;
                    value++;
                }
                // x264 has multiple names for some options
                if( encoder == HB_VCODEC_X264 )
                    name = hb_x264_encopt_name( name );
#ifdef USE_X265
                // x265 has multiple names for some options
                if( encoder == HB_VCODEC_X265 )
                    name = hb_x265_encopt_name( name );
#endif
                hb_dict_set( &dict, name, value );
            }
        }
        free( opts_start );
    }
    return dict;
}

char * hb_dict_to_encopts( hb_dict_t * dict )
{
    int first_opt = 1;
    char *tmp, *encopts_tmp, *encopts = NULL;
    hb_dict_entry_t * entry = NULL;
    while( ( entry = hb_dict_next( dict, entry ) ) )
    {
        tmp = hb_strdup_printf( "%s%s%s%s",
                                first_opt    ? "" : ":",
                                entry->key,
                                entry->value ? "=" : "",
                                entry->value ? entry->value : "" );
        if( tmp )
        {
            encopts_tmp = hb_strncat_dup( encopts, tmp, strlen( tmp ) );
            if( encopts_tmp )
            {
                if( encopts )
                    free( encopts );
                encopts = encopts_tmp;
            }
            first_opt = 0;
            free( tmp );
        }
    }
    return encopts;
}
