#ifndef STRINGCONV_H__
#define STRINGCONV_H__

#pragma once

#include <string> 
#include <sstream>

#define USE_SIMPLEUNITTESTS 0
/*
To include unit tests for the functions of this header file,
set USE_SIMPLEUNITTESTS to 1 like in 

  #define USE_SIMPLEUNITTESTS 1

and place the files
 
  SimpleUnitTests.h
  NearlyEqual.h 

in the same directory as StringConv.h
*/

#if USE_SIMPLEUNITTESTS
#include "SimpleUnitTests.h" 
#endif

#define USE_MSCLR_MARSHAL 0

#if	_MSC_VER >= 1500 // erst ab VS2008 
#if	USE_MSCLR_MARSHAL // Mehrdeutigkeiten
  #include <msclr\marshal.h>
  #include <msclr\marshal_cppstd.h>
#endif // USE_MSCLR_MARSHAL
#endif



//------ ###Aufgabe 4.1.??? -------------------------------------------------------

// d)

// ??? Da die Unterschiede zwischen Deklarationen und Definitionen zum
// Zeitpunkt der Aufgabenstellung noch nicht behandelt wurden, wurde die
// Lösung in eine einfache ".cpp"-Datei geschrieben.
// Es wird eine weitere Aufgabe in Kapitel 6 sein, diese Datei in eine
// ".h" und eine ".cpp"-Datei aufzuteilen. Diese Dateien befinden sich im
// Verzeichnis CppLib.

namespace rk1 {

#if  __cplusplus_cli

using System::String;
using std::string;
// using namespace System; // Diese using-Anweisung funktioniert meist 
// auch, kann aber zu Namenskonflikten führen.
	

char* toCharPtr(String^ S)
{
using System::Runtime::InteropServices::Marshal;
char* p = (char*) Marshal::StringToHGlobalAnsi(S).ToPointer();

//// Always free the unmanaged string.
//Marshal::FreeHGlobal(IntPtr(stringPointer));
return p;
}

String^ toString(const char* s)
{
return gcnew System::String(s);
}

System::String^ toString(const wchar_t* s)
{
return gcnew System::String(s);
}

String^ toString(std::string s)
{
return gcnew System::String(s.c_str());
}

System::String^ toString(std::wstring s)
{
return gcnew System::String(s.c_str());
}

std::string tostring(String^ S)
{ // warning: conversion from 'wchar_t' to 'char', possible loss of data
  // Für Strings mit Sonderzeichen nur in wstring konvertieren
  // ??? noch testen mit deutschen Umlauten
string s;
for (int i=0;i<S->Length; i++)
  s+=S[i];
return s;
}

std::wstring towstring(String^ S)
{
std::wstring w;
for (int i=0; i<S->Length; i++)
  w+=S[i];
return w;
}


#if USE_SIMPLEUNITTESTS
namespace test {

  void test_tostring()
  {
  Assert::WriteLine(__FUNCTION__);
  const int max=6;
  char*     a[max]={ "", "1", "a", "ab", "11", "123456abcdefg"};
  using std::wstring;
  wchar_t*  w[max]={L"",L"1",L"a",L"ab",L"11",L"123456abcdefg"};
  array<String^>^ S=gcnew array<String^>(max)
                   { "", "1", "a", "ab", "11", "123456abcdefg"};

  const char* ca[max];
  string       s[max];
  
  const wchar_t* cw[max];
  wstring        ws[max];
  
  for (int i=0;i<max;i++)
    {
      ca[i]=a[i];
      s[i] =a[i];
  
      cw[i]=w[i];
      ws[i]=w[i];

      String^ S1=toString(a[i]);
      Assert::AreEqual(S1,S[i],"toString 1.");

      S1=toString(ca[i]);
      Assert::AreEqual(S1,S[i],"toString 2.");

      S1=toString(s[i]);
      Assert::AreEqual(S1,S[i],"toString 3.");

      S1=toString(w[i]);
      Assert::AreEqual(S1,S[i],"toString 4.");
      
      S1=toString(cw[i]);
      Assert::AreEqual(S1,S[i],"toString 5.");
      
      S1=toString(ws[i]);
      Assert::AreEqual(S1,S[i],"toString 6.");
      
      Assert::AreEqual(strcmp(toCharPtr(S[i]),a[i]),0,"toCharPtr(String)");

      string s1=tostring(S[i]);
      Assert::AreEqual(s1,s[i],"tostring 8.");

      wstring w1=towstring(S[i]);
      Assert::AreEqual(w1,ws[i],"towstring 9.");      
    }  
  }

} // end of namespace test 
#endif // #if USE_SIMPLEUNITTESTS

#endif  // __cplusplus_cli

//------ ###Aufgabe 4.1, 1 ----------------------------------------------------------

int toInt(const string& s, bool& success)
{
std::istringstream is(s);
int result=0;
is>>result;
success=(s.length()==is.tellg());
return result;
}

#if USE_SIMPLEUNITTESTS
namespace test {

  void test_stringToInt(int n)
  {
  Assert::WriteLine(__FUNCTION__);
  bool success;
  int x=toInt(string("123"),success);    // result=123, t=3
  Assert::AreEqual(x, 123, "toInt(123)");
  Assert::AreEqual(success, true, "toInt(123)");

  x=toInt(string("123a"),success);   // result=123, t=3
  Assert::AreEqual(x, 123, "toInt(123a)");
  Assert::AreEqual(success, false, "toInt(123a)");

  x=toInt(string("a123"),success);   // result=0,   t=-1
  Assert::AreEqual(x, 0, "toInt(a123)");
  Assert::AreEqual(success, false, "toInt(a123)");

  x=toInt(string("12 34"),success);  // result=12,  t=2
  Assert::AreEqual(x, 12, "toInt(12 34)");
  Assert::AreEqual(success, false, "toInt(12 34)");

  x=toInt(string(" -1234"),success); // result=-1234, t=6
  Assert::AreEqual(x, -1234, "toInt( -1234)");
  Assert::AreEqual(success, true, "toInt( -1234)");
  }

} // end of namespace test 
#endif // #if USE_SIMPLEUNITTESTS

double toDouble(const string& s, bool& success)
{
std::istringstream is(s);
double result=0;
is>>result;
success=(s.length()==is.tellg());
return result;
}

#if USE_SIMPLEUNITTESTS
namespace test {

  void test_stringToDouble()
  {
  Assert::WriteLine(__FUNCTION__);
  bool success;
  double x=toDouble(string("123.45"),success); // result=123, t=3
  Assert::AreEqual(x, 123.45, "toDouble(123.45)");
  Assert::AreEqual(success, true, "toDouble(123.45)");

  x=toDouble(string("-123.45"),success);       // result=123, t=3
  Assert::AreEqual(x, -123.45, "toDouble(-123.45)");
  Assert::AreEqual(success, true, "toDouble(-123.45)");

  x=toDouble(string("0.12345"),success);       // result=123, t=3
  Assert::AreEqual(x, 0.12345, "toDouble(0.12345)");
  Assert::AreEqual(success, true, "toDouble(0.12345)");

  x=toDouble(string("-0.12345"),success);      // result=123, t=3
  Assert::AreEqual(x, -0.12345, "toDouble(-0.12345)");
  Assert::AreEqual(success, true, "toDouble(-0.12345)");

  x=toDouble(string("-0.0000"),success);       // result=123, t=3
  Assert::AreEqual(x, 0.0, "stringToDouble(-0.0000)");
  Assert::AreEqual(success, true, "stringToDouble(-0.0000)");

  x=toDouble(string("-0.12.34"),success);      // result=123, t=3
  Assert::AreEqual(x, -0.12, "stringToDouble(-0.12.34)");
  Assert::AreEqual(success, false, "stringToDouble(-0.12.34)");
  }

} // end of namespace test 
#endif // #if USE_SIMPLEUNITTESTS

string tostring(int x)
{
std::ostringstream os;
os<<x;
return os.str();
}

string tostring(double x)
{
std::ostringstream os;
os<<x;
return os.str();
}

#if USE_SIMPLEUNITTESTS
namespace test {

  void test_inttostring(int n)
  {
  Assert::WriteLine(__FUNCTION__);
  Assert::AreEqual(tostring(0),string("0"), "tostring(0)");
  Assert::AreEqual(tostring(1),string("1"), "tostring(1)");
  Assert::AreEqual(tostring(-1),string("-1"), "tostring(-1)");
  Assert::AreEqual(tostring(12),string("12"), "tostring(12)");
  Assert::AreEqual(tostring(-12),string("-12"), "tostring(-12)");
  for (int i=-n; i<n; i++)
    {
     string s=tostring(i);
     bool success;
     int is=toInt(s, success);
     Assert::AreEqual(i,is, "tostring/toint: "+i.ToString());     
    }
  }

  void test_doubletostring(int n)
  {
  Assert::WriteLine(__FUNCTION__);
  Assert::AreEqual(tostring(0.0),string("0"), "tostring(0.0)");
  Assert::AreEqual(tostring(1.0),string("1"), "tostring(1.0)");
  Assert::AreEqual(tostring(-1.0),string("-1"), "tostring(-1.0)");
  Assert::AreEqual(tostring(1.2),string("1.2"), "tostring(1.2)");
  Assert::AreEqual(tostring(-1.2),string("-1.2"), "tostring(-1.2)");
  for (int i=-n; i<n; i++)
    {
     string s=tostring(i+0.5);
     bool success;
     double d=toDouble(s, success);
     Assert::AreEqual(i+0.5,d, "tostring/toDouble: "+i.ToString());     
    }
  }

} // end of namespace test 
#endif // #if USE_SIMPLEUNITTESTS

//------ ###Aufgaben 9.1 -----------------------------------------------------

//------ ###Aufgabe 2 -----------------------------------------------------
// a)

template<typename T>
string tostring(T x)
{
std::ostringstream os;
os<<x;
return os.str();
}

template<typename T>
T ConvertTo(string s)
{
T result;
std::istringstream is(s);
is>>result;
if (s.length()!=is.tellg())
  throw std::invalid_argument("Cannot convert '"+s+"' to int");
return result;
}

template<typename T>
bool tryConvertTo(string s, T& result)
{
std::istringstream is(s);
is>>result;
return (s.length()==is.tellg());
}

// b)
// ??? Eine explizite Spezialisierung wäre nicht so gut
std::string tostring(bool x)
{
if (x) return "true";
else return "false";
}
// /* ???? 6.2.08

template<>
bool ConvertTo<bool>(std::string x)
{
if (x=="true") return true;
else if (x=="false") return false;
else
  {
    throw std::invalid_argument("Cannot convert '"+x+"' to bool");
  }
}

template<>
bool tryConvertTo<bool>(std::string x, bool& result)
{
bool success=true;
if (x=="true") result=true;
else if (x=="false") result=false;
else
  {
    result=false;
    success=false;
  }
return success;
}

// c)
// Eine explizite Spezialisierung wäre nicht so gut

#if USE_SIMPLEUNITTESTS
namespace test {

  void test_to_string_template(int n)
  {
  Assert::WriteLine(__FUNCTION__);
  for (int i=-n; i<=n; i++)
    {
      string s=tostring(i);
      int x=ConvertTo<int>(s);
      Assert::AreEqual(i,x,"test_to_string_template int");
     }
  for (double i=-n+0.5; i<=n; i++)
    {
      string s=tostring(i);
      double x=ConvertTo<double>(s);
      Assert::AreEqual(i,x,"test_to_string_template double");
     }  
  }

} // end of namespace test 
#endif // #if USE_SIMPLEUNITTESTS

#if  __cplusplus_cli

namespace Alternative_1 { // ----------------------------------------
  // String zeichenweise aus string/wstring-Zeichen zusammensetzen  

  String^ toString(string s)
  { 
  String^ S="";
  for (int i=0;i<s.length();i++) 
    S+=wchar_t(s[i]);
  return S;
  }

  String^ toString(std::wstring s)
  { 
  String^ S="";
  for (int i=0;i<s.length();i++) 
    S+=s[i];
  return S;
  }

#if USE_SIMPLEUNITTESTS
  namespace test {

    void test_tostring()
    {
    Assert::WriteLine(__FUNCTION__);
    const int max=6;
    string   s[max]=     { "", "1", "a", "ab", "11", "123456abcdefg"};
    std::wstring  w[max]={L"",L"1",L"a",L"ab",L"11",L"123456abcdefg"};
    array<String^>^ S=gcnew array<String^>(max)
                         { "", "1", "a", "ab", "11", "123456abcdefg"};

    for (int i=0;i<max;i++)
    {
      String^ S1=toString(s[i]);
      Assert::AreEqual(S1,S[i],"toString(string) Alternative 1.");

      S1=toString(w[i]);
      Assert::AreEqual(S1,S[i],"toString(wstring) Alternative 1.");
    }  
  }

  } // end of namespace test 
#endif // #if USE_SIMPLEUNITTESTS

} // end of namespace Alternative_1  ---------------------------------


namespace use_InteropMarshal {   // ----------------------------------------
  // Einige der Funktionen von oben, hier mit den 
  // System::Runtime::InteropServices::Marshal Funktionen 

  using System::Runtime::InteropServices::Marshal;
  
  String^ toString(const char* s)
  { 
  return Marshal::PtrToStringAnsi((System::IntPtr) (char *) s);
  }

  String^ toString(const wchar_t* s)
  { 
  return Marshal::PtrToStringUni((System::IntPtr) (wchar_t *) s);
  }

  System::String^ toString(const std::string& s)
  { 
  return Marshal::PtrToStringAnsi((System::IntPtr)(char*)s.c_str());
  }

  System::String^ toString(const std::wstring& s)
  { 
  return Marshal::PtrToStringUni((System::IntPtr)(wchar_t*)s.c_str());
  }

  std::string tostring(System::String^ S)
  {
  std::string r;
  if (S == nullptr) return r;
  System::IntPtr ip;
  try
  {
	ip = Marshal::StringToHGlobalAnsi(S);
    r = static_cast<const char*>(ip.ToPointer());
  }
  catch(...)
  {
	  throw;
  }
  finally
  {
    Marshal::FreeHGlobal(ip);
  }
  return r;
  }

#if USE_SIMPLEUNITTESTS
  namespace test {
 
    void test_tostring()
    {
	Assert::WriteLine(__FUNCTION__);
    const int max=6;
    const char*     a[max]={ "", "1", "a", "ab", "11", "123456abcdefg"};
    const wchar_t*  w[max]={L"",L"1",L"a",L"ab",L"11",L"123456abcdefg"};
    array<String^>^ S=gcnew array<String^>(max)
                     { "", "1", "a", "ab", "11", "123456abcdefg"};
  
    for (int i=0;i<max;i++)
    {
      String^ S1=toString(a[i]);
      Assert::AreEqual(S1,S[i],"toString 1.");

      S1=toString(w[i]);
      Assert::AreEqual(S1,S[i],"toString 2.");      
      
      string s=a[i];
	  S1=toString(s);
      Assert::AreEqual(S1,S[i],"toString 3.");
      
      std::wstring ws=w[i];
	  S1=toString(ws);
      Assert::AreEqual(S1,S[i],"toString 4.");
      

      string s1=tostring(S1);
      Assert::AreEqual(s1,s,"tostring 5.");
	  }  
  }

} // end of namespace test 
#endif // USE_SIMPLEUNITTESTS

} // end of namespace use_InteropMarshal ----------------------------

 
#if	_MSC_VER >= 1500 // erst ab VS2008 
#if	USE_MSCLR_MARSHAL // Mehrdeutigkeiten
  #include <msclr\marshal.h>
  #include <msclr\marshal_cppstd.h>
#endif

#if	USE_MSCLR_MARSHAL // Mehrdeutigkeiten
namespace use_msclr { // ----------------------------------------
  // Einige alternative Versionen der Funktionen von oben, hier mit den  
  // marshal_as Funktionen von Visual C++ 2008.
  // Diese Funktionen sollen vor allem die Syntax von marshal_as zeigen. 
  // Sie sind eigentlich nicht notwendig, da marshal_as genauso einfach 
  // wie die toString-Funktionen aufgerufen werden kann. 

  using namespace msclr::interop; 
  using std::wstring;

  String^ toString(const char* s)
  {
  return marshal_as<String^>(s);
  }

  System::String^ toString(const wchar_t* s)
  {
  return marshal_as<String^>(s);
  }


  String^ toString(string s)
  {
  return marshal_as<String^>(s);
  }

  System::String^ toString(wstring s)
  {
  return marshal_as<String^>(s);
  }


  string tostring(System::String^ s)
  {
  return marshal_as<string>(s);
  }

  wstring towstring(System::String^ s)
  {
  return marshal_as<wstring>(s);
  }


#if USE_SIMPLEUNITTESTS
  namespace test {

    void test_tostring()
    {
	Assert::WriteLine(__FUNCTION__);
    const int max=6;
    const char*     a[max]={ "", "1", "a", "ab", "11", "123456abcdefg"};
    const wchar_t*  w[max]={L"",L"1",L"a",L"ab",L"11",L"123456abcdefg"};
    array<String^>^ S=gcnew array<String^>(max)
                     { "", "1", "a", "ab", "11", "123456abcdefg"};
  
    for (int i=0;i<max;i++)
    {
      String^ S1=toString(a[i]);
      Assert::AreEqual(S1,S[i],"toString 1.");
	  S1=marshal_as<String^>(a[i]);
      Assert::AreEqual(S1,S[i],"marshal_as<String^> 2.");

      S1=toString(w[i]);
      Assert::AreEqual(S1,S[i],"toString 3.");      
      S1=marshal_as<String^>(w[i]);
      Assert::AreEqual(S1,S[i],"marshal_as<String^> 4.");
      
      string s=a[i];
	  S1=toString(s);
      Assert::AreEqual(S1,S[i],"toString 5.");
	  S1=marshal_as<String^>(s);
      Assert::AreEqual(S1,S[i],"marshal_as<String^> 6.");
      
      wstring ws=w[i];
	  S1=toString(ws);
      Assert::AreEqual(S1,S[i],"toString 7.");
	  S1=marshal_as<String^>(ws);
      Assert::AreEqual(S1,S[i],"marshal_as<String^> 8.");
      

      string s1=tostring(S1);
      Assert::AreEqual(s1,s,"tostring(const char*) 9.");
	  s1=marshal_as<string>(S1);
      Assert::AreEqual(s1,s,"marshal_as<string> 10.");

      wstring ws1=towstring(S1);
      Assert::AreEqual(ws1,ws,"towstring 11.");
	  ws1=marshal_as<wstring>(S1);
      Assert::AreEqual(ws1,ws,"marshal_as<wstring> 12.");
	  }  
  }

} // end of namespace test 
#endif // USE_SIMPLEUNITTESTS


} // end of namespace use_msclr  ----------------------------------------
#endif // #if	_MSC_VER >= 1500
#endif 

#if USE_SIMPLEUNITTESTS
namespace test { // ----------------------------------------

void test_StringConv(System::Windows::Forms::TextBox^ tb)
{
const char* s="jjj";
tb->AppendText("toString: "+toString(s)+"\r\n");

Assert::Init(tb,"test_StringConv"); // Tests initialisieren
test_tostring();
test_stringToInt(100);;
test_inttostring(100);
test_doubletostring(100);
test_stringToDouble();
test_to_string_template(100);
Alternative_1::test::test_tostring();
use_InteropMarshal::test::test_tostring();
#if	USE_MSCLR_MARSHAL // Mehrdeutigkeiten
use_msclr::test::test_tostring();
#endif // USE_MSCLR_MARSHAL
Assert::Summary(); // Testzusammenfassung ausgeben
}

} // end of namespace test  // ----------------------------------------
#endif // USE_SIMPLEUNITTESTS

#endif  // __cplusplus_cli
} // end of namespace rk1 
#endif // STRINGCONV_H__

