/* libwmf (api/bbuf.c): library for wmf conversion
   Copyright (C) 2000 - various; see CREDITS, ChangeLog, and sources

   The libwmf Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The libwmf Library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with the libwmf Library; see the file COPYING.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */


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

#include <stdio.h>

#include "wmfdefs.h"

#include "api/bbuf.h"

/**
 * Set custom metafile input-stream handler functions.
 * 
 * @param API       the API handle
 * @param fp_read   function to read a byte
 * @param fp_seek   function to set position
 * @param fp_tell   function to get position
 * @param user_data handle for user data
 * 
 * \b libwmf has simple methods for reading either from file or from memory, but many applications will want
 * to use customized variants of these. wmf_bbuf_input() enables this.
 * 
 * @verbatim
typedef int  (*wmfRead) (void* user_data);
typedef int  (*wmfSeek) (void* user_data,long position);
typedef long (*wmfTell) (void* user_data);
@endverbatim
 * 
 * \b wmfRead returns unsigned char cast to int, or EOF (cf. fgetc())
 * 
 * \b wmfSeek returns (-1) on error, otherwise 0 (cf. fseek())
 * 
 * \b wmfTell returns (-1) on error, otherwise current position (cf. ftell())
 * 
 * @return Returns the library error state (\b wmf_E_None on success).
 *         Possible library error state of \b wmf_E_Glitch, if any of the three functions is zero.
 */
wmf_error_t wmf_bbuf_input (wmfAPI* API,wmfRead fp_read,wmfSeek fp_seek,wmfTell fp_tell,void* user_data)
{	if (ERR (API)) return (API->err);

	if ((fp_read == 0) || (fp_seek == 0) || (fp_tell == 0))
	{	WMF_ERROR (API,"wmf_bbuf_input: null arg. given unexpectedly!");
		API->err = wmf_E_Glitch;
	}
	else
	{	API->buffer_data = user_data;

		API->bbuf.read = fp_read;
		API->bbuf.seek = fp_seek;
		API->bbuf.tell = fp_tell;
	}

	return (API->err);
}

/**
 * Open file as metafile.
 * 
 * @param API  the API handle
 * @param file file name
 * 
 * Simple method for reading from file.
 * 
 * @return Returns the library error state (\b wmf_E_None on success).
 *         Possible library error states of \b wmf_E_Glitch or \b wmf_E_BadFile.
 */
wmf_error_t wmf_file_open (wmfAPI* API,const char* file)
{	wmfBBufFileInfo* file_info = 0;

	if (ERR (API)) return (API->err);

	if (API->buffer_data)
	{	WMF_ERROR (API,"wmf_file_open: input stream already open!");
		API->err = wmf_E_Glitch;
		return (API->err);
	}

	file_info = (wmfBBufFileInfo*) wmf_malloc (API,sizeof (wmfBBufFileInfo));

	if (ERR (API)) return (API->err);

	if ((file_info->file = fopen (file,"rb")) == 0)
	{	WMF_ERROR (API,"wmf_file_open: unable to open file for reading.");
		wmf_free (API,file_info);
		API->err = wmf_E_BadFile;
		return (API->err);
	}

	wmf_bbuf_input (API,wmf_file_read,wmf_file_seek,wmf_file_tell,(void*) file_info);

	if (ERR (API))
	{	wmf_file_close (API);
		return (API->err);
	}

	return (API->err);
}

/**
 * Close metafile input file stream.
 * 
 * @param API the API handle
 * 
 * @return Returns the library error state (\b wmf_E_None on success).
 *         Possible library error state of \b wmf_E_Glitch.
 */
wmf_error_t wmf_file_close (wmfAPI* API)
{	wmfBBufFileInfo* file_info = (wmfBBufFileInfo*) (API->buffer_data);

	if (API->buffer_data == 0)
	{	WMF_ERROR (API,"wmf_file_close: attempt to close unopened stream!");
		API->err = wmf_E_Glitch;
		return (API->err);
	}

	fclose (file_info->file);

	wmf_free (API,API->buffer_data);

	API->buffer_data = 0;

	API->bbuf.read = 0;
	API->bbuf.seek = 0;
	API->bbuf.tell = 0;

	return (API->err);
}

/**
 * @internal
 */
int wmf_file_read (void* user_data)
{	wmfBBufFileInfo* file_info = (wmfBBufFileInfo*) user_data;

	return (fgetc (file_info->file));
}

/**
 * @internal
 */
int wmf_file_seek (void* user_data,long pos)
{	wmfBBufFileInfo* file_info = (wmfBBufFileInfo*) user_data;

	return (fseek (file_info->file,pos,SEEK_SET));
}

/**
 * @internal
 */
long wmf_file_tell (void* user_data)
{	wmfBBufFileInfo* file_info = (wmfBBufFileInfo*) user_data;

	return (ftell (file_info->file));
}

/**
 * Open metafile in memory.
 * 
 * @param API    the API handle
 * @param mem    the metafile in memory
 * @param length the length in bytes of metafile data
 * 
 * Simple method for reading from memory as array of unsigned char.
 * 
 * @return Returns the library error state (\b wmf_E_None on success).
 *         Possible library error state of \b wmf_E_Glitch.
 */
wmf_error_t wmf_mem_open (wmfAPI* API,unsigned char* mem,long length)
{	wmfBBufMemInfo* mem_info = 0;

	if (ERR (API)) return (API->err);

	if (API->buffer_data)
	{	WMF_ERROR (API,"wmf_mem_open: input stream already open!");
		API->err = wmf_E_Glitch;
		return (API->err);
	}

	if ((mem == 0) || (length <= 0))
	{	WMF_ERROR (API,"wmf_mem_open: null or improper buffer!");
		API->err = wmf_E_Glitch;
		return (API->err);
	}

	mem_info = (wmfBBufMemInfo*) wmf_malloc (API,sizeof (wmfBBufMemInfo));

	if (ERR (API)) return (API->err);

	mem_info->mem = mem;
	mem_info->ptr = mem;

	mem_info->pos = 0;
	mem_info->length = length;

	wmf_bbuf_input (API,wmf_mem_read,wmf_mem_seek,wmf_mem_tell,(void*) mem_info);

	if (ERR (API))
	{	wmf_mem_close (API);
		return (API->err);
	}

	return (API->err);
}

/**
 * Close metafile input memory stream.
 * 
 * @param API the API handle
 * 
 * @return Returns the library error state (\b wmf_E_None on success).
 */
wmf_error_t wmf_mem_close (wmfAPI* API)
{	wmf_free (API,API->buffer_data);

	API->buffer_data = 0;

	API->bbuf.read = 0;
	API->bbuf.seek = 0;
	API->bbuf.tell = 0;

	return (API->err);
}

/**
 * @internal
 */
int wmf_mem_read (void* user_data)
{	int byte = EOF;

	wmfBBufMemInfo* mem_info = (wmfBBufMemInfo*) user_data;

	if (mem_info->pos < mem_info->length)
	{	byte = (int) (*(mem_info->ptr));

		mem_info->ptr++;
		mem_info->pos++;
	}

	return (byte);
}

/**
 * @internal
 */
int wmf_mem_seek (void* user_data,long pos)
{	wmfBBufMemInfo* mem_info = (wmfBBufMemInfo*) user_data;

	if ((pos < 0) || (pos >= mem_info->length)) return (-1);

	mem_info->ptr = mem_info->mem + pos;
	mem_info->pos = pos;

	return (0);
}

/**
 * @internal
 */
long wmf_mem_tell (void* user_data)
{	wmfBBufMemInfo* mem_info = (wmfBBufMemInfo*) user_data;

	return (mem_info->pos);
}
