//----------------------------------------------------------------------------
//
// File:        support.cpp
// Date:        15-Jan-2003
// Programmer:  Marc Rousseau
//
// Description:
//
// Copyright (c) 2003-2004 Marc Rousseau, All Rights Reserved.
//
// 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
//
// Revision History:
//
//   15-Jan-2003    Renamed from original fileio.cpp
//
//----------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <map>
#if defined( __GNUC__ )
	#include <dirent.h>
	#include <fnmatch.h>
	#include <unistd.h>
#endif
#if defined( OS_WINDOWS )
	#include <windows.h>
#endif
#include "common.hpp"
#include "logger.hpp"
#include "support.hpp"

DBG_REGISTER( __FILE__ );

#if !defined( TI_DATA_DIR )
	#define TI_DATA_DIR "/opt/ti99sim"
#endif

#if defined( OS_LINUX ) || defined( OS_MACOSX )
	#define MKDIR( x,y ) mkdir( x, y )
	const char FILE_SEPERATOR = '/';
	const char *COMMON_PATH = TI_DATA_DIR;
#elif defined( OS_OS2 ) || defined( OS_WINDOWS )
	#include <direct.h>
	#define MKDIR( x,y ) mkdir( x )
	const char FILE_SEPERATOR = '\\';
	const char *COMMON_PATH = "";
#elif defined( OS_AMIGAOS )
	#include <direct.h>
	#define MKDIR( x,y ) mkdir( x )
	const char FILE_SEPERATOR = '/';
	const char *COMMON_PATH = TI_DATA_DIR;
#endif

static char home[ 256 ] = ".";

static int CreateHomePath( )
{
	FUNCTION_ENTRY( nullptr, "CreateHomePath", true );

	const char *ptr = getenv( "HOME" );

#if defined( OS_WINDOWS )
		char fileName[ 256 ];
		if( ptr == nullptr )
		{
			GetModuleFileName( nullptr, fileName, 256 );
			char *end = strrchr( fileName, FILE_SEPERATOR );
			if( end != nullptr )
			{
				*end = '\0';
			}
			ptr = fileName;
		}
#endif

	sprintf( home, "%s%c.ti99sim", ptr ? ptr : ".", FILE_SEPERATOR );
	MKDIR( home, 0775 );

	return 0;
}

static int x = CreateHomePath( );

const char *HOME_PATH = home;

bool IsWriteable( const char *filename )
{
	FUNCTION_ENTRY( nullptr, "IsWriteable", true );

	bool retVal = false;

	struct stat info;
	if( stat( filename, &info ) == 0 )
	{
#if defined( OS_WINDOWS ) || defined( OS_AMIGAOS )
			if( info.st_mode & S_IWRITE )
			{
				retVal = true;
			}
#else
			if( getuid( ) == info.st_uid )
			{
				if( info.st_mode & S_IWUSR )
				{
					retVal = true;
				}
			}
			else if( getgid( ) == info.st_gid )
			{
				if( info.st_mode & S_IWGRP )
				{
					retVal = true;
				}
			}
			else
			{
				if( info.st_mode & S_IWOTH )
				{
					retVal = true;
				}
			}
#endif
	}
	else
	{
		// TBD: Check write permissions to the directory
		retVal = true;
	}

	return retVal;
}

static bool TryPath( const std::string &path, const std::string &filename )
{
	FUNCTION_ENTRY( nullptr, "TryPath", true );

	std::string buffer;
	buffer.reserve( path.size( ) + filename.size( ) + 2 );

	buffer = path + FILE_SEPERATOR + filename;

	FILE *file = fopen( buffer.c_str( ), "rb" );
	if( file != nullptr )
	{
		fclose( file );
		return true;
	}

	return false;
}

std::string LocateFile( const std::string &filename, const std::string &path )
{
	FUNCTION_ENTRY( nullptr, "LocateFile", true );

	if( filename.empty( ))
	{
		return filename;
	}

	// If we were given an absolute path, see if it's valid or not
#if defined( OS_WINDOWS )
		if(( filename[ 0 ] == FILE_SEPERATOR ) || ( filename[ 1 ] == ':' ))
		{
#else
		if( filename[ 0 ] == FILE_SEPERATOR )
		{
#endif
			FILE *file = fopen( filename.c_str( ), "rb" );
			if( file != nullptr )
			{
				fclose( file );
				return filename;
			}
			return "";
		}

		std::string fullname = path;
		if( path.empty( ) == false )
		{
			fullname += FILE_SEPERATOR;
		}
		fullname += filename;

		// Try: CWD, CWD/path
		if( TryPath( ".", filename ) == true )
		{
			return filename;
		}
		if( TryPath( ".", fullname ) == true )
		{
			return fullname;
		}

		std::string fullpath;

		// Try: ~/.ti99sim/path, /opt/ti99sim/path
		if(( TryPath( fullpath = HOME_PATH, fullname )   == true ) ||
		   ( TryPath( fullpath = COMMON_PATH, fullname ) == true ))
		{
			return fullpath + FILE_SEPERATOR + fullname;
		}

		return "";
}

std::list<std::string> GetFiles( const std::string &directory, const std::string &pattern )
{
	static const char *paths[ ] =
	{
		COMMON_PATH,
		HOME_PATH,
		".",
	};

	std::map<std::string,std::string> files;

	for( auto path : paths )
	{
		std::string testDir = path;
		testDir += FILE_SEPERATOR + directory;

		DIR *dir = opendir( testDir.c_str( ));
		if( dir != nullptr )
		{
			dirent *dp = nullptr;

			while(( dp = readdir( dir )) != nullptr )
			{
				if((( dp->d_type & DT_DIR ) == 0 ) && ( fnmatch( pattern.c_str( ), dp->d_name, FNM_CASEFOLD ) == 0 ))
				{
					files[ dp->d_name ] = testDir;
				}
			}

			closedir( dir );
		}
	}

	std::list<std::string> list;

	for( auto iter : files )
	{
		list.push_back( iter.second + FILE_SEPERATOR + iter.first );
	}

	list.sort( );

	return list;
}
