#pragma once

#include <algorithm>  // std::min

/****************************************************************************/
// SCString

class SCString
{
	friend class c_Graph;

	private: //On 64-bit build size is 24 bytes
		char* m_String;
		int m_OwnString;
		int m_IsModified;
		const char* m_DefaultString;
		#ifndef _WIN64
		char Reserve[60];
		#endif

	public:
		SCString();
		SCString(int DummyValue);  // this is needed by the c_ArrayWrapper class
		SCString(const SCString& Str);//Copy constructor
		SCString(const char* SourceString);
		SCString(const char* SourceString, int Length);
		SCString(const char ch);
		~SCString();

		void Initialize();

		void Clear();

		bool operator == (const SCString& Rhs) const;
		bool operator == (const char* Rhs) const;
		bool operator != (const SCString& Rhs) const;
		bool operator != (const char* Rhs) const;
		bool operator < (const SCString& Rhs) const;
		bool operator < (const char* Rhs) const;
		SCString& operator = (const SCString& Rhs);
		SCString& operator = (const char* String);
		SCString& operator += (const SCString& Rhs);
		SCString operator + (const SCString& Rhs) const;

		operator const char* () const;

		SCString& Format(const char* String, ...);
		SCString& AppendFormat(const char* String, ...);

		int Compare(const char* StringToCompare, int NumChars = 0) const;
		int CompareNoCase(const char* StringToCompare, int NumChars = 0) const;
		int CompareNoCase(const SCString& StringToCompare, int NumChars = 0) const;

		int IsModified() const;
		int IsEmpty() const;

		const char* GetChars() const;
		int GetLength() const;

		int IndexOf(char Delimiter, int StartIndex = 0) const;
		int LastIndexOf(char SearchCharacter, int StartIndex) const;

		SCString GetSubString(int SubstringLength, int StartIndex = 0) const;

		void ParseLines(std::vector<SCString> &Lines);
		void ParseLineItemsAsFloats(std::vector<float> &FloatValues);

		int Tokenize(const char* Delim, std::vector<char*>& Tokens);

		SCString& Append(const SCString& Rhs);

		SCString Left(int Count) const;
		SCString Right(int Count) const;

	private:
		// The maximum allowed length of a string is the maximum value that
		// can be stored in an int, -1 so that the buffer size which has to
		// include the null terminating character can also be stored in an
		// int.
		static const int MAX_LENGTH = INT_MAX - 1;

	private:
		static int TerminatedStringLength(const char* String);

		static char* StringNew(const int NumBytes);
		static void StringDelete(char* String);

		SCString& Copy(const char* String, int StringLength);

		void InternalAppendFormat
			( const char* String
			, const va_list& ArgumentList
			);

		// For Sierra Chart internal use only
		void InternalSetStatic(const char* String);
};

/*==========================================================================*/
inline SCString::SCString()
{
	m_DefaultString = "";
	m_String = (char*)m_DefaultString; //NULL
	m_OwnString = 0;
	m_IsModified = 0;
}

/*============================================================================
	This constructor is needed by the c_ArrayWrapper class.
----------------------------------------------------------------------------*/
inline SCString::SCString(int DummyValue)
{
	m_DefaultString = "";
	m_String = (char*)m_DefaultString; //NULL
	m_OwnString= 0;
	m_IsModified= 0;

}

/*==========================================================================*/
inline SCString::SCString(const SCString& Str)
{
	m_DefaultString = "";
	m_String = (char*)m_DefaultString; //NULL
	m_OwnString = 0;
	m_IsModified = Str.m_IsModified;

	const int StringLength = TerminatedStringLength(Str.m_String);

	if (StringLength != 0)
	{
		const int BufferSize = StringLength + 1;

		m_String = StringNew(BufferSize);
		if (m_String != NULL)
		{
			#if __STDC_WANT_SECURE_LIB__
			strcpy_s(m_String, BufferSize, Str.m_String);
			#else
			strcpy(m_String, Str.m_String);
			#endif
			m_OwnString = 1;
		}
		else
		{
			m_String = (char*)m_DefaultString; //NULL
		}
	}
	else
	{
		m_String = (char*)m_DefaultString; //NULL
	}
}

/*==========================================================================*/
inline SCString::SCString(const char* SourceString)
{
	m_DefaultString = "";
	m_String = (char*)m_DefaultString; //NULL
	m_OwnString= 0;
	m_IsModified= 0;


	const int StringLength = TerminatedStringLength(SourceString);

	if (StringLength != 0)
	{
		const int BufferSize = StringLength + 1;

		m_String = StringNew(BufferSize);
		if (m_String != NULL)
		{
			#if __STDC_WANT_SECURE_LIB__
			strncpy_s(m_String, BufferSize, SourceString, StringLength);
			#else
			strncpy(m_String, SourceString, StringLength);
			#endif
			m_OwnString = 1;
			m_IsModified = 1;
		}
		else
		{
			m_String = (char*)m_DefaultString; //NULL
		}
	}
	else
		m_String = (char*)m_DefaultString; //NULL

}

/*==========================================================================*/
inline SCString::SCString(const char* SourceString, int Length)
{
	m_DefaultString = "";
	m_String = (char*)m_DefaultString; //NULL
	m_OwnString= 0;
	m_IsModified= 0;

	int StringLength = 0;
	if (SourceString != NULL)
	{
		if (Length >= 0)
		{
			// Note: if `min` is defined as a macro, that prevents us from
			// using `std::min`.  But if `min` is not defined, we need to
			// specify the `std` namespace.
			#ifdef min
			StringLength = min(Length, MAX_LENGTH);
			#else
			StringLength = std::min(Length, MAX_LENGTH);
			#endif
		}
		else
			StringLength = TerminatedStringLength(SourceString);
	}

	if (StringLength != 0)
	{
		const int BufferSize = StringLength + 1;

		m_String = StringNew(BufferSize);
		if (m_String != NULL)
		{
			#if __STDC_WANT_SECURE_LIB__
			strncpy_s(m_String, BufferSize, SourceString, StringLength);
			#else
			strncpy(m_String, SourceString, StringLength);
			#endif
			m_OwnString = 1;
			m_IsModified = 1;
		}
		else
		{
			m_String = (char*)m_DefaultString; //NULL
		}
	}
	else
		m_String = (char*)m_DefaultString; //NULL

}

/*==========================================================================*/
inline SCString::SCString(const char ch)
{
	m_DefaultString = "";
	m_String = (char*)m_DefaultString; //NULL
	m_OwnString= 0;
	m_IsModified= 0;

	const int StringLength = 1;
	const int BufferSize = StringLength + 1;

	m_String = StringNew(BufferSize);
	if (m_String != NULL)
	{
		m_String[0] = ch;
		m_String[StringLength] = '\0';
		m_OwnString = 1;
		m_IsModified = 1;
	}
	else
		m_String = (char*)m_DefaultString; //NULL

}

/*==========================================================================*/
inline SCString::~SCString()
{
	if (m_OwnString && m_String != NULL)
		StringDelete(m_String);
}

/*==========================================================================*/
inline void SCString::Initialize()
{
	if (m_OwnString && m_String != NULL)
		StringDelete(m_String);


	m_String = (char*)m_DefaultString; //NULL
	m_OwnString = 0;
	m_IsModified = 0;
}

/*==========================================================================*/
inline void SCString::Clear()
{
	Initialize();
}

/*==========================================================================*/
inline bool SCString::operator == (const SCString& Rhs) const
{
	return (Compare(Rhs.GetChars()) == 0);
}

/*==========================================================================*/
inline bool SCString::operator == (const char* Rhs) const
{
	return (Compare(Rhs) == 0);
}

/*==========================================================================*/
inline bool SCString::operator != (const SCString& Rhs) const
{
	return (Compare(Rhs.GetChars()) != 0);
}

/*==========================================================================*/
inline bool SCString::operator != (const char* Rhs) const
{
	return (Compare(Rhs) != 0);
}

/*==========================================================================*/
inline bool SCString::operator < (const SCString& Rhs) const
{
	return Compare(Rhs.GetChars()) < 0;
}

/*==========================================================================*/
inline bool SCString::operator < (const char* Rhs) const
{
	return Compare(Rhs) < 0;
}

/*==========================================================================*/
inline SCString& SCString::operator = (const SCString& Rhs)
{
	if (&Rhs == this)
		return *this;

	if (Rhs.IsEmpty() && IsEmpty() )
		return *this;

	if (Rhs.m_String)
		Copy(Rhs.m_String, TerminatedStringLength(Rhs.m_String));
	else
		Initialize();

	m_IsModified = 1;

	return *this;
}

/*==========================================================================*/
inline SCString& SCString::operator = (const char* String)
{
	if ((String == NULL || String[0] == '\0') && IsEmpty())
		return *this;//nothing to do

	if (String)
		Copy(String, TerminatedStringLength(String));
	else
		Initialize();

	m_IsModified = 1;

	return *this;
}

/*==========================================================================*/
inline SCString& SCString::operator += (const SCString& Rhs)
{
	return Append(Rhs);
}

/*==========================================================================*/
inline SCString SCString::operator + (const SCString& Rhs) const
{
	SCString Result(*this);
	Result += Rhs;
	return Result;
}

/*==========================================================================*/
inline SCString::operator const char* () const
{
	if (m_String)
		return m_String;
	else
		return "";
}

/*==========================================================================*/
inline SCString& SCString::Format(const char* String, ...)
{
	Initialize();

	va_list ArgumentList;
	va_start(ArgumentList, String);

	InternalAppendFormat(String, ArgumentList);

	va_end(ArgumentList);

	return *this;
}

/*==========================================================================*/
inline SCString& SCString::AppendFormat(const char* String, ...)
{
	va_list ArgumentList;
	va_start(ArgumentList, String);

	InternalAppendFormat(String, ArgumentList);

	va_end(ArgumentList);

	return *this;
}

/*==========================================================================*/
inline int SCString::Compare(const char* StringToCompare, int NumChars) const
{
	const char* InternalString= ""; 
	if (m_String)
		InternalString=m_String;

	if (StringToCompare != NULL)
	{
		if (NumChars == 0)
			return strcmp(StringToCompare, InternalString);
		else
			return strncmp(StringToCompare, InternalString, NumChars);
	}
	else 
		return 0;

}

/*==========================================================================*/
inline int SCString::CompareNoCase(const char* StringToCompare, int NumChars) const
{
	const char* InternalString= ""; 
	if (m_String)
		InternalString=m_String;

	#if _MSC_VER >= 1400
	if (NumChars == 0)
		return _stricmp(StringToCompare, InternalString);
	else
		return _strnicmp(StringToCompare, InternalString, NumChars);
	#else
	if (NumChars == 0)
		return stricmp(StringToCompare, InternalString);
	else
		return strnicmp(StringToCompare, InternalString, NumChars);
	#endif
}

/*==========================================================================*/
inline int SCString::CompareNoCase(const SCString& StringToCompare, int NumChars) const
{
	return CompareNoCase(StringToCompare.GetChars(),NumChars);
}

/*==========================================================================*/
inline int SCString::IsModified() const
{
	return m_IsModified;
}

/*==========================================================================*/
inline int SCString::IsEmpty() const
{
	if (m_String == NULL)
		return 1;

	return (m_String[0] == '\0') ? 1 : 0;
}

/*==========================================================================*/
inline const char* SCString::GetChars() const
{
	if (m_String)
		return m_String;
	else
		return "";
}

/*==========================================================================*/
inline int SCString::GetLength() const
{
	return TerminatedStringLength(m_String);
}

/*==========================================================================*/
inline int SCString::IndexOf(char Delimiter, int StartIndex) const
{
	if (m_String == NULL)
		return -1;

	for (int i = StartIndex; i < GetLength(); i++)
	{
		if (m_String[i] == Delimiter)
			return i;
	}
	return -1;
}

/*==========================================================================*/
inline int SCString::LastIndexOf(char SearchCharacter, int StartIndex) const
{
	if (m_String == NULL)
		return -1;

	const int Length = GetLength();

	if (StartIndex >= Length)
		StartIndex = Length - 1;

	for (int CharacterIndex = StartIndex; CharacterIndex >= 0; CharacterIndex--)
	{
		if (m_String[CharacterIndex] == SearchCharacter)
			return CharacterIndex;
	}

	return -1;
}

/*==========================================================================*/
inline SCString SCString::GetSubString(int SubstringLength, int StartIndex) const
{
	if (m_String == NULL)
		return SCString();

	const int Length = GetLength();
	if (StartIndex >= Length)
		return SCString();

	if (SubstringLength > Length - StartIndex)
		SubstringLength = Length - StartIndex;

	SCString Result;
	Result.Copy(m_String + StartIndex, SubstringLength);

	return Result;
}

/*==========================================================================*/
inline void SCString::ParseLines(std::vector<SCString>& Lines)
{
	int StartIndex = 0;
	while(true)
	{
		int Pos = IndexOf('\n', StartIndex);


		if (Pos == -1)
			Pos = GetLength(); 

		SCString Line;
		Line = GetSubString(Pos - StartIndex, StartIndex);
		Lines.push_back(Line);
		StartIndex = Pos + 1;

		if(StartIndex >= GetLength())
			break;

	}
}

/*==========================================================================*/
inline void SCString::ParseLineItemsAsFloats(std::vector<float>& FloatValues)
{
	char FieldsDelimitter = ',';

	int find_delim = IndexOf(FieldsDelimitter);
	int pos = 0;

	int ArraySize = 1000;
	int CurrentItem = 0;

	while (find_delim > 0 && CurrentItem < ArraySize)
	{
		SCString NextStr = GetSubString(find_delim - pos, pos);

		FloatValues.push_back((float)atof(NextStr));
		pos = find_delim + 1;
		find_delim = IndexOf(FieldsDelimitter,find_delim + 1);

		CurrentItem++;
	}
}

/*==========================================================================*/
inline int SCString::Tokenize(const char* Delim, std::vector<char*>& Tokens)
{
	Tokens.erase(Tokens.begin(), Tokens.end());

	if (m_String == NULL)
		return 0;

	// The object must own the string for this method because this method
	// modifies the string.
	if (!m_OwnString)
		Copy(m_String, TerminatedStringLength(m_String));

	Tokens.push_back(m_String);

	if (Delim == NULL)
		return 1;

	int StrLen = GetLength();
	int DelimLen = TerminatedStringLength(Delim);

	if (DelimLen == 0)
		return 1;  // Not worth splitting every character

	for (int a = 0; a < StrLen; a++)
	{
		// Look for the delimiter
		bool DelimFound = true;
		for (int b = 0; b < DelimLen; b++)
		{
			if (a + b >= StrLen)
			{
				DelimFound = false;
				break;
			}

			if (m_String[a + b] != Delim[b])
			{
				DelimFound = false;
				break;
			}
		}

		if (DelimFound)
		{
			m_String[a] = '\0';
			Tokens.push_back(m_String + a + DelimLen);
			a += DelimLen - 1;
		}
	}

	return static_cast<int>(Tokens.size());
}

/*==========================================================================*/
inline SCString& SCString::Append(const SCString& Rhs)
{
	int RightStringLength = Rhs.GetLength();
	if (RightStringLength == 0)
		return *this;

	const int LeftStringLength = GetLength();

	// Make sure the new string length does not exceed MAX_LENGTH.
	if (RightStringLength > MAX_LENGTH - LeftStringLength)
		RightStringLength = MAX_LENGTH - LeftStringLength;

	const int NewStringLength = LeftStringLength + RightStringLength;

	char* NewString = StringNew(NewStringLength + 1);
	if (NewString)
	{
		const char*InternalString = "";
		if (m_String)
			InternalString =m_String;

		#if __STDC_WANT_SECURE_LIB__
		strncpy_s(NewString, NewStringLength + 1, InternalString, LeftStringLength);
		strncpy_s(NewString + LeftStringLength, NewStringLength + 1 - LeftStringLength, Rhs.m_String, RightStringLength);
		#else
		strncpy(NewString, InternalString, LeftStringLength);
		strncpy(NewString + LeftStringLength, Rhs.m_String, RightStringLength);
		#endif
		NewString[NewStringLength] = '\0';

		Initialize();
		m_String = NewString;
		m_OwnString = 1;
	}

	m_IsModified = 1;

	return *this;
}

/*==========================================================================*/
inline SCString SCString::Left(int Count) const
{
	const int StringLength = GetLength();

	if (Count >= StringLength)
		return *this;
	else if (Count > 0)
		return SCString(m_String, Count);
	else if (Count < 0)
	{
		int TargetLength = StringLength + Count;
		if (TargetLength <= 0)
			return SCString();
		else
			return SCString(m_String, TargetLength);
	}
	else
		return SCString();
}

/*==========================================================================*/
inline SCString SCString::Right(int Count) const
{
	int StringLength = GetLength();

	if (Count >= StringLength)
		return *this;
	else if (Count > 0)
		return SCString(m_String + (StringLength - Count), Count);
	else if (Count < 0)
	{
		int TargetLength = StringLength + Count;
		if (TargetLength <= 0)
			return SCString();
		else
			return SCString(m_String + (StringLength - TargetLength), TargetLength);
	}
	else
		return SCString();
}

/*============================================================================
	Static
----------------------------------------------------------------------------*/
inline int SCString::TerminatedStringLength(const char* String)
{
	if (String == NULL)
		return 0;

	const size_t StringLength = strlen(String);

	if (StringLength > MAX_LENGTH)
		return MAX_LENGTH;

	return static_cast<int>(StringLength);
}

/*============================================================================
	Static
----------------------------------------------------------------------------*/
inline char* SCString::StringNew(const int NumBytes)
{
	if (NumBytes <= 0)
		return NULL;

	char* p_Block = (char*)HeapAlloc(GetProcessHeap(), 0, NumBytes);

	if (p_Block != NULL)
		memset(p_Block, 0, NumBytes);

	return p_Block;
}

/*============================================================================
	Static
----------------------------------------------------------------------------*/
inline void SCString::StringDelete(char* String)
{
	if (String != NULL)
		HeapFree(GetProcessHeap(), 0, String);
}

/*==========================================================================*/
inline SCString& SCString::Copy(const char* String, int StringLength)
{
	if (String == NULL)
		return *this;

	Initialize();

	if (StringLength != 0)
	{
		if (StringLength > MAX_LENGTH)
			StringLength = MAX_LENGTH;

		const int BufferSize = StringLength + 1;

		m_String = StringNew(BufferSize);

		if (m_String == NULL)
			m_String = (char*)m_DefaultString;
		else
		{
			#if __STDC_WANT_SECURE_LIB__
			strncpy_s(m_String, BufferSize, String, StringLength);
			#else
			strncpy(m_String, String, StringLength);
			#endif
			m_String[StringLength] = '\0';
			m_OwnString = 1;
		}
	}

	m_IsModified = 1;

	return *this;
}

/*==========================================================================*/
inline void SCString::InternalAppendFormat
( const char* FormatString
, const va_list& ArgumentList
)
{
	if (FormatString == NULL)
		return;

	const int PriorLength = TerminatedStringLength(m_String);

	const int TargetLength = _vscprintf(FormatString, ArgumentList);

	if (TargetLength < 0 || TargetLength > MAX_LENGTH - PriorLength)
		return;

	char* NewStringBuffer = StringNew(PriorLength + TargetLength + 1);
	if (NewStringBuffer == NULL)
		return;

	// Copy the current string to the new buffer.
	if (PriorLength > 0)
		memcpy(NewStringBuffer, m_String, PriorLength);

	#if __STDC_WANT_SECURE_LIB__
	vsprintf_s
		( NewStringBuffer + PriorLength
		, TargetLength + 1
		, FormatString
		, ArgumentList
		);
	#else
	vsprintf(NewStringBuffer + PriorLength, FormatString, ArgumentList);
	#endif

	Clear();

	m_String = NewStringBuffer;
	m_OwnString = 1;
	m_IsModified = 1;
}

/*============================================================================
	For Sierra Chart internal use only.
----------------------------------------------------------------------------*/
inline void SCString::InternalSetStatic(const char* String)
{
	Initialize();

	m_String = const_cast<char*>(String);
	m_OwnString = 0;
	m_IsModified = 0;
}

/*==========================================================================*/
