/*  see copyright notice in squirrel.h */
#ifndef _SQSTD_BLOBIMPL_H_
#define _SQSTD_BLOBIMPL_H_

struct SQBlob : public SQStream
{
	SQBlob(SQInteger size)
	{
		_size = size;
		_allocated = size;
		_buf = (unsigned char *)sq_malloc(size);
		memset(_buf, 0, _size);
		_ptr = 0;
		_owns = true;
	}
	virtual ~SQBlob()
	{
		sq_free(_buf, _allocated);
	}
	SQInteger Write(void *buffer, SQInteger size)
	{
		if(!CanAdvance(size)) {
			GrowBufOf(_ptr + size - _size);
		}
		memcpy(&_buf[_ptr], buffer, size);
		_ptr += size;
		return size;
	}
	SQInteger Read(void *buffer, SQInteger size)
	{
		SQInteger n = size;
		if(!CanAdvance(size)) {
			if((_size - _ptr) > 0)
				n = _size - _ptr;
			else
				return 0;
		}
		memcpy(buffer, &_buf[_ptr], n);
		_ptr += n;
		return n;
	}
	bool Resize(SQInteger n)
	{
		if(!_owns)
			return false;
		if(n != _allocated) {
			unsigned char *newbuf = (unsigned char *)sq_malloc(n);
			memset(newbuf, 0, n);
			if(_size > n)
				memcpy(newbuf, _buf, n);
			else
				memcpy(newbuf, _buf, _size);
			sq_free(_buf, _allocated);
			_buf = newbuf;
			_allocated = n;
			if(_size > _allocated)
				_size = _allocated;
			if(_ptr > _allocated)
				_ptr = _allocated;
		}
		return true;
	}
	bool GrowBufOf(SQInteger n)
	{
		bool ret = true;
		if(_size + n > _allocated) {
			if(_size + n > _size * 2)
				ret = Resize(_size + n);
			else
				ret = Resize(_size * 2);
		}
		_size = _size + n;
		return ret;
	}
	bool CanAdvance(SQInteger n)
	{
		if(_ptr + n > _size)
			return false;
		return true;
	}
	SQInteger Seek(SQInteger offset, SQInteger origin)
	{
		switch(origin) {
			case SQ_SEEK_SET:
				if(offset > _size || offset < 0)
					return -1;
				_ptr = offset;
				break;
			case SQ_SEEK_CUR:
				if(_ptr + offset > _size || _ptr + offset < 0)
					return -1;
				_ptr += offset;
				break;
			case SQ_SEEK_END:
				if(_size + offset > _size || _size + offset < 0)
					return -1;
				_ptr = _size + offset;
				break;
			default:
				return -1;
		}
		return 0;
	}
	bool IsValid()
	{
		return _size == 0 || _buf ? true : false;
	}
	bool EOS()
	{
		return _ptr == _size;
	}
	SQInteger Flush()
	{
		return 0;
	}
	SQInteger Tell()
	{
		return _ptr;
	}
	SQInteger Len()
	{
		return _size;
	}
	SQUserPointer GetBuf()
	{
		return _buf;
	}

private:
	SQInteger _size;
	SQInteger _allocated;
	SQInteger _ptr;
	unsigned char *_buf;
	bool _owns;
};

#endif //_SQSTD_BLOBIMPL_H_
