/*  Inti: Integrated Foundation Classes
 *  Copyright (C) 2003 The Inti Development Team.
 *
 *  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 Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library 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.
 */

//! @file inti/glib/iochannel.h
//! @brief A GIOChannel C++ interface.
//!
//! Provides IO Channels for portable support for using files, pipes and sockets.

#ifndef INTI_G_IOCHANNEL_H
#define INTI_G_IOCHANNEL_H

#ifndef INTI_OBJECT_H
#include <inti/object.h>
#endif

#ifndef INTI_SLOT_H
#include <inti/slot.h>
#endif

#ifndef INTI_UTF_STRING_H
#include <inti/utf-string.h>
#endif

#ifndef __G_IOCHANNEL_H__
#include <glib/giochannel.h>
#endif

namespace Inti {

namespace G {

class Error;

//! enum IOChannelError
//! The error codes returned by IOChannel operations.

enum IOChannelError
{
	IO_CHANNEL_ERROR_FBIG = G_IO_CHANNEL_ERROR_FBIG, //!< File too large.
	IO_CHANNEL_ERROR_INVAL = G_IO_CHANNEL_ERROR_INVAL, //!< Invalid argument.
	IO_CHANNEL_ERROR_IO = G_IO_CHANNEL_ERROR_IO, //!< IO error.
	IO_CHANNEL_ERROR_ISDIR = G_IO_CHANNEL_ERROR_ISDIR, //!< File is a directory.
	IO_CHANNEL_ERROR_NOSPC = G_IO_CHANNEL_ERROR_NOSPC, //!< No space left on device.
	IO_CHANNEL_ERROR_NXIO = G_IO_CHANNEL_ERROR_NXIO, //!< No such device or address.
	IO_CHANNEL_ERROR_OVERFLOW = G_IO_CHANNEL_ERROR_OVERFLOW, //!< Value too large for defined datatype.
	IO_CHANNEL_ERROR_PIPE = G_IO_CHANNEL_ERROR_PIPE, //!< Broken pipe.
	IO_CHANNEL_ERROR_FAILED = G_IO_CHANNEL_ERROR_FAILED //!< Some other error.

};

//! enum IOStatus
//! Describes the status of an IO operation.

enum IOStatus
{
	IO_STATUS_ERROR = G_IO_STATUS_ERROR, //!< An error occurred.
	IO_STATUS_NORMAL = G_IO_STATUS_NORMAL, //!< Success.
	IO_STATUS_EOF = G_IO_STATUS_EOF, //!< End of file.
	IO_STATUS_AGAIN = G_IO_STATUS_AGAIN //!< Resource temporarily unavailable.
};

//! @enum SeekType
//! Specifies the base position for a IOChannel::seek() operation.

enum SeekType
{
	IO_SEEK_CUR = G_SEEK_CUR, //!< The current position in the file.
	IO_SEEK_SET = G_SEEK_SET, //!< The start of the file.
	IO_SEEK_END = G_SEEK_END //!< The end of the file.
};

//! enum IOCondition
//! A bitwise combination representing a condition to watch for on an event source.

enum IOCondition
{
	IO_IN = G_IO_IN, //!< There is data to read.
	IO_OUT = G_IO_OUT, //!< Data can be written (without blocking).
	IO_PRI = G_IO_PRI, //!< There is urgent data to read.
	IO_ERR = G_IO_ERR, //!< Error condition.
	IO_HUP = G_IO_HUP, //!< Hung up (the connection has been broken, usually for pipes and sockets).
	IO_NVAL = G_IO_NVAL //!< Invalid request. The file descriptor is not open.
} ;

//! IOConditionField holds one or more conditions from the IOCondition enumeration OR'd together.

typedef unsigned int IOConditionField;

//! enum IOFlags
//! Specifies the properties of an IOChannel. Some of the flags can only be read
//! with G::IOChannel::get_flags(), but not changed with G::IOChannel::set_flags().

enum IOFlags
{
	IO_FLAG_APPEND = G_IO_FLAG_APPEND,
	//!< Turns on append mode, corresponds to O_APPEND (see the documentation of the UNIX open() syscall).

	IO_FLAG_NONBLOCK = G_IO_FLAG_NONBLOCK,
	//!< Turns on nonblocking mode, corresponds to O_NONBLOCK/O_NDELAY (see the documentation of the UNIX open() syscall).

	IO_FLAG_IS_READABLE = G_IO_FLAG_IS_READABLE,	/* Read only flag */
	//!< Read only flag; Indicates that the io channel is readable.

	IO_FLAG_IS_WRITEABLE = G_IO_FLAG_IS_WRITEABLE,	/* Read only flag */
	//!< Read only flag; Indicates that the io channel is writable..

	IO_FLAG_IS_SEEKABLE = G_IO_FLAG_IS_SEEKABLE,	/* Read only flag */
	//!< Read only flag; Indicates that the io channel is seekable, i.e. that G::IOChannel::seek_position() can be used on it.

	IO_FLAG_MASK = G_IO_FLAG_MASK,
	//!< Mask flag (I dont know what this means).

	IO_FLAG_GET_MASK = G_IO_FLAG_GET_MASK,
	//!< Same as mask flag.

	IO_FLAG_SET_MASK = G_IO_FLAG_SET_MASK
	//!< Turns on append mode and nonblocking mode.
};

//! IOFlags holds one or more flags form the IOFlags enumeration OR'd together.

typedef unsigned int IOFlagsField;

//! @class IOChannel iochannel.h inti/glib/iochannel.h
//! @brief A GIOChannel C++ wrapper class.
//!
//! The IOChannel object provides a portable method for using file descriptors, pipes,
//! and sockets, and integrating them into the main event loop. Currently full support
//! is available on UNIX platforms, support for Windows is only partially complete.
//!
//! To create a new IOChannel on UNIX systems use the create() 'file descriptor' method.
//! This works for plain file descriptors, pipes and sockets. Alternatively, a channel
//! can be opened for a file in a system independent manner using open_file(). Once an
//! IOChannel has been created, it can be used in a generic manner with the methods
//! read(), write(), seek(), and close().
//!
//! To add an IOChannel to the main event loop use add_watch(). Here you specify which
//! events you are interested in watching on the IOChannel, and provide a slot to be called
//! whenever these events occur.
//!
//! IOChannel instances are created with an initial reference count of 1. ref() and
//! unref() can be used to increment or decrement the reference count respectively.
//! When the reference count falls to 0, the IOChannel is freed. (Though it isn't
//! closed automatically, unless it was opened using open_file().) Using add_watch()
//! increments a channel's reference count.

class IOChannel : public ReferencedObject
{
	IOChannel(const IOChannel&);
	IOChannel& operator=(const IOChannel&);

	GIOChannel *channel_;

public:
	typedef Slot2<bool, IOChannel&, IOConditionField> IOSlot;
	//!< Signature of the callback slot to be called when the requested condition
	//!< on a IOChannel is satisfied.
	//!< <B>Example:</B> Method signature for IOSlot.
	//!< @code
	//!< bool method(IOChannel& source, IOConditionField condition);
	//!<
	//!< // source: The IOChannel event source.
	//!< // condition: The condition which has been satisfied.
	//!< // return: The slot should return false if the event source should be removed.
	//!< @endcode

//! @name Constructors
//! @{

	explicit IOChannel(GIOChannel *channel, bool reference = true);
	//!< Construct a IO channel from an existing GIOChannel.
	//!< @param channel A pointer to a GIOChannel.
	//!< @param reference Set <EM>true</EM> if the initial reference count is owned by this object.
	//!<
	//!< <BR>The IOChannel object takes over the ownership of the GIOChannel and
	//!< unreferences it when the destructor is called.

	virtual ~IOChannel();
	//!< Destructor.

//! @}

	static Pointer<IOChannel> create(const String& filename, const char *mode, G::Error *error);
	//!< Create a new channel for the file <EM>filename</EM> using the mode <EM>mode</EM>.
	//!< @param filename A string containing the name of a file.
	//!< @param mode One of "r", "w", "a", "r+", "w+", "a+". These have the same meaning as in fopen().
	//!< @param error A location to return an error of type G_FILE_ERROR.
	//!< @return A smart pointer to a new IOChannel on success, or null on failure.
	//!<
	//!< <BR>This channel will be closed automatically when the last reference to it is dropped.

	static Pointer<IOChannel> create(int fd);
	//!< Create a new channel for the file descriptor <EM>fd</EM>.
	//!< @param fd A file descriptor.
	//!< @return A smart pointer to a new IOChannel.
	//!<
	//!< <BR>On UNIX systems this works for plain files, pipes, and sockets. The default
	//!< encoding for an IOChannel is UTF-8. If your application is reading output from a
	//!< command using via pipe, you may need to set the encoding to the encoding of the
	//!< current locale (see g_get_charset()) with the IOChannel::set_encoding() method.

	static IOChannelError error_from_errno(int errno_number);
	//!< Converts an errno error number to a IOChannelError.
	//!< @param errno_number An errno error number, e.g. EINVAL.
	//! @return An IOChannelError error number, e.g. IO_CHANNEL_ERROR_INVAL.

//! @name Accessors
//! @{

	GIOChannel* g_io_channel() const { return channel_; }
	//!< Get a pointer to the GIOChannel structure.

	size_t get_buffer_size() const;
	//!< Gets the buffer size.
	//!< @return The size of the buffer.

	IOConditionField get_buffer_condition() const;
	//!< Gets an IOCondition depending on whether there is data to be read/space to write data
	//!< in the internal buffers in the IOChannel.
	//!< @return An IOCondition.
	//!<
	//!< <BR>Note only the flags IO_IN and IO_OUT may be set.

	IOFlagsField get_flags() const;
	//!< Gets the current flags for the channel, including read-only flags such as IO_FLAG_IS_READABLE.
	//!< @return The flags which are set on the channel.
	//!<
	//!< <BR>The values of the flags IO_FLAG_IS_READABLE and IO_FLAG_IS_WRITEABLE are cached
	//!< for internal use by the channel when it is created. If they should change at some
	//!< later point (e.g. partial shutdown of a socket with the UNIX shutdown() function),
	//!< the user should immediately call get_flags() to update the internal values of these
	//!< flags.

	String get_line_term() const;
	//!< Gets the string that the channel uses to determine where in the file a line break
	//!< occurs.
	//!< @return The line termination string (a null String indicates auto detection).

	bool get_buffered() const;
	//!< Returns whether the channel is buffered.
	//!< @return <EM>true</EM> if the channel is buffered.

	String get_encoding() const;
	//!< Gets the encoding for the input/output of the channel.
	//!< @return A string containing the encoding.
	//!<
	//!< <BR>The internal encoding is always UTF-8. The encoding <EM>null</EM> makes the channel safe
	//!< for binary data.

	bool get_close_on_unref() const;
	//!< Returns whether the file/socket/whatever associated with the channel will be closed
	//!< when the channel receives its final unref and is destroyed.
	//!< @return Whether the channel will be closed on the final unref of the IOChannel data structure.
	//!<
	//!< <BR>The return value is <EM>true</EM> for channels created from a filename, and <EM>false</EM>
	//!< for all other channels.

	int get_fd() const;
	//!< Returns the file descriptor of the UNIX channel created by the create() file descriptor method.
	//!< @return The file descriptor of the channel.

//! @}
//! @name Methods
//! @{

	virtual void ref();
	//!< Increase an IO channel's reference count by one.

	virtual void unref();
	//!< Decrease an IO channel's reference count by one.
	//!< When the reference count becomes zero delete is called. Remember, as with all Inti Objects
	//!< you must call unref() on a dynamically allocated IOChannel, not delete. If you use a smart
	//!< you don't need to call unref(), the smart pointer will do that for you. You must also call
	//!< unref() on an IOChannel allocated on the stack because IOChannels don't get passed on to
	//!< owner objects that manage their reference count (like widgets in a container).

	IOStatus close(bool flush, G::Error *error);
	//!< Close an IO channel.
	//!< @param flush Set <EM>true</EM> to flush pending data.
	//!< @param error The location to store an IOChannelError
	//!< @return The status of the operation.
	//!<
	//!< <BR>Any pending data to be written will be flushed if <EM>flush</EM> is true.
	//!< The channel will not be freed until the last reference is dropped using unref().
	//!< Note that you don't need top call unref() with smart pointers, the smart pointer
	//!< does that for you.

	unsigned int add_watch(IOConditionField condition, const IOSlot *slot, int priority = G_PRIORITY_DEFAULT, GDestroyNotify notify = 0);
	//!< Adds the channel into the main event loop with the given priority.
	//!< @param condition The condition to watch for.
	//!< @param slot The IOSlot to call when the condition is satisfied.
	//!< @param priority The priority of the IOChannel source.
	//!< @param notify The function to call when the source is removed, or null.
	//!< @return The event source id.

	void set_buffer_size(size_t size);
	//!< Sets the buffer size.
	//!< @param size The size of the buffer (0 == pick a good size).

	IOStatus set_flags(IOFlagsField flags, G::Error *error);
	//!< Sets the (writeable) flags in the channel to (flags & IO_CHANNEL_SET_MASK).
	//!< @param flags The flags to set on the IO channel.
	//!< @param error The location to store an error of type IOChannelErro.
	//!< @return The status of the operation.

	void set_line_term(const String& line_term);
	//!< This sets the string that the channel uses to determine where in the file a line break occurs.
	//!< @param line_term The line termination string (use null for auto detect).
	//!<
	//!< <BR>Auto detection breaks on "\n", "\r\n", "\r", "\0", and the Unicode paragraph separator.
	//!< Auto detection should not be used for anything other than file-based channels.

	void set_buffered(bool buffered);
	//!< Set the buffering state of the channel (the default state is buffered).
	//!< @param buffered Whether to set the channel buffered or unbuffered.
	//!<
	//!< <BR>The buffering state can only be set if the channel's encoding is null. For any other
	//!< encoding, the channel must be buffered. A buffered channel can only be set unbuffered if
	//!< the channel's internal buffers have been flushed. Newly created channels or channels which
	//!< have returned IO_STATUS_EOF do not require such a flush. For write-only channels, a call
	//!< to flush() is sufficient. For all other channels, the buffers may be flushed by a call to
	//!< seek(). This includes the possibility of seeking with seek type IO_SEEK_CUR and an offset of
	//!< zero. Note that this means that socket-based channels cannot be set unbuffered once they
	//!< have had data read from them.

	IOStatus set_encoding(const char *encoding, G::Error *error);
	//!< Sets the encoding for the input/output of the channel.
	//!< @param encoding The encoding type.
	//!< @param error The location to store an error of type GConvertError.
	//!< @return IO_STATUS_NORMAL if the encoding was successfully set.
	//!<
	//!< <BR>The internal encoding is always UTF-8. The default encoding for the external file is UTF-8.
	//!< The encoding 'null' is safe to use with binary data. The encoding can only be set if one of the
	//!< following conditions is true:
	//!< -# The channel was just created, and has not been written to or read from yet.
	//!< -# The channel is write-only.
	//!< -# The channel is a file, and the file pointer was just repositioned by a call
	//!<    to seek() (this flushes all the internal buffers).
	//!< -# The current encoding is NULL or UTF-8.
	//!< -# One of the read functions has just returned IO_STATUS_EOF (or, in the case of read_to_end(),
	//!<    IO_STATUS_NORMAL).
	//!< -# One of the read() methods has returned IO_STATUS_AGAIN or IO_STATUS_ERROR. This may be useful
	//!<    in the case of G_CONVERT_ERROR_ILLEGAL_SEQUENCE. Returning one of these statuses from
	//!<    read_line() or read_to_end() does not guarantee that the encoding can be changed.
	//!< Channels which do not meet one of the above conditions cannot call seek with an offset of IO_SEEK_CUR,
	//!< and, if they are "seekable", cannot call write after calling one of the read() methods.

	void set_close_on_unref(bool do_close);
	//!< Setting this flag to true for a channel you have already closed can cause problems.
	//!< @param do_close Whether to close the channel on the final unref of the IOChannel data structure.
	//!<
	//!< <BR>The default value is <EM>true</EM> for channels created by the create() filename method,
	//!< and <EM>false</EM> for all other channels.

	IOStatus flush(G::Error *error);
	//!< Flushes the write buffer for the channel.
	//!< @param error The location to store an error of type G::IOChannelError.
	//!< @return The status of the operation: One of IO_CHANNEL_NORMAL, IO_CHANNEL_AGAIN, or IO_CHANNEL_ERROR.
	
	IOStatus read_line(String& str, G::Error *error);
	//!< Reads a line, including the terminating character(s), from a channel into String.
	//!< @param str The line read from the IOChannel, including the line terminator.
	//!< @param error A location to return an error of type GConvertError or IOChannelError.
	//!< @return The status of the operation.

	IOStatus read_to_end(String& str, G::Error *error);
	//!< Reads data from a channel.
	//!< @param str A String to read the data into.
	//!< @param error A location to return an error of type GConvertError or G::IOChannelError.
	//!< @return The status of the operation.

	IOStatus read(char *buffer, size_t count, size_t *bytes_read, G::Error *error);
	//!< Reads data from a channel.
	//!< @param buffer A buffer to read data into.
	//!< @param count The size of the buffer.
	//!< @param bytes_read The number of bytes read.
	//!< @param error A location to return an error of type GConvertError or G::IOChannelError.
	//!< @return The status of the operation.
	//!<
	//!< <BR>Note that the buffer may not be complelely filled even if there is data in the
	//!< buffer if the remaining data is not a complete character. The <EM>bytes_read</EM>
	//!< may be zero even on success if <EM>count</EM> < 6 and the channel's encoding is
	//!< non-null. This indicates that the next UTF-8 character is too wide for the buffer.

	IOStatus read(String& str, size_t count, G::Error *error);
	//!< Reads at most <EM>count</EM> data from a channel into a String.
	//!< @param str A String to read the data into.
	//!< @param count The maximum size of the data to read.
	//!< @param error A location to return an error of type GConvertError or G::IOChannelError.
	//!< @return The status of the operation.
	//!<
	//!< <BR>Note that the buffer may not be complelely filled even if there is data in the
	//!< buffer if the remaining data is not a complete character.

	IOStatus read(G::Unichar& unichar, G::Error *error);
	//!< This method cannot be called on a channel with a null encoding.
	//!< @param unichar A location to return a unicode character.
	//!< @param error A location to return an error of type GConvertError or G::IOChannelError.
	//!< @return The status of the operation.

	IOStatus write(const char *buffer, size_t count, size_t *bytes_written, G::Error *error);
	//!< Writes data to a channel.
	//!< @param buffer A buffer to write data from.
	//!< @param count The size of the buffer. If -1, the buffer is taken to be a null-terminated string.
	//!< @param bytes_written The location to store the number of bytes written.
	//!< @param error A location to return an error of type GConvertError or G::IOChannelError
	//!< @return The status of the operation.
	//!<
	//!< <BR>On seekable channels with encodings other than null or UTF-8, generic mixing
	//!< of reading and writing is not allowed. A call to write() may only be made on a
	//!< channel from which data has been read in the cases described in the documentation
	//!< for set_encoding().
	//!<
	//!< The <EM>bytes_read</EM> can be nonzero even if the return value is not G_IO_STATUS_NORMAL.
	//!< If the return value is G_IO_STATUS_NORMAL and the channel is blocking, <EM>bytes_read</EM>
	//!< will always be equal to count if count >= 0.

	IOStatus write(const String& str, G::Error *error);
	//!< Writes a String to a channel.
	//!< @param str A String to write from.
	//!< @param error A location to return an error of type GConvertError or G::IOChannelError
	//!< @return The status of the operation.
	//!<
	//!< <BR>On seekable channels with encodings other than null or UTF-8, generic mixing
	//!< of reading and writing is not allowed. A call to write() may only be made on a
	//!< channel from which the String has been read in the cases described in the documentation
	//!< for set_encoding().

	IOStatus write(G::Unichar unichar, G::Error *error);
	//!< This method cannot be called on a channel with a null encoding.
	//!< @param unichar A unicode character.
	//!< @param error A location to return an error of type GConvertError or G::IOChannelError.
	//!< @return The status of the operation.

	IOStatus seek(gint64 offset, SeekType type, G::Error *error);
	//!< Sets the current position in the channel, similar to the standard library function fseek().
	//!< @param offset The offset in bytes from the position specified by type.
	//!< @param type A SeekType.
	//!< @param error A location to return an error of type G::IOChannelError.
	//!< @return The status of the operation.
	//!<
	//!< <BR>The G::SeekType IO_SEEK_CUR is only allowed in those cases where a call to set_encoding()
	//!< is allowed. See the documentation for set_encoding() for details.

//! @}

};

} // namespace G

} // namespace Inti

#endif // INTI_G_IOCHANNEL_H
