/* 
 * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
 *
 * 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; version 2 of the
 * License.
 * 
 * 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., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

#ifndef __CONV_UTILS_H__
#define __CONV_UTILS_H__

#include <vcclr.h>
#include <string>
#include <vector>
#include <list>

#include "base/drawing.h"

using namespace System;
using namespace System::Text;
using namespace System::Collections::Generic;
using namespace System::Drawing;

namespace MySQL {
  
static String^ CppStringToNative(const std::string &str)
{
  if (str.length() == 0)
    return "";

  if (str.find('\r') == std::string::npos)
  {
    // convert from \n to \r\n (but don't double-convert \r\n to \r\r\n)
    String^ result = gcnew String(str.c_str(), 0, (int) str.size(), Encoding::UTF8);
    return result->Replace("\n", "\r\n");
  }
  return gcnew String(str.c_str(), 0, (int) str.size(), Encoding::UTF8);
}

static String^ CppStringToNativeRaw(const std::string &str)
{
  if (str.length() == 0)
    return "";

  return gcnew String(str.c_str(), 0, (int) str.size(), Encoding::UTF8);
}


static std::string NativeToCppString(String^ str)
{
  if (str == nullptr || str->Length == 0)
    return "";

  array<unsigned char>^ chars= Encoding::UTF8->GetBytes(str->Replace("\r\n", "\n"));
  if (chars == nullptr || chars->Length == 0)
    return "";

  pin_ptr<unsigned char> char_ptr= &chars[0];
  std::string result((char*) char_ptr);
  return result;
}

static std::string NativeToCppStringRaw(String^ str)
{
  if (str == nullptr || str->Length == 0)
    return "";

  array<unsigned char>^ chars= Encoding::UTF8->GetBytes(str);
  if (chars == nullptr || chars->Length == 0)
    return "";

  pin_ptr<unsigned char> char_ptr= &chars[0];
  std::string result((char*) char_ptr);
  return result;
}

static List<String^>^ CppStringListToNative(const std::vector<std::string> &input)
{
  int cap = static_cast<int>(input.size());
  List<String^>^ result= gcnew List<String^>(cap);
  std::vector<std::string>::const_iterator e= input.end();
  
  for (std::vector<std::string>::const_iterator i= input.begin(); i != e; i++)
    result->Add(CppStringToNative(*i));
  return result;
}

static std::vector<std::string> NativeToCppStringList(List<String^>^ input)
{
  std::vector<std::string> result;
  result.reserve(input->Count);
  for each(String ^str_item in input)
    result.push_back(NativeToCppString(str_item));
  return result;
}

static std::list<std::string> NativeToCppStringList2(List<String^>^ input)
{
  std::list<std::string> result;
  for each(String ^str_item in input)
    result.push_back(NativeToCppString(str_item));
  return result;
}

static List<String^>^ CppStringListToNative2(const std::list<std::string> &input)
{
  int cap = static_cast<int>(input.size());
  List<String^>^ result= gcnew List<String^>(cap);
  std::list<std::string>::const_iterator e= input.end();
  
  for (std::list<std::string>::const_iterator i= input.begin(); i != e; i++)
    result->Add(CppStringToNative(*i));
  return result;
}

static List<int>^ CppVectorToIntList(const std::vector<int>& input)
{
  typedef const std::vector<int> SourceContainerType;
  typedef List<int> TargetContainerType;

  TargetContainerType^ result= gcnew TargetContainerType(static_cast<int>(input.size()));
  SourceContainerType::const_iterator e= input.end();
  
  for(SourceContainerType::const_iterator i= input.begin(); i != e; i++)
    result->Add(*i);

  return result;
}

static std::vector<int> IntListToCppVector(List<int>^ input)
{
  typedef const List<int>^ SourceContainerType;
  typedef std::vector<int> TargetContainerType;

  TargetContainerType result;
  result.reserve(static_cast<int>(input->Count));
  
  for each(int listitem in input)
    result.push_back(listitem);

  return result;
}

static Dictionary<String^, String^>^ CppStringMapToDictionary(const std::map<std::string, std::string>& input)
{
  typedef const std::map<std::string, std::string> SourceContainerType;
  typedef Dictionary<String^, String^> TargetContainerType;

  TargetContainerType^ result = gcnew TargetContainerType(static_cast<int>(input.size()));
  SourceContainerType::const_iterator e = input.end();

  for(SourceContainerType::const_iterator iterator = input.begin(); iterator != input.end(); ++iterator)
    result->Add(CppStringToNativeRaw(iterator->first), CppStringToNativeRaw(iterator->second));

  return result;
}

//--------------------------------------------------------------------------------------------------

static base::Color NativeToColor(System::Drawing::Color^ color)
{
  return base::Color(color->R / 255.0, color->G / 255.0, color->B / 255.0, color->A / 255.0);
}

//--------------------------------------------------------------------------------------------------

} // namespace MySQL

// C++/CLI variant of the C# is operator.
template <typename T, class O> 
Boolean is(O o) {
  return o->GetType() == T::typeid;
}

/**
 * A more general conversion routine from any managed collection to a std::vector.
 * For strings no new line conversion is done.
 */
template<typename T>
generic<typename S>
std::vector<T> marshal_as(System::Collections::Generic::ICollection<S>^ list)
{
  if (list == nullptr)
    throw gcnew ArgumentNullException(L"list");

  std::vector<T> result;
  result.reserve(list->Count);
  for each (S& element in list)
    result.push_back(marshal_as<T>(element));

  return result;
}

#endif // __CONV_UTILS_H__
