/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: sysinfo.cpp,v 1.1.2.2 2004/10/18 18:53:31 rggammon Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

#include "sysinfo.h"

#include "commonapp.h"

#include "appver.h"

#include <sys/utsname.h>

#include "hlxclib/stdio.h"
#include "hlxclib/ctype.h"
#include "hlxclib/string.h"

/* XXXRGG: Hack! Get rid of this when we fix preferences */
static gchar g_distcode[8];
static gchar g_origcode[8];

void
hx_sys_info_set_distcode (const gchar* distcode)
{
    g_strlcpy(g_distcode, distcode, sizeof(g_distcode));
}

void
hx_sys_info_set_origcode (const gchar* origcode)
{
    g_strlcpy(g_origcode, origcode, sizeof(g_origcode));
}


/* end hack */

typedef struct _HXSysInfo
{
    gboolean distribution_info_set;

    gchar* lsb_version;
    gchar* distrib_id;
    gchar* distrib_release;
    gchar* distrib_description;
    gchar* distrib_codename;    
    
} _HXSysInfo;


HXSysInfo*
hx_sys_info_new (void)
{
    return g_new0(HXSysInfo, 1);
}

void
hx_sys_info_destroy (HXSysInfo* info)
{
    g_free(info->lsb_version);
    g_free(info->distrib_id);
    g_free(info->distrib_release);
    g_free(info->distrib_description);
    g_free(info->distrib_codename);    

    g_free(info);    
}

static gboolean
get_distribution_info_from_etc_lsb_release(HXSysInfo* info)
{
    GError* error = NULL;
    GIOChannel* chan = NULL;
    gchar* line = NULL;
    gboolean result = FALSE;
    GIOStatus status;
    
    if(g_file_test("/etc/lsb-release", G_FILE_TEST_EXISTS))
    {
        info->distribution_info_set = TRUE;
        chan = g_io_channel_new_file("/etc/lsb-release", "r", &error);
    
        if(chan && !error)
        {
            for(;;)
            {
                do
                {
                    status = g_io_channel_read_line(chan, &line, NULL, NULL, &error);                
                } while(status == G_IO_STATUS_AGAIN);

                if(!line || status != G_IO_STATUS_NORMAL)
                {
                    g_free(line);
                    break;
                }

                /* process line */
                gchar* quoted_value;
                gchar* key;
                gchar* value;

                quoted_value = strchr(line, '=');
                if(quoted_value)
                {
                    *quoted_value = '\0';
                    quoted_value++;

                    gchar* newline = strchr(quoted_value, '\n');
                    if(newline)
                    {
                        *newline = '\0';
                    }
                    
                    key = line;
                    GError* shell_err = NULL;
                    
                    value = g_shell_unquote(quoted_value, &shell_err);
                    if(shell_err)
                    {
                        g_warning(error->message);
                        g_error_free(error);
                        error = NULL;
                    }
                    else if(strcmp(key, "LSB_VERSION") == 0)
                    {
                        g_free(info->lsb_version);
                        info->lsb_version = value;
                    }
                    else if(strcmp(key, "DISTRIB_ID") == 0)
                    {                        
                        g_free(info->distrib_id);
                        info->distrib_id = value;
                    }
                    else if(strcmp(key, "DISTRIB_RELEASE") == 0)
                    {                        
                        g_free(info->distrib_release);
                        info->distrib_release = value;
                    }
                    else if(strcmp(key, "DISTRIB_DESCRIPTION") == 0)
                    {                        
                        g_free(info->distrib_description);
                        info->distrib_description = value;
                    }
                    else if(strcmp(key, "DISTRIB_CODENAME") == 0)
                    {                        
                        g_free(info->distrib_codename);
                        info->distrib_codename = value;    
                    }
                    else
                    {
                        g_free(value);
                    }
                }
                
                g_free(line);                    
            } 
        }

        if(error)
        {
            g_warning(error->message);
            g_error_free(error);
            error = NULL;
        }

        if(chan)
        {
            g_io_channel_shutdown(chan, TRUE, &error);
        }
        
        if(error)
        {
            g_warning(error->message);
            g_error_free(error);
            error = NULL;
        }
    }

    return result;
}

static gboolean
get_distribution_name_from_etc_xxx_release(HXSysInfo* info)
{
    GIOChannel* chan = NULL;
    GIOStatus status;
    GError* error = NULL;
    guint i;
    gchar* line;
    gboolean result = FALSE;

    /* If we get here, it means that /etc/lsb-release does not exist.
       We look for some well-known distribution files in order to
       determine the DISTRIB_ID, and send the first line from these
       files as the DISTRIB_DESCRIPTION. */
    
    struct
    {
        gchar* id;
        gchar* release_file;
    } distro_map[] =
    {
        { "Debian",     "/etc/debian_version",      },
        { "Gentoo",     "/etc/gentoo-release",      },
        { "Fedora",     "/etc/fedora-release",      },
        { "Redhat",     "/etc/redhat-release",      },
        { "SuSE",       "/etc/SuSE-release",        },
        { "JDS",        "/etc/sun-release",         },
        { "Mandrake",   "/etc/mandrake-release",    },
        { "Turbolinux", "/etc/turbolinux-release",  },
        { "Slackware",  "/etc/slackware-release",   },
        { "Slackware",  "/etc/slackware-version",   },
        { "Yellow Dog", "/etc/yellowdog-release",   },
        { NULL,         "/etc/release",             }
    };

    for(i = 0; i < sizeof(distro_map) / sizeof(*distro_map); i++)
    {
        if(g_file_test(distro_map[i].release_file, G_FILE_TEST_EXISTS))
        {
            info->distribution_info_set = TRUE;

            g_free(info->distrib_id);
            info->distrib_id = g_strdup(distro_map[i].id);
            
            chan = g_io_channel_new_file(distro_map[i].release_file, "r", &error);
            if(chan && !error)
            {
                do
                {
                    status = g_io_channel_read_line(chan, &line, NULL, NULL, &error);                
                } while(status == G_IO_STATUS_AGAIN);

                if(line && status == G_IO_STATUS_NORMAL)
                {
                    gchar* trimmed = hxcommon_strtrim(line);

                    g_free(info->distrib_description);
                    info->distrib_description = g_strdup(trimmed);
                }

                g_free(line);
                
                break;
            }            
        } 
    }    

    if(error)
    {
        g_warning(error->message);
        g_error_free(error);
        error = NULL;
    }

    if(chan)
    {
        g_io_channel_shutdown(chan, TRUE, &error);
    }
        
    if(error)
    {
        g_warning(error->message);
        g_error_free(error);
        error = NULL;
    }
    
    return result;
}

static gchar*
get_distribution_info(HXSysInfo*         info,
                      HXSysInfoParamType type)
{
    gchar* query_result = NULL;

    if(!info->distribution_info_set)
    {
        /* Query for info & cache for future calls */
        get_distribution_info_from_etc_lsb_release(info);
    }

    if(!info->distribution_info_set)
    {
        get_distribution_name_from_etc_xxx_release(info);
    }

    info->distribution_info_set = TRUE;

    switch(type)
    {
        case HX_SYS_INFO_LSB_VERSION:
            if(info->lsb_version)
            {
                query_result = g_strdup(info->lsb_version);
            }
            break;
        
        case HX_SYS_INFO_DISTRIB_ID:
            if(info->distrib_id)
            {
                query_result = g_strdup(info->distrib_id);
            }
            break;
            
        case HX_SYS_INFO_DISTRIB_RELEASE:
            if(info->distrib_release)
            {
                query_result = g_strdup(info->distrib_release);
            }
            break;
            
        case HX_SYS_INFO_DISTRIB_DESCRIPTION:
            if(info->distrib_description)
            {
                query_result = g_strdup(info->distrib_description);
            }
            break;
            
        case HX_SYS_INFO_DISTRIB_CODENAME:
            if(info->distrib_description)
            {
                query_result = g_strdup(info->distrib_codename);
            }
            break;

        default:
            g_assert_not_reached();
    }

    return query_result;
}

gchar*
hx_sys_info_get_param (HXSysInfo*         info,
                       HXSysInfoParamType type)
{
    gchar* query_result = NULL;
    
    switch (type)
    {
        case HX_SYS_INFO_OPERATING_SYSTEM:
        case HX_SYS_INFO_KERNEL_VERSION:
        case HX_SYS_INFO_PROCESSOR_TYPE:
        {
            /* uname */
            int result;
            struct utsname uname_data;
            
            result = uname(&uname_data);
            if(result == 0)
            {
                if (type == HX_SYS_INFO_OPERATING_SYSTEM)
                {
                    query_result = g_strdup(uname_data.sysname);
                }
                else if (type == HX_SYS_INFO_KERNEL_VERSION)
                {
                    query_result = g_strdup(uname_data.release);
                }
                else if (type == HX_SYS_INFO_PROCESSOR_TYPE)
                {
                    query_result = g_strdup(uname_data.machine);
                }
            }
            break;
        }

        case HX_SYS_INFO_LSB_VERSION:
        case HX_SYS_INFO_DISTRIB_ID:
        case HX_SYS_INFO_DISTRIB_RELEASE:
        case HX_SYS_INFO_DISTRIB_DESCRIPTION:
        case HX_SYS_INFO_DISTRIB_CODENAME:
            query_result = get_distribution_info(info, type);
            break;

        case HX_SYS_INFO_PLAYER_NAME:
            query_result = g_strdup(APP_NAME_FULL);
            break;
            
        case HX_SYS_INFO_PLAYER_VERSION:
            query_result = g_strdup(TARVER_STRING_VERSION);
            break;
                        
        case HX_SYS_INFO_PLAYER_DISTCODE:
            query_result = g_strdup(g_distcode);
            break;

        case HX_SYS_INFO_PLAYER_ORIGCODE:
            query_result = g_strdup(g_origcode);
            break;

        case HX_SYS_INFO_GCC_VERSION:            
#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__)
            query_result = g_strdup_printf("%d.%d.%d",
                                           __GNUC__,
                                           __GNUC_MINOR__,
                                           __GNUC_PATCHLEVEL__);
#endif
            break;

        case HX_SYS_INFO_GETTEXT_LANGUAGE:
        {
            const gchar* language = hxcommon_get_gettext_language();
            if(language)
            {
                query_result = g_strdup(language);
            }
            break;
        }        
            
        default:
            g_warning("Unknown system information requested: %d", type);
            break;
    }

    return query_result;
}
