/*
 * fgcom - VoIP-Client for the FlightGear-Radio-Infrastructure
 *
 * This program 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 2 of the
 * License, or (at your option) any later version.
 * 
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA  02110-1301, USA.
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef _MSC_VER
#include <WinSock2.h> // which included <Windows.h>
#else
#include <unistd.h>
#endif

#ifdef __APPLE__
#include <CoreFoundation/CoreFoundation.h>
#include <mach-o/dyld.h> /* for _NSGetExecutablePath() */
#endif

#include <fcntl.h>
#include <sys/stat.h>

#include <simgear/debug/logstream.hxx>
#include "fgcom.hxx"

#ifndef bcopy
#define bcopy(from, to, n) memcpy(to, from, n)
#endif

static int s_index;
static int s_file_handle;
static char *s_content;
static int s_size;

/**
 *
 * \fn void parser_init(void)
 *
 * \brief Starts parser initialization.
 *
 */
int parser_init(const char *filename)
{
	struct stat l_stat;
	ssize_t l_nbytes;
	int l_status;
    int oflag = O_RDONLY;
#ifdef _MSC_VER
    oflag |= _O_BINARY; /* if comparing to stat size then must be binary */
#endif

	s_index = 0;

	if((s_file_handle = open(filename, oflag)) < 0)
		return (s_file_handle);

	fstat(s_file_handle, &l_stat);

	l_status = -1;
	if((s_content = (char *)malloc((l_stat.st_size + 1) * sizeof(char))) != NULL)
	{
		if((l_nbytes = read(s_file_handle, s_content, l_stat.st_size)) == l_stat.st_size)
		{
			l_status = 0;
			s_size = l_stat.st_size;
		}
	}
	close(s_file_handle);

	return(l_status);
}

/**
 *
 * \fn void parser_exit(void)
 *
 * \brief Exits parser.
 *
 */
void parser_exit(void)
{
	free(s_content);
}

/**
 *
 * \fn int parser_get_next_value(const char *line, float *value)
 *
 * \brief Extract a numeric value.
 *
 * \param line	pointer on the line extracted from the input file.
 * \param value	pointer on the returned value.
 *
 * \return Returns 0 if value successfully extracted. Otherwhise, returns
 * a negative value meaning that an error occured.
 *
 */
int parser_get_next_value(double *value)
{
	int l_status = 0;
	unsigned int l_j;
	unsigned int l_size;
	char *l_buf;

	/* Check if we are already at the end of the string. */
	if(s_index >= s_size)
		return(-1);

	/* Enter main parser loop. */
	while((s_index < s_size) && (l_status == 0))
	{
		/* Search for something different than an espace or tab. */
		while(  (s_content[s_index] == ' ' || s_content[s_index] == '\t') &&
				(s_index < s_size) )
			s_index++;

		/* If we have reached end of file, we exit now. */
		if (s_index >= s_size)
			return(-1);

		/* If character is a CR, we restart for next line. */
		if(s_content[s_index] == '\n')
		{
			s_index++;
			continue;
		}

		/* Is it a comment ? */
		if(s_content[s_index] == '#')
		{
			/* Yes, go until end of line. */
			while((s_content[s_index] != '\n') && (s_index < s_size))
				s_index++;
		}
		else
		{
			/* We have found something that is not a comment. */
			while((s_content[s_index] < '0' || s_content[s_index] > '9') && (s_index < s_size))
				s_index++;

			if(s_index < s_size)
			{
				l_j = s_index + 1;
				while(  ((s_content[l_j] >= '0' && s_content[l_j] <= '9') ||
						 (s_content[l_j] == '.' || s_content[l_j] == ',')) &&
						((s_content[l_j] != '\n') && (l_j < (unsigned int)s_size)) )
					l_j++;

				l_size = l_j - s_index + 1;
				if((l_buf = (char *)malloc(l_size * sizeof(char))) != NULL)
				{
					/* Initialize buffer with O. */
					memset((void *)l_buf, 0, l_size);
					bcopy((const void *)(s_content + s_index), (void *)l_buf, l_size - 1);
					/* Convert string into double. */
					*value = atof(l_buf);
					/* Buffer is not needed any longer. */
					free(l_buf);
					/* Prepare for next value. */
					s_index = l_j + 1;
					break;
				}
			}
		}
	} /* while((s_index < s_size) && (l_status == 0)) */

	return(0);
}

/* cross-platform stat and check if directory or file
 * return 2 == directory
 * return 1 == file
 * return 0 == neither
 * NOTE: Think this fails in Windows if 
 *       the path has a trailing path separator
 *       and in some cases even if the path contains a forward (unix) separator
 * TODO: Should copy the path, and fix it for stat
 */

#ifdef _MSC_VER
#define M_ISDIR(a) (a & _S_IFDIR)
#else
#define M_ISDIR S_ISDIR
#endif

int is_file_or_directory( const char * path )
{
    struct stat buf;
    if ( stat(path,&buf) == 0 ) {
        if (M_ISDIR(buf.st_mode))
            return 2;
        return 1;
    }
    return 0;
}

/* trim to base binary path IN BUFFER,
 * essentially removing the executable name
 */
void trim_base_path_ib( char *path )
{
    size_t len = strlen(path);
    size_t i, off;
    int c;
    off = 0;
    for (i = 0; i < len; i++) {
        c = path[i];
        if (( c == '/' ) || ( c == '\\')) {
            off = i + 1;    // get after separator
#ifdef _MSC_VER
            if ( c == '/' )
                path[i] = '\\';
#endif // _MSC_VER
        }
    }
    path[off] = 0;
}

/*
 * get data path per OS 
 * In Windows and OSX the compiler only supplies a partial data file path,
 *  so this is to get the current binary installed path.
 * In *nix the full path is supplied, so this does nothing, except zero the path
 *
 */
int get_data_path_per_os( char *path, size_t len )
{
#if defined(MACOSX)
// if we're running as part of an application bundle, return the bundle's
// resources directory
// The following code looks for the base package inside the application
// bundle, in the standard Contents/Resources location.
    
  CFURLRef resourcesUrl = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle());
  if (resourcesUrl) {
      CFURLGetFileSystemRepresentation(resourcesUrl, true, (UInt8*) path, len);
      CFRelease(resourcesUrl);
      // append trailing seperator since CF doesn't
      len = strlen(path);
      path[len] = '/';
      path[len+1] = 0;
      return 0;
  }
  
// we're unbundled, simply return the executable path
    unsigned int size = (unsigned int) len;
    if (_NSGetExecutablePath(path, &size) == 0)  {
        // success
        trim_base_path_ib(path);
    } else {
        SG_LOG( SG_GENERAL, SG_ALERT, "ERROR: path buffer too small; need size " << size );
        return 1;
    }
#elif defined(_MSC_VER)
    unsigned int size = GetModuleFileName(NULL,path, len);
    if (size && (size != len)) {
        // success
        trim_base_path_ib(path);
    } else {
        if (size) {
            SG_LOG( SG_GENERAL, SG_ALERT, "ERROR: GetModuleFileName: path buffer too small; need size more than " << len );
        } else {
            SG_LOG( SG_GENERAL, SG_ALERT, "ERROR: GetModuleFileName FAILED!" );
        }
        return 1;
    }
#else
    path[0] = 0;
#endif // MACOSX | _MSC_VER | others   
    return 0;
}

/* eof - utils.cpp */