/*
*      Copyright (C) 2010 Marcel Groothuis
*
*  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, either version 2 of the License, or
*  (at your option) any later version.
*
*  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, see <http://www.gnu.org/licenses/>.
*
*/
#if defined(TARGET_WINDOWS)
#pragma warning(disable:4244) //wchar to char = loss of data
#endif

#include "utils.h"
#include "p8-platform/os.h"
#include "client.h" //For XBMC->Log
#include <string>
#include <algorithm> // sort

using namespace ADDON;

namespace Json
{
  void printValueTree( const Json::Value& value, const std::string& path)
  {
    switch ( value.type() )
    {
    case Json::nullValue:
      XBMC->Log(LOG_DEBUG, "%s=null\n", path.c_str() );
      break;
    case Json::intValue:
      XBMC->Log(LOG_DEBUG, "%s=%d\n", path.c_str(), value.asInt() );
      break;
    case Json::uintValue:
      XBMC->Log(LOG_DEBUG, "%s=%u\n", path.c_str(), value.asUInt() );
      break;
    case Json::realValue:
      XBMC->Log(LOG_DEBUG, "%s=%.16g\n", path.c_str(), value.asDouble() );
      break;
    case Json::stringValue:
      XBMC->Log(LOG_DEBUG, "%s=\"%s\"\n", path.c_str(), value.asString().c_str() );
      break;
    case Json::booleanValue:
      XBMC->Log(LOG_DEBUG, "%s=%s\n", path.c_str(), value.asBool() ? "true" : "false" );
      break;
    case Json::arrayValue:
      {
        XBMC->Log(LOG_DEBUG, "%s=[]\n", path.c_str() );
        int size = value.size();
        for ( int index =0; index < size; ++index )
        {
          static char buffer[16];
          snprintf( buffer, 16, "[%d]", index );
          printValueTree( value[index], path + buffer );
        }
      }
      break;
    case Json::objectValue:
      {
        XBMC->Log(LOG_DEBUG, "%s={}\n", path.c_str() );
        Json::Value::Members members( value.getMemberNames() );
        std::sort( members.begin(), members.end() );
        std::string suffix = *(path.end()-1) == '.' ? "" : ".";
        for ( Json::Value::Members::iterator it = members.begin(); 
          it != members.end(); 
          ++it )
        {
          const std::string &name = *it;
          printValueTree( value[name], path + suffix + name );
        }
      }
      break;
    default:
      break;
    }
  }
} //namespace Json

namespace BASE64
{

static const char *to_base64 =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
abcdefghijklmnopqrstuvwxyz\
0123456789+/";

std::string b64_encode(unsigned char const* in, unsigned int in_len, bool urlEncode)
{
  std::string ret;
  int i(3);
  unsigned char c_3[3];
  unsigned char c_4[4];

  while (in_len) {
    i = in_len > 2 ? 3 : in_len;
    in_len -= i;
    c_3[0] = *(in++);
    c_3[1] = i > 1 ? *(in++) : 0;
    c_3[2] = i > 2 ? *(in++) : 0;

    c_4[0] = (c_3[0] & 0xfc) >> 2;
    c_4[1] = ((c_3[0] & 0x03) << 4) + ((c_3[1] & 0xf0) >> 4);
    c_4[2] = ((c_3[1] & 0x0f) << 2) + ((c_3[2] & 0xc0) >> 6);
    c_4[3] = c_3[2] & 0x3f;

    for (int j = 0; (j < i + 1); ++j)
    {
      if (urlEncode && to_base64[c_4[j]] == '+')
        ret += "%2B";
      else if (urlEncode && to_base64[c_4[j]] == '/')
        ret += "%2F";
      else
        ret += to_base64[c_4[j]];
    }
  }
  while ((i++ < 3))
    ret += urlEncode ? "%3D" : "=";
  return ret;
}

} //Namespace BASE64

// transform [\\nascat\qrecordings\NCIS\2012-05-15_20-30_SBS 6_NCIS.ts]
// into      [smb://user:password@nascat/qrecordings/NCIS/2012-05-15_20-30_SBS 6_NCIS.ts]
std::string ToCIFS(std::string& UNCName)
{
  std::string CIFSname = UNCName;
  std::string SMBPrefix = "smb://";
  size_t found;
  while ((found = CIFSname.find("\\")) != std::string::npos)
  {
    CIFSname.replace(found, 1, "/");
  }
  CIFSname.erase(0,2);
  CIFSname.insert(0, SMBPrefix);
  return CIFSname;
}


// transform [smb://user:password@nascat/qrecordings/NCIS/2012-05-15_20-30_SBS 6_NCIS.ts]
// into      [\\nascat\qrecordings\NCIS\2012-05-15_20-30_SBS 6_NCIS.ts]
std::string ToUNC(std::string& CIFSName)
{
  std::string UNCname = CIFSName;

  UNCname.erase(0,6);
  size_t found;
  while ((found = UNCname.find("/")) != std::string::npos)
  {
    UNCname.replace(found, 1, "\\");
  }
  UNCname.insert(0, "\\\\");
  return UNCname;
}

std::string ToUNC(const char* CIFSName)
{
  std::string temp = CIFSName;
  return ToUNC(temp);
}
#if defined(TARGET_WINDOWS)
//////////////////////////////////////////////////////////////////////////////
//
// *** Routines to convert between Unicode UTF-8 and Unicode UTF-16 ***
//
// By Giovanni Dicanio <giovanni.dicanio AT gmail.com>
//
// Last update: 2010, January 2nd
//
//
// These routines use ::MultiByteToWideChar and ::WideCharToMultiByte
// Win32 API functions to convert between Unicode UTF-8 and UTF-16.
//
// UTF-16 strings are stored in instances of CStringW.
// UTF-8 strings are stored in instances of CStringA.
//
// On error, the conversion routines use AtlThrow to signal the
// error condition.
//
// If input string pointers are NULL, empty strings are returned.
//
//
// Prefixes used in these routines:
// --------------------------------
//
//  - cch  : count of characters (CHAR's or WCHAR's)
//  - cb   : count of bytes
//  - psz  : pointer to a NUL-terminated string (CHAR* or WCHAR*)
//  - str  : instance of CString(A/W) class
//
//
//
// Useful Web References:
// ----------------------
//
// WideCharToMultiByte Function
// http://msdn.microsoft.com/en-us/library/dd374130.aspx
//
// MultiByteToWideChar Function
// http://msdn.microsoft.com/en-us/library/dd319072.aspx
//
// AtlThrow
// http://msdn.microsoft.com/en-us/library/z325eyx0.aspx
//
//
// Developed on VC9 (Visual Studio 2008 SP1)
//
//
//////////////////////////////////////////////////////////////////////////////



namespace UTF8Util
{
  //----------------------------------------------------------------------------
  // FUNCTION: ConvertUTF8ToUTF16
  // DESC: Converts Unicode UTF-8 text to Unicode UTF-16 (Windows default).
  //----------------------------------------------------------------------------
  CStdStringW ConvertUTF8ToUTF16(const char* pszTextUTF8)
  {
    //
    // Special case of NULL or empty input string
    //
    if ( (pszTextUTF8 == NULL) || (*pszTextUTF8 == '\0') )
    {
      // Return empty string
      return L"";
    }

    //
    // Consider CHAR's count corresponding to total input string length,
    // including end-of-string (\0) character
    //
    const size_t cchUTF8Max = INT_MAX - 1;
    size_t cchUTF8 = strlen(pszTextUTF8);

    // Consider also terminating \0
    ++cchUTF8;

    // Convert to 'int' for use with MultiByteToWideChar API
    int cbUTF8 = static_cast<int>( cchUTF8 );

    //
    // Get size of destination UTF-16 buffer, in WCHAR's
    //
    int cchUTF16 = ::MultiByteToWideChar(
      CP_UTF8,                // convert from UTF-8
      MB_ERR_INVALID_CHARS,   // error on invalid chars
      pszTextUTF8,            // source UTF-8 string
      cbUTF8,                 // total length of source UTF-8 string,
      // in CHAR's (= bytes), including end-of-string \0
      NULL,                   // unused - no conversion done in this step
      0                       // request size of destination buffer, in WCHAR's
      );

    if ( cchUTF16 == 0 )
    {
      DWORD dwErr = GetLastError();
      XBMC->Log(LOG_ERROR, "ConvertUTF8ToUTF16 failed lasterror == 0x%X.", dwErr);
      return L"";
    }


    //
    // Allocate destination buffer to store UTF-16 string
    //
    CStdStringW strUTF16;
    WCHAR * pszUTF16 = strUTF16.GetBuffer( cchUTF16 );

    //
    // Do the conversion from UTF-8 to UTF-16
    //
    int result = ::MultiByteToWideChar(
      CP_UTF8,                // convert from UTF-8
      MB_ERR_INVALID_CHARS,   // error on invalid chars
      pszTextUTF8,            // source UTF-8 string
      cbUTF8,                 // total length of source UTF-8 string,
      // in CHAR's (= bytes), including end-of-string \0
      pszUTF16,               // destination buffer
      cchUTF16                // size of destination buffer, in WCHAR's
      );

    if ( result == 0 )
    {
      DWORD dwErr = GetLastError();
      XBMC->Log(LOG_ERROR, "ConvertUTF8ToUTF16 failed lasterror == 0x%X.", dwErr);
      return L"";
    }

    // Release internal CString buffer
    strUTF16.ReleaseBuffer();

    // Return resulting UTF16 string
    return strUTF16;
  }


  //----------------------------------------------------------------------------
  // FUNCTION: ConvertUTF16ToUTF8
  // DESC: Converts Unicode UTF-16 (Windows default) text to Unicode UTF-8.
  //----------------------------------------------------------------------------
  CStdStringA ConvertUTF16ToUTF8(const WCHAR * pszTextUTF16)
  {
    //
    // Special case of NULL or empty input string
    //
    if ( (pszTextUTF16 == NULL) || (*pszTextUTF16 == L'\0') )
    {
      // Return empty string
      return "";
    }

    //
    // Consider WCHAR's count corresponding to total input string length,
    // including end-of-string (L'\0') character.
    //
    const size_t cchUTF16Max = INT_MAX - 1;
    size_t cchUTF16 = wcslen(pszTextUTF16);

    // Consider also terminating \0
    ++cchUTF16;

    //
    // WC_ERR_INVALID_CHARS flag is set to fail if invalid input character
    // is encountered.
    // This flag is supported on Windows Vista and later.
    // Don't use it on Windows XP and previous.
    //
#if (WINVER >= 0x0600)
    DWORD dwConversionFlags = WC_ERR_INVALID_CHARS;
#else
    DWORD dwConversionFlags = 0;
#endif

    //
    // Get size of destination UTF-8 buffer, in CHAR's (= bytes)
    //
    int cbUTF8 = ::WideCharToMultiByte(
      CP_UTF8,                // convert to UTF-8
      dwConversionFlags,      // specify conversion behavior
      pszTextUTF16,           // source UTF-16 string
      static_cast<int>( cchUTF16 ),   // total source string length, in WCHAR's,
      // including end-of-string \0
      NULL,                   // unused - no conversion required in this step
      0,                      // request buffer size
      NULL, NULL              // unused
      );

    if ( cbUTF8 == 0 )
    {
      DWORD dwErr = GetLastError();
      XBMC->Log(LOG_ERROR, "ConvertUTF16ToUTF8 failed lasterror == 0x%X.", dwErr);
      return L"";
    }

    //
    // Allocate destination buffer for UTF-8 string
    //
    CStdStringA strUTF8;
    int cchUTF8 = cbUTF8; // sizeof(CHAR) = 1 byte
    CHAR * pszUTF8 = strUTF8.GetBuffer( cchUTF8 );

    //
    // Do the conversion from UTF-16 to UTF-8
    //
    int result = ::WideCharToMultiByte(
      CP_UTF8,                // convert to UTF-8
      dwConversionFlags,      // specify conversion behavior
      pszTextUTF16,           // source UTF-16 string
      static_cast<int>( cchUTF16 ),   // total source string length, in WCHAR's,
      // including end-of-string \0
      pszUTF8,                // destination buffer
      cbUTF8,                 // destination buffer size, in bytes
      NULL, NULL              // unused
      ); 

    if ( result == 0 )
    {
      DWORD dwErr = GetLastError();
      XBMC->Log(LOG_ERROR, "ConvertUTF16ToUTF8 failed lasterror == 0x%X.", dwErr);
      return L"";
    }

    // Release internal CString buffer
    strUTF8.ReleaseBuffer();

    // Return resulting UTF-8 string
    return strUTF8;
  }
} // namespace UTF8Util
#endif

//////////////////////////////////////////////////////////////////////////////
