// vr_record.h
// Read and save records
//
// Copyright (C) 2000 Agenda Computing Inc.
//
// 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.

#ifndef VR_RECORD_CLASS
#define VR_RECORD_CLASS 

#include <string>

struct vr_datum {
private:
	char* _dptr;
	int _size;
public:
	vr_datum(char* d, int s) {
		_dptr = d;
		_size = s;
	}

	const char* dptr() { return _dptr; }
	int size() { return _size; }
};

/**
 * Manages record metadata and user data.
 *
 * For performance reasons, some frightening tricks are played with
 * allocated memory.
 * Be very careful how you use this class, especially when you get datum
 * back in save() and data().
 */
class VR_Record
{
	enum flag_t {
		FLAG_Modified = 1,
		FLAG_Deleted = 2,
		FLAG_Private = 4
	};

	bool _private;
	bool _modified;
	bool _deleted;
	unsigned char _category;

	struct vr_record_t {
		unsigned char flags;
		unsigned char category;
		char user_data[0];
	};

	vr_record_t* _record;
	int _record_size;

public:
	VR_Record() : _private(false), _modified(false), _deleted(false),
		_category(0), _record(0), _record_size(0) {};

	~VR_Record() {
		if(_record)
			free(_record);
	}

	/**
	 * Initialize with datum.
	 * After this is called, the VR_Record object then owns the datum.
	 * Do not modify or free the datum after doing this.
	 */
	void load(vr_datum d) {
		if(((unsigned)(d.size())) < sizeof(vr_record_t)) {
			_record = (vr_record_t*)realloc(_record, sizeof(vr_record_t));
			_private = false;
			_modified = false;
			_deleted = false;
			_record->user_data[0] = 0;
			_record_size = sizeof(vr_record_t);
			return;
		}
		// Free any existing pointer.
		if(_record)
			free(_record);

		// Interpret the datum as a vr_record_t.
		_record = reinterpret_cast<vr_record_t*>((void*)d.dptr());
		_record_size = d.size();

		// Interpret the flag bits and category.
		_private =  _record->flags & FLAG_Private;
		_modified = _record->flags & FLAG_Modified;
		_deleted =  _record->flags & FLAG_Deleted;
		_category = _record->category;
	}

	/**
	 * Convert to vr_datum.
	 * This returns a pointer (dptr) to allocated memory owned by this object.
	 * Do not modify or free the dptr.
	 */
	vr_datum save(bool reset_modified_flag = false) {
		// ACTION: Do we need the reset_modified_flag parameter?
		if(reset_modified_flag)
			_modified = false;

		_record->flags = (_private ? FLAG_Private : 0)
			| (_modified ? FLAG_Modified : 0)
			| (_deleted ? FLAG_Deleted : 0);
		_record->category = _category;

		return vr_datum((char *)_record, _record_size);
	}

	/**
	 * Set user data.
	 * This always copies the data passed to it.
	 */
	void user_data(vr_datum data, bool set_modified = true) {
		// Is this a new record?
		if(!_record) {
			_category = 0;
		}

		// (Re)size the record buffer.
		_record_size = sizeof(vr_record_t) + data.size();
		_record = static_cast<vr_record_t*>(realloc(_record, _record_size));

		// Store the user data in the buffer.
		// Be sure data.pdtr have \0 at the end and data.size count it in.
		// or else next time you fetch this record datum.dptr would be larger.
		memcpy(_record->user_data, data.dptr(), data.size());

		_modified = set_modified;
	}

	/**
	 * Get user data.
	 * This returns a pointer (dptr) to allocated memory owned by this object.
	 * Do not modify or free the dptr.
	 */
	vr_datum user_data() {
		return vr_datum(_record->user_data, _record_size - sizeof(vr_record_t) -1);
	}

	/**
	 * Metadata accessor functions.
	 */
	void category(unsigned char c) { _category = c; }
	unsigned char category() { return _category; }
	void deleted(bool b) { _deleted = b; };
	bool deleted() { return _deleted; };
	void modified(bool b) { _modified = b; }
	bool modified() { return _modified; }
	void privated(bool b) { _private = b; }
	bool privated() { return _private; }
};
#endif
