/*
 * Copyright 2012, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Copyright 2012-2016, Rene Gollent, rene@gollent.com.
 * Distributed under the terms of the MIT License.
 */
 
 
#include "UiUtils.h"
 
#include <ctype.h>
#include <stdio.h>
 
#include <DateTime.h>
#include <KernelExport.h>
#include <Path.h>
#include <String.h>
#include <Variant.h>
 
#include <vm_defs.h>
 
#include "FunctionInstance.h"
#include "Image.h"
#include "RangeList.h"
#include "SignalDispositionTypes.h"
#include "StackFrame.h"
#include "Team.h"
#include "TeamMemoryBlock.h"
#include "Thread.h"
#include "Type.h"
#include "Value.h"
#include "ValueNode.h"
 
 
/*static*/ const char*
UiUtils::ThreadStateToString(int state, int stoppedReason)
{
	switch (state) {
		case THREAD_STATE_RUNNING:
			return "Running";
		case THREAD_STATE_STOPPED:
			break;
		case THREAD_STATE_UNKNOWN:
		default:
			return "?";
	}
 
	// thread is stopped -- get the reason
	switch (stoppedReason) {
		case THREAD_STOPPED_DEBUGGER_CALL:
			return "Call";
		case THREAD_STOPPED_EXCEPTION:
			return "Exception";
		case THREAD_STOPPED_BREAKPOINT:
		case THREAD_STOPPED_WATCHPOINT:
		case THREAD_STOPPED_SINGLE_STEP:
		case THREAD_STOPPED_DEBUGGED:
		case THREAD_STOPPED_UNKNOWN:
		default:
			return "Debugged";
	}
}
 
 
/*static*/ const char*
UiUtils::VariantToString(const BVariant& value, char* buffer,
	size_t bufferSize)
{
	if (!value.IsNumber())
		return value.ToString();
 
	switch (value.Type()) {
		case B_FLOAT_TYPE:
		case B_DOUBLE_TYPE:
			snprintf(buffer, bufferSize, "%.3g", value.ToDouble());
			break;
		case B_INT8_TYPE:
		case B_UINT8_TYPE:
			snprintf(buffer, bufferSize, "0x%02x", value.ToUInt8());
			break;
		case B_INT16_TYPE:
		case B_UINT16_TYPE:
			snprintf(buffer, bufferSize, "0x%04x", value.ToUInt16());
			break;
		case B_INT32_TYPE:
		case B_UINT32_TYPE:
			snprintf(buffer, bufferSize, "0x%08" B_PRIx32,
				value.ToUInt32());
			break;
		case B_INT64_TYPE:
		case B_UINT64_TYPE:
		default:
			snprintf(buffer, bufferSize, "0x%016" B_PRIx64,
				value.ToUInt64());
			break;
	}
 
	return buffer;
}
 
 
/*static*/ const char*
UiUtils::FunctionNameForFrame(StackFrame* frame, char* buffer,
	size_t bufferSize)
{
	Image* image = frame->GetImage();
	FunctionInstance* function = frame->Function();
	if (image == NULL && function == NULL) {
		snprintf(buffer, bufferSize, "?");
		return buffer;
	}
 
	BString name;
	target_addr_t baseAddress;
	if (function != NULL) {
		name = function->PrettyName();
		baseAddress = function->Address();
	} else {
		name = image->Name();
		baseAddress = image->Info().TextBase();
	}
 
	snprintf(buffer, bufferSize, "%s + %#" B_PRIx64,
		name.String(), frame->InstructionPointer() - baseAddress);
 
	return buffer;
}
 
 
/*static*/ const char*
UiUtils::ImageTypeToString(image_type type, char* buffer, size_t bufferSize)
{
	switch (type) {
		case B_APP_IMAGE:
			snprintf(buffer, bufferSize, "app");
			break;
		case B_LIBRARY_IMAGE:
			snprintf(buffer, bufferSize, "lib");
			break;
		case B_ADD_ON_IMAGE:
			snprintf(buffer, bufferSize, "add-on");
			break;
		case B_SYSTEM_IMAGE:
			snprintf(buffer, bufferSize, "system");
			break;
		default:
			snprintf(buffer, bufferSize, "unknown");
			break;
	}
 
	return buffer;
}
 
 
/*static*/ const char*
UiUtils::AreaLockingFlagsToString(uint32 flags, char* buffer,
	size_t bufferSize)
{
	switch (flags) {
		case B_NO_LOCK:
			snprintf(buffer, bufferSize, "none");
			break;
		case B_LAZY_LOCK:
			snprintf(buffer, bufferSize, "lazy");
			break;
		case B_FULL_LOCK:
			snprintf(buffer, bufferSize, "full");
			break;
		case B_CONTIGUOUS:
			snprintf(buffer, bufferSize, "contiguous");
			break;
		case B_LOMEM:
			snprintf(buffer, bufferSize, "lo-mem");
			break;
		case B_32_BIT_FULL_LOCK:
			snprintf(buffer, bufferSize, "32-bit full");
			break;
		case B_32_BIT_CONTIGUOUS:
			snprintf(buffer, bufferSize, "32-bit contig.");
			break;
		default:
			snprintf(buffer, bufferSize, "unknown");
			break;
	}
 
	return buffer;
}
 
 
/*static*/ const BString&
UiUtils::AreaProtectionFlagsToString(uint32 protection, BString& _output)
{
	#undef ADD_AREA_FLAG_IF_PRESENT
	#define ADD_AREA_FLAG_IF_PRESENT(flag, protection, name, output, missing)\
		if ((protection & flag) != 0) { \
			_output += name; \
			protection &= ~flag; \
		} else \
			_output += missing; \
 
	_output.Truncate(0);
	uint32 userFlags = protection & B_USER_PROTECTION;
	bool userProtectionPresent = userFlags != 0;
	ADD_AREA_FLAG_IF_PRESENT(B_READ_AREA, protection, "r", _output,
		userProtectionPresent ? "-" : " ");
	ADD_AREA_FLAG_IF_PRESENT(B_WRITE_AREA, protection, "w", _output,
		userProtectionPresent ? "-" : " ");
	ADD_AREA_FLAG_IF_PRESENT(B_EXECUTE_AREA, protection, "x", _output,
		userProtectionPresent ? "-" : " ");
 
	// if the user versions of these flags are present,
	// filter out their kernel equivalents since they're implied.
	if ((userFlags & B_READ_AREA) != 0)
		protection &= ~B_KERNEL_READ_AREA;
	if ((userFlags & B_WRITE_AREA) != 0)
		protection &= ~B_KERNEL_WRITE_AREA;
	if ((userFlags & B_EXECUTE_AREA) != 0)
		protection &= ~B_KERNEL_EXECUTE_AREA;
 
	if ((protection & B_KERNEL_PROTECTION) != 0) {
		ADD_AREA_FLAG_IF_PRESENT(B_KERNEL_READ_AREA, protection, "r",
			_output, "-");
		ADD_AREA_FLAG_IF_PRESENT(B_KERNEL_WRITE_AREA, protection, "w",
			_output, "-");
		ADD_AREA_FLAG_IF_PRESENT(B_KERNEL_EXECUTE_AREA, protection, "x",
			_output, "-");
	}
 
	ADD_AREA_FLAG_IF_PRESENT(B_STACK_AREA, protection, "s", _output, "");
	ADD_AREA_FLAG_IF_PRESENT(B_KERNEL_STACK_AREA, protection, "s", _output, "");
	ADD_AREA_FLAG_IF_PRESENT(B_OVERCOMMITTING_AREA, protection, _output, "o",
		"");
	ADD_AREA_FLAG_IF_PRESENT(B_SHARED_AREA, protection, "S", _output, "");
 
	if (protection != 0) {
		char buffer[32];
		snprintf(buffer, sizeof(buffer), ", u:(%#04" B_PRIx32 ")",
			protection);
		_output += buffer;
	}
 
	return _output;
}
 
 
/*static*/ const char*
UiUtils::ReportNameForTeam(::Team* team, char* buffer, size_t bufferSize)
{
	BPath teamPath(team->Name());
	BDateTime currentTime;
	currentTime.SetTime_t(time(NULL));
	snprintf(buffer, bufferSize, "%s-%" B_PRId32 "-debug-%02" B_PRId32 "-%02"
		B_PRId32 "-%02" B_PRId32 "-%02" B_PRId32 "-%02" B_PRId32 "-%02"
		B_PRId32 ".report", teamPath.Leaf(), team->ID(),
		currentTime.Date().Day(), currentTime.Date().Month(),
		currentTime.Date().Year(), currentTime.Time().Hour(),
		currentTime.Time().Minute(), currentTime.Time().Second());
 
	return buffer;
}
 
 
/*static*/ const char*
UiUtils::CoreFileNameForTeam(::Team* team, char* buffer, size_t bufferSize)
{
	BPath teamPath(team->Name());
	BDateTime currentTime;
	currentTime.SetTime_t(time(NULL));
	snprintf(buffer, bufferSize, "%s-%" B_PRId32 "-debug-%02" B_PRId32 "-%02"
		B_PRId32 "-%02" B_PRId32 "-%02" B_PRId32 "-%02" B_PRId32 "-%02"
		B_PRId32 ".core", teamPath.Leaf(), team->ID(),
		currentTime.Date().Day(), currentTime.Date().Month(),
		currentTime.Date().Year(), currentTime.Time().Hour(),
		currentTime.Time().Minute(), currentTime.Time().Second());
 
	return buffer;
 
}
 
 
/*static*/ void
UiUtils::PrintValueNodeGraph(BString& _output, ValueNodeChild* child,
	int32 indentLevel, int32 maxDepth)
{
	_output.Append('\t', indentLevel);
	_output << child->Name();
 
	ValueNode* node = child->Node();
	if (node == NULL) {
		_output << ": Unavailable\n";
		return;
	}
 
	if (node->GetType()->Kind() != TYPE_COMPOUND) {
		_output << ": ";
		status_t resolutionState = node->LocationAndValueResolutionState();
		if (resolutionState == VALUE_NODE_UNRESOLVED)
			_output << "Unresolved";
		else if (resolutionState == B_OK) {
			Value* value = node->GetValue();
			if (value != NULL) {
				BString valueData;
				value->ToString(valueData);
				_output << valueData;
			} else
				_output << "Unavailable";
		} else
			_output << strerror(resolutionState);
	}
 
	if (maxDepth == 0 || node->CountChildren() == 0) {
		_output << "\n";
		return;
	}
 
	if (node->CountChildren() == 1
		&& node->GetType()->ResolveRawType(false)->Kind() == TYPE_ADDRESS
		&& node->ChildAt(0)->GetType()->ResolveRawType(false)->Kind()
			== TYPE_COMPOUND) {
		// for the case of a pointer to a compound type,
		// we want to hide the intervening compound node and print
		// the children directly.
		node = node->ChildAt(0)->Node();
	}
 
	if (node != NULL) {
		_output << " {\n";
 
		for (int32 i = 0; i < node->CountChildren(); i++) {
			// don't dump compound nodes if our depth limit won't allow
			// us to traverse into their children anyways, and the top
			// level node contains no data of intereest.
			if (node->ChildAt(i)->GetType()->Kind() != TYPE_COMPOUND
				|| maxDepth > 1) {
				PrintValueNodeGraph(_output, node->ChildAt(i),
					indentLevel + 1, maxDepth - 1);
			}
		}
		_output.Append('\t', indentLevel);
		_output << "}\n";
	} else
		_output << "\n";
 
	return;
}
 
 
/*static*/ void
UiUtils::DumpMemory(BString& _output, int32 indentLevel,
	TeamMemoryBlock* block, target_addr_t address, int32 itemSize,
	int32 displayWidth, int32 count)
{
	BString data;
 
	int32 j;
	_output.Append('\t', indentLevel);
	for (int32 i = 0; i < count; i++) {
		if (!block->Contains(address + i * itemSize))
			break;
 
		uint8* value;
 
		if ((i % displayWidth) == 0) {
			int32 displayed = min_c(displayWidth, (count-i)) * itemSize;
			if (i != 0) {
				_output.Append("\n");
				_output.Append('\t', indentLevel);
			}
 
			data.SetToFormat("[%#" B_PRIx64 "]  ", address + i * itemSize);
			_output += data;
			char c;
			for (j = 0; j < displayed; j++) {
				c = *(block->Data() + address - block->BaseAddress()
					+ (i * itemSize) + j);
				if (!isprint(c))
					c = '.';
 
				_output += c;
			}
			if (count > displayWidth) {
				// make sure the spacing in the last line is correct
				for (j = displayed; j < displayWidth * itemSize; j++)
					_output += ' ';
			}
			_output.Append("  ");
		}
 
		value = block->Data() + address - block->BaseAddress()
			+ i * itemSize;
 
		switch (itemSize) {
			case 1:
				data.SetToFormat(" %02" B_PRIx8, *(uint8*)value);
				break;
			case 2:
				data.SetToFormat(" %04" B_PRIx16, *(uint16*)value);
				break;
			case 4:
				data.SetToFormat(" %08" B_PRIx32, *(uint32*)value);
				break;
			case 8:
				data.SetToFormat(" %016" B_PRIx64, *(uint64*)value);
				break;
		}
 
		_output += data;
	}
 
	_output.Append("\n");
}
 
 
static status_t ParseRangeString(BString& rangeString, int32& lowerBound,
	int32& upperBound)
{
	lowerBound = atoi(rangeString.String());
	int32 index = rangeString.FindFirst('-');
	if (index >= 0) {
		rangeString.Remove(0, index + 1);
		upperBound = atoi(rangeString.String());
	} else
		upperBound = lowerBound;
 
	if (lowerBound > upperBound)
		return B_BAD_VALUE;
 
	return B_OK;
}
 
 
/*static*/ status_t
UiUtils::ParseRangeExpression(const BString& rangeExpression, int32 lowerBound,
	int32 upperBound, bool fixedRange, RangeList& _output)
{
	if (rangeExpression.IsEmpty())
		return B_BAD_DATA;
 
	BString dataString = rangeExpression;
	dataString.RemoveAll(" ");
 
	// first, tokenize the range list to its constituent child ranges.
	int32 index;
	int32 lowValue;
	int32 highValue;
	BString tempRange;
	while (!dataString.IsEmpty()) {
		index = dataString.FindFirst(',');
		if (index == 0)
			return B_BAD_VALUE;
		else if (index > 0) {
			dataString.MoveInto(tempRange, 0, index);
			dataString.Remove(0, 1);
		} else {
			tempRange = dataString;
			dataString.Truncate(0);
		}
 
		status_t result = ParseRangeString(tempRange, lowValue, highValue);
		if (result != B_OK)
			return result;
 
 
		if (fixedRange && (lowValue < lowerBound || highValue > upperBound))
			return B_BAD_VALUE;
 
		result = _output.AddRange(lowValue, highValue);
		if (result != B_OK)
			return result;
 
		tempRange.Truncate(0);
	}
 
	return B_OK;
}
 
 
/*static*/ const char*
UiUtils::TypeCodeToString(type_code type)
{
	switch (type) {
		case B_INT8_TYPE:
			return "int8";
		case B_UINT8_TYPE:
			return "uint8";
		case B_INT16_TYPE:
			return "int16";
		case B_UINT16_TYPE:
			return "uint16";
		case B_INT32_TYPE:
			return "int32";
		case B_UINT32_TYPE:
			return "uint32";
		case B_INT64_TYPE:
			return "int64";
		case B_UINT64_TYPE:
			return "uint64";
		case B_FLOAT_TYPE:
			return "float";
		case B_DOUBLE_TYPE:
			return "double";
		case B_STRING_TYPE:
			return "string";
		default:
			return "unknown";
	}
}
 
 
template<typename T>
T GetSIMDValueAtOffset(char* data, int32 index)
{
	return ((T*)data)[index];
}
 
 
static int32 GetSIMDFormatByteSize(uint32 format)
{
	switch (format) {
		case SIMD_RENDER_FORMAT_INT8:
			return sizeof(char);
		case SIMD_RENDER_FORMAT_INT16:
			return sizeof(int16);
		case SIMD_RENDER_FORMAT_INT32:
			return sizeof(int32);
		case SIMD_RENDER_FORMAT_INT64:
			return sizeof(int64);
		case SIMD_RENDER_FORMAT_FLOAT:
			return sizeof(float);
		case SIMD_RENDER_FORMAT_DOUBLE:
			return sizeof(double);
	}
 
	return 0;
}
 
 
/*static*/
const BString&
UiUtils::FormatSIMDValue(const BVariant& value, uint32 bitSize,
	uint32 format, BString& _output)
{
	_output.SetTo("{");
	char* data = (char*)value.ToPointer();
	uint32 count = bitSize / (GetSIMDFormatByteSize(format) * 8);
	for (uint32 i = 0; i < count; i ++) {
		BString temp;
		switch (format) {
			case SIMD_RENDER_FORMAT_INT8:
				temp.SetToFormat("%#" B_PRIx8,
					GetSIMDValueAtOffset<uint8>(data, i));
				break;
			case SIMD_RENDER_FORMAT_INT16:
				temp.SetToFormat("%#" B_PRIx16,
					GetSIMDValueAtOffset<uint16>(data, i));
				break;
			case SIMD_RENDER_FORMAT_INT32:
				temp.SetToFormat("%#" B_PRIx32,
					GetSIMDValueAtOffset<uint32>(data, i));
				break;
			case SIMD_RENDER_FORMAT_INT64:
				temp.SetToFormat("%#" B_PRIx64,
					GetSIMDValueAtOffset<uint64>(data, i));
				break;
			case SIMD_RENDER_FORMAT_FLOAT:
				temp.SetToFormat("%.3g",
					(double)GetSIMDValueAtOffset<float>(data, i));
				break;
			case SIMD_RENDER_FORMAT_DOUBLE:
				temp.SetToFormat("%.3g",
					GetSIMDValueAtOffset<double>(data, i));
				break;
		}
		_output += temp;
		if (i < count - 1)
			_output += ", ";
	}
	_output += "}";
 
	return _output;
}
 
 
const char*
UiUtils::SignalNameToString(int32 signal, BString& _output)
{
	#undef DEFINE_SIGNAL_STRING
	#define DEFINE_SIGNAL_STRING(x)										\
		case x:															\
			_output = #x;												\
			return _output.String();
 
	switch (signal) {
		DEFINE_SIGNAL_STRING(SIGHUP)
		DEFINE_SIGNAL_STRING(SIGINT)
		DEFINE_SIGNAL_STRING(SIGQUIT)
		DEFINE_SIGNAL_STRING(SIGILL)
		DEFINE_SIGNAL_STRING(SIGCHLD)
		DEFINE_SIGNAL_STRING(SIGABRT)
		DEFINE_SIGNAL_STRING(SIGPIPE)
		DEFINE_SIGNAL_STRING(SIGFPE)
		DEFINE_SIGNAL_STRING(SIGKILL)
		DEFINE_SIGNAL_STRING(SIGSTOP)
		DEFINE_SIGNAL_STRING(SIGSEGV)
		DEFINE_SIGNAL_STRING(SIGCONT)
		DEFINE_SIGNAL_STRING(SIGTSTP)
		DEFINE_SIGNAL_STRING(SIGALRM)
		DEFINE_SIGNAL_STRING(SIGTERM)
		DEFINE_SIGNAL_STRING(SIGTTIN)
		DEFINE_SIGNAL_STRING(SIGTTOU)
		DEFINE_SIGNAL_STRING(SIGUSR1)
		DEFINE_SIGNAL_STRING(SIGUSR2)
		DEFINE_SIGNAL_STRING(SIGWINCH)
		DEFINE_SIGNAL_STRING(SIGKILLTHR)
		DEFINE_SIGNAL_STRING(SIGTRAP)
		DEFINE_SIGNAL_STRING(SIGPOLL)
		DEFINE_SIGNAL_STRING(SIGPROF)
		DEFINE_SIGNAL_STRING(SIGSYS)
		DEFINE_SIGNAL_STRING(SIGURG)
		DEFINE_SIGNAL_STRING(SIGVTALRM)
		DEFINE_SIGNAL_STRING(SIGXCPU)
		DEFINE_SIGNAL_STRING(SIGXFSZ)
		DEFINE_SIGNAL_STRING(SIGBUS)
		default:
			break;
	}
 
	if (signal == SIGRTMIN)
		_output = "SIGRTMIN";
	else if (signal == SIGRTMAX)
		_output = "SIGRTMAX";
	else
		_output.SetToFormat("SIGRTMIN+%" B_PRId32, signal - SIGRTMIN);
 
	return _output.String();
}
 
 
const char*
UiUtils::SignalDispositionToString(int disposition)
{
	switch (disposition) {
		case SIGNAL_DISPOSITION_IGNORE:
			return "Ignore";
		case SIGNAL_DISPOSITION_STOP_AT_RECEIPT:
			return "Stop at receipt";
		case SIGNAL_DISPOSITION_STOP_AT_SIGNAL_HANDLER:
			return "Stop at signal handler";
		default:
			break;
	}
 
	return "Unknown";
}

V609 Divide by zero. Denominator range [0..64].