/*
 * ice_streamer_utils.cpp
 *
 *  Created on: Jul 1, 2012
 *      Author: ergino
 */

#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>

#include "frost_common.h"
#include "frost_basicstring.h"
#include "frost_str.h"
#include "frost_mem.h"
#include "ice_streamer.h"

#include "utilities_debug/cabot_assert.h"
#include "utilities_debug/trace.h"
#include "frost_mem.h"
#include "frost_list.h"

TRACE_IMPLEMENT("ice/ice_streamer_utils");

/**
 * Convert a list of variable arguments into a list of strings.
 * Note: Assumes that the arguments provided are all strings.
 * @param first The first string. Cannot be NULL.
 * @param ap List of variable arguments.
 * @return A newly allocated list of strings.
 */
char **
ice_streamer_valist_to_strlist (const char *first, va_list ap)
{
    const char *cur = first;
    char **ret = NULL;
    int i, size = sizeof (char *);

    if (first == NULL)
        abort ();

    for (i = 0; cur != NULL; i++) {
        size += sizeof (char *);
        ret = (char**)realloc (ret, size);
        ret[i] = strdup (cur);
        cur = va_arg (ap, char *);
    }
    ret[i] = NULL;

    return ret;
}

/**
 * Convert a variable number of arguments into a list of strings.
 * Note: Assumes that the arguments provided are all strings.
 * @param first The first string. Cannot be NULL.
 * @param ... Variable number of strings.
 * @return A newly allocated list of strings.
 */
char **
ice_streamer_vargs_to_strlist (const char *first, ...)
{
    va_list ap;
    char **ret = NULL;

    if (first == NULL)
        abort ();

    va_start (ap, first);
    ret = ice_streamer_valist_to_strlist (first, ap);
    va_end (ap);

    return ret;
}

/**
 * Get the number of strings in a list. Note that the real length of the
 * (char **) array will be larger by 1 element (containing the terminating NULL).
 * @param data The list of strings
 * @return Number of strings
 */
int
ice_streamer_strlist_len (char **data)
{
    int i;
    if (data == NULL)
        abort ();
    for (i = 0; data[i] != NULL; i++);
    return i;
}

/**
 * Destroy a list of strings. Equivalent to frost_strfreev().
 * @param data The list to destroy.
 */
void
ice_streamer_strlist_destroy (char **data)
{
    int i;
    if (data == NULL)
        abort ();
    for (i = 0; data[i] != NULL; i++) {
        free (data[i]);
    }
    free (data);
}

/**
 * Return a copy of a list with newstr prepended.
 * @param data The original list.
 * @param newstr The string to prepend.
 * @return A newly allocated list of strings.
 */
char **
ice_streamer_strlist_prepend_copy (char **data, char *newstr) {
    int i;
    char **ret;

    ret = (char**)malloc ((ice_streamer_strlist_len (data) + 2) * sizeof (char *));
    ret[0] = strdup (newstr);

    for (i = 0; data[i] != NULL; i++)
        ret[i+1] = strdup (data[i]);
    ret[i+1] = NULL;

    return ret;
}

/**
 * Return a deep copy of a list.
 * @param strlist The original list.
 * @return A newly allocated list of strings.
 */
char **
ice_streamer_strlist_copy (char **strlist)
{
    char **ret;
    int i;

    ret = (char**)malloc ((ice_streamer_strlist_len (strlist) + 1) * sizeof (char *));

    for (i = 0; strlist[i] != NULL; i++) {
        ret[i] = strdup (strlist[i]);
    }

    ret[i] = NULL;

    return ret;
}

char *
ice_streamer_build_path (const char *first, ...)
{
    va_list ap;
    char confdir[ICE_STREAMER_PATH_MAX] = "/conf/";
    return confdir;
}

static char *
path_get_body (const char *path)
{
    char *beg, *end;

    frost_return_val_if_fail (path, NULL);

    beg = strstr (path, "://");

    if (!beg) {
        return frost_strndup (path, strcspn (path, "/"));
    }

    beg += 3;
    end = strchr (beg, '/');

    if (!end) {
        return frost_strdup (path);
    }

    return frost_strndup (path, end - path);
}

/* frost_path_get_dirname returns "file:" with "file:///foo.pls", while "file://"
   is wanted. */
static char *
path_get_dirname (const char *path)
{
    frost_uint i, n = 0;

    frost_return_val_if_fail (path, NULL);

    for (i = 0; path[i] ; i++) {
        if (path[i] == '/') {
            n = i;
        }
    }

    return frost_strndup (path, n);
}

char *
ice_streamer_build_playlist_url (const char *plspath, const char *file)
{
    char *url;
    char *path;

    frost_return_val_if_fail (plspath, NULL);
    frost_return_val_if_fail (file, NULL);

    if (strstr (file, "://") != NULL) {
        return frost_strdup (file);
    }

    if (file[0] == '/') {
        path = path_get_body (plspath);
        url = frost_strconcat (path, file, NULL);
    } else {
        path = path_get_dirname (plspath);
        url = frost_strconcat (path, "/", file, NULL);
    }

    frost_free (path);
    return url;
}

static void
ice_streamer_stream_type_destroy (ice_streamer_object_t *obj)
{
    ice_streamer_stream_type_t *st = (ice_streamer_stream_type_t *)obj;
    FROST_List *n;

    frost_free (st->name);

    for (n = st->list; n; n = frost_list_next (n)) {
        ice_streamer_stream_type_val_t *val = (ice_streamer_stream_type_val_t *)n->data;
        if (val->type == STRING) {
            frost_free (val->d.string);
        }
        frost_free (val);
    }

    frost_list_free (st->list);
}

ice_streamer_stream_type_t *
ice_streamer_stream_type_parse (va_list ap)
{
    ice_streamer_stream_type_t *res;

    res = ice_streamer_object_new (ice_streamer_stream_type_t, ice_streamer_stream_type_destroy);
    if (!res) {
        return NULL;
    }

    res->priority = -1;
    res->name = NULL;

    for (;;) {
        ice_streamer_stream_type_val_t *val;
        ice_streamer_stream_type_key_t key;

        key = (ice_streamer_stream_type_key_t)va_arg (ap, int);
        if (key == ICE_STREAM_TYPE_END)
            break;

        if (key == ICE_STREAM_TYPE_NAME) {
            res->name = frost_strdup (va_arg (ap, char *));
            continue;
        }

        if (key == ICE_STREAM_TYPE_PRIORITY) {
            res->priority = va_arg (ap, int);
            continue;
        }

        val = frost_new0 (ice_streamer_stream_type_val_t, 1);
        val->key = key;

        switch (val->key) {
        case ICE_STREAM_TYPE_MIMETYPE:
        case ICE_STREAM_TYPE_URL:
            val->type = STRING;
            val->d.string = frost_strdup (va_arg (ap, char *));
            break;
        case ICE_STREAM_TYPE_FMT_FORMAT:
        case ICE_STREAM_TYPE_FMT_CHANNELS:
        case ICE_STREAM_TYPE_FMT_SAMPLERATE:
            val->type = INTEGER;
            val->d.num = va_arg (ap, int);
            break;
        default:
            TRACE_INFO(("UNKNOWN TYPE!!"));
            frost_free (val);
            ice_streamer_object_unref (res);
            return NULL;
        }
        res->list = frost_list_append (res->list, val);
    }

    if (!res->name) {
        const char *mime = ice_streamer_stream_type_get_str (res, ICE_STREAM_TYPE_MIMETYPE);
        const char *url = ice_streamer_stream_type_get_str (res, ICE_STREAM_TYPE_URL);

        if (mime && url) {
            res->name = frost_strconcat (mime, ":", url, NULL);
        } else if (mime) {
            res->name = frost_strdup (mime);
        } else {

        }

        frost_strdelimit (res->name, ".", '_');
    }

    if (res->priority < 0) {
        res->priority = ICE_STREAM_TYPE_PRIORITY_DEFAULT;
    }

    return res;
}

const char *
ice_streamer_stream_type_get_str (const ice_streamer_stream_type_t *st, ice_streamer_stream_type_key_t key)
{
    FROST_List *n;

    if (key == ICE_STREAM_TYPE_NAME) {
        return st->name;
    }

    for (n = st->list; n; n = frost_list_next (n)) {
        ice_streamer_stream_type_val_t *val = (ice_streamer_stream_type_val_t *)n->data;
        if (val->key == key) {
            if (val->type != STRING) {
                TRACE_INFO(("Key passed to get_str is not string"));
                return NULL;
            }
            return val->d.string;
        }
    }
    return NULL;
}


frost_int
ice_streamer_stream_type_get_int (const ice_streamer_stream_type_t *st, ice_streamer_stream_type_key_t key)
{
    FROST_List *n;

    if (key == ICE_STREAM_TYPE_PRIORITY) {
        return st->priority;
    }
    if(!st)
    {
        return -1;
    }
    for (n = st->list; n; n = frost_list_next (n)) {
        ice_streamer_stream_type_val_t *val = (ice_streamer_stream_type_val_t *)n->data;
        if (val->key == key) {
            if (val->type != INTEGER) {
                TRACE_INFO(("Key passed to get_int is not int"));
                return -1;
            }
            return val->d.num;
        }
    }
    return -1;
}

frost_int
ice_streamer_sample_size_get (ice_streamer_sample_format_t fmt)
{
    switch (fmt) {
    case ICE_SAMPLE_FORMAT_UNKNOWN:
        return -1;
    case ICE_SAMPLE_FORMAT_S8:
        return sizeof (ice_samples8_t);
    case ICE_SAMPLE_FORMAT_U8:
        return sizeof (ice_sampleu8_t);
    case ICE_SAMPLE_FORMAT_S16:
        return sizeof (ice_samples16_t);
    case ICE_SAMPLE_FORMAT_U16:
        return sizeof (ice_sampleu16_t);
    case ICE_SAMPLE_FORMAT_S32:
        return sizeof (ice_samples32_t);
    case ICE_SAMPLE_FORMAT_U32:
        return sizeof (ice_sampleu32_t);
    case ICE_SAMPLE_FORMAT_FLOAT:
        return sizeof (ice_samplefloat_t);
    case ICE_SAMPLE_FORMAT_DOUBLE:
        return sizeof (ice_sampledouble_t);
    }
    return -1;
}

frost_uint
ice_streamer_sample_ms_to_samples (const ice_streamer_stream_type_t *st, frost_uint milliseconds)
{
    frost_int rate;
    rate = ice_streamer_stream_type_get_int (st, ICE_STREAM_TYPE_FMT_SAMPLERATE);
    return (frost_uint)(((double) rate) * milliseconds / 1000);
}

/**
 * Convert from samples to milliseconds for this format
 */
frost_uint
ice_streamer_sample_samples_to_ms (const ice_streamer_stream_type_t *st, frost_uint samples)
{
    frost_int rate;
    rate = ice_streamer_stream_type_get_int (st, ICE_STREAM_TYPE_FMT_SAMPLERATE);
    return (frost_uint) (((double)samples) * 1000.0 / rate);
}

/**
 * Convert from bytes to milliseconds for this format
 */
frost_uint
ice_streamer_sample_bytes_to_ms (const ice_streamer_stream_type_t *st, frost_uint bytes)
{
    frost_uint samples = bytes / ice_streamer_sample_frame_size_get (st);
    return ice_streamer_sample_samples_to_ms (st, samples);
}

frost_int
ice_streamer_sample_frame_size_get (const ice_streamer_stream_type_t *st)
{
    frost_int format, channels;
    format = ice_streamer_stream_type_get_int (st, ICE_STREAM_TYPE_FMT_FORMAT);
    channels = ice_streamer_stream_type_get_int (st, ICE_STREAM_TYPE_FMT_CHANNELS);
    return ice_streamer_sample_size_get ((ice_streamer_sample_format_t)format) * channels;
}

static frost_bool
match_val (ice_streamer_stream_type_val_t *vin, ice_streamer_stream_type_val_t *vout)
{
//    TRACE_ERROR(("===============> vin->type : %d , vout->type : %d \n",vin->type , vout->type));
    if (vin->type != vout->type)
        return frost_false;
    switch (vin->type) {
    case STRING:
//        TRACE_ERROR(("===============> vin->string : %s , vout->string : %s \n",vin->d.string, vout->d.string));
        return frost_pattern_match_simple (vin->d.string, vout->d.string);
    case INTEGER:
//        TRACE_ERROR(("===============> vin->value : %d , vout->value : %d \n",vin->d.num, vout->d.num));
        return (vin->d.num == vout->d.num) ? frost_true : frost_false;
    }
    return frost_false;
}

frost_bool
ice_streamer_stream_type_match (const ice_streamer_stream_type_t *in_type, const ice_streamer_stream_type_t *out_type)
{
    FROST_List *in;
    frost_bool found = frost_false;
    
    for (in = in_type->list; in; in = frost_list_next (in)) {
        ice_streamer_stream_type_val_t *inval = (ice_streamer_stream_type_val_t *)in->data;
        
        FROST_List *n;
        for (n = out_type->list; n; n = frost_list_next (n)) {
            ice_streamer_stream_type_val_t *outval = (ice_streamer_stream_type_val_t *)n->data;
            //TRACE_ERROR(("===============> inval->key : %d , outval->key : %d \n",inval->key , outval->key));
            if (inval->key == outval->key) {
                //TRACE_ERROR(("===============> MATCHED inval->key : %d , outval->key : %d \n",inval->key , outval->key));
                if (match_val (inval, outval))
                {
                    found = frost_true;
                    break;
                }
            }
        }
    }

    return found;
}


