/*
**
**  File:   Log.h
**  Date:   November 24, 2004
**  By:     Bryant Collard
**  Desc:   Utility to support data logging.
**
**  Copyright (c) 2004, Avalanche Software Inc.
**  All Rights Reserved.
**
**      $File: //depot/Avalanche/GameLib/GameHelper/LogUtil.h $
**  $Revision: #11 $
**  $DateTime: 2005/08/24 11:00:40 $
**    $Author: Bryant_Collard $
**
*/

#pragma once

#ifndef LOGUTIL_H
#define LOGUTIL_H

// GameLib includes
#include "EngineHelper/Singleton.h"

// File Support
#define USE_NONE        0
#define USE_STDIO       1
#define USE_FILESTREAM  2
#define USE_EMAIL       3

#ifdef CONSUMER_BUILD
#define LOG_TYPE        USE_NONE
#elif defined(CDROM)
#define LOG_TYPE        USE_NONE // USE_EMAIL
#elif defined(WIN32) && !defined(_XBOX)
#define LOG_TYPE        USE_STDIO
#else
#define LOG_TYPE        USE_FILESTREAM
#endif

#if LOG_TYPE == USE_STDIO
#include <stdio.h>
#elif LOG_TYPE == USE_FILESTREAM
#include "EngineHelper/filestream.h"
#elif LOG_TYPE == USE_EMAIL
#include "Layers/EMail/Email.h"
#define MAX_RECORD_SIZE  512
#endif

#define MAX_LOG_NAME_LEN 261

/////////////////////////////////// CLogUtil ///////////////////////////////////

// This class builds full file names. Three file utility sets are currently
// supported: the standard utilities provided in stdio.h; the Avalanche
// filestream cross-platform utilities; and utilities that send email instead of
// actually writing outfiles.
//
// This class stores file name components and uses them to build full file path
// names. Three components are stored; a path, a directory, and a prefix. The
// path is only used in stdio files. Its intended use is to specify a UNC path
// so log files generated by different computers can be stored in a common
// shared directory. The directory is the name of a directory, relative to the
// specified path, if any, where log files are to be written. The directory
// (along with the path) is not used in email. If a prefix is specified, the
// file name will start with the prefix and an underscore. So, if all three
// components are specified and MakeFullName is called with some file name, the
// resulting full path name will be:
//
//     <PATH>/<DIRECTORY>/<PREFIX>_<FILENAME>

class CLogUtil
{
  public:
	CLogUtil();
	void Enable(bool i_enabled) {d_enabled = i_enabled;}
	bool Enabled(void) {return(d_enabled);}
	bool SetPath(const char* i_path);
	bool SetDirectory(const char* i_dir);
	bool SetPrefix(const char* i_prefix);
	const char* MakeFullName(const char* i_name);

  private:
	enum EAppendResult
	{
		APPEND_SUCCESS,
		APPEND_INVALID,
		APPEND_TOO_LONG
	};
	EAppendResult Append(char* io_dest, const char* i_src,
			const char i_terminate);

	bool d_enabled;
	char d_path[MAX_LOG_NAME_LEN];
	char d_dir[MAX_LOG_NAME_LEN];
	char d_prefix[MAX_LOG_NAME_LEN];
};

/////////////////////////////////// CLogFile ///////////////////////////////////

#if LOG_TYPE != USE_NONE

class CLogFile
{
  public:
	// Construct/Destruct
	CLogFile();
	~CLogFile();

	// Open a file and prep for logging.
	bool Open(const char* i_name, uint i_numFields, bool i_incRecNum = false);

	// Cleanup
	void Close(void);

	// If not open, open a file and prep for logging.
	bool Reopen(const char* i_name, uint i_numFields, bool i_incRecNum = false);

	// Cleanup if not closed.
	void Reclose(void);

	// Write a label in the record number field (if there is one), instead of
	// the record number.
	void LabelRecordNumberField(const char* i_label);

	// Set a field to an integer value.
	void SetField(uint i_index, int i_value);

	// Set a field to an unsigned integer value. Output will be in hex.
	void SetField(uint i_index, uint i_value);

	// Set a field to a floating point value.
	void SetField(uint i_index, float i_value);

	// Set a field to character string.
	void SetField(uint i_index, const char* i_value, int i_indentSize = 0,
			const char i_indentChar = ' ');

	// Output the current field values.
	void Print(void);

	// Query state.
	bool IsInvalid(void) {return(d_state == LOG_INVALID);}
	bool IsOpen(void) {return(d_state == LOG_OPEN);}
	bool IsClosed(void) {return(d_state == LOG_CLOSED);}

  private:
	enum
	{
		LOG_CLOSED,
		LOG_OPEN,
		LOG_INVALID
	} d_state;
	uint d_numFields;
	enum EFieldType
	{
		FIELD_NONE,
		FIELD_INT,
		FIELD_HEX_UINT,
		FIELD_FLOAT,
		FIELD_STRING
	};
	struct SField
	{
		char* d_header;
		EFieldType d_type;
		union
		{
			int d_intValue;
			uint d_uintValue;
			float d_floatValue;
		};
		uint d_stringSize;
		char* d_stringValue;
	} *d_field;
	int d_recordNumber;
	char* d_recordNumberLabel;
#if LOG_TYPE == USE_STDIO
	FILE* d_file;
#elif LOG_TYPE == USE_FILESTREAM
	FileStream* d_file;
#elif LOG_TYPE == USE_EMAIL
	char* EmailOpen(const char* i_name);
	void EmailClose(void);
	void EmailField(void);
	void EmailRecord(void);
	char* d_file;
	EMail d_email;
	char d_record[MAX_RECORD_SIZE];
#else
	char* d_file;
#endif
};

#else

class CLogFile
{
  public:
	CLogFile() {}
	~CLogFile() {}
	bool Open(const char* i_name, uint i_numFields, bool i_incRecNum = false) {return false;}
	void Close(void) {}
	bool Reopen(const char* i_name, uint i_numFields, bool i_incRecNum = false) {return false;}
	void Reclose(void) {}
	void LabelRecordNumberField(const char* i_label) {}
	void SetField(uint i_index, int i_value) {}
	void SetField(uint i_index, uint i_value) {}
	void SetField(uint i_index, float i_value) {}
	void SetField(uint i_index, const char* i_value, int i_indentSize = 0,
		const char i_indentChar = ' ') {}
	void Print(void) {}
	bool IsInvalid(void) {return true;}
	bool IsOpen(void) {return false;}
	bool IsClosed(void) {return true;}
};

#endif

////////////////////////////////// SLogMessage /////////////////////////////////

struct SLogMessage
{
	enum
	{
		LOG_PLAIN,
		LOG_SUBJECT,
		LOG_ACTION
	} d_type;

	char* d_label;
	union
	{
		uint32 d_subject;
		char* d_action;
	};
};

extern CLogUtil g_logUtil;

#endif // LOGUTIL_H
