#include "pch.h"
#include "System/Convert.h"
#include "System/Array.h"
#include "System/OverflowException.h"
#include "System/FormatException.h"
#include "System/ArgumentException.h"
#include <string>
#include <limits>
#include <algorithm>
#include <cwctype>
#include <cstring>
#include <vector>

namespace DotNetDupe {
    namespace System {

        template<typename Target, typename Source>
        static Target CheckRange(Source value) {
            if (value < (Source)(std::numeric_limits<Target>::min)() || value > (Source)(std::numeric_limits<Target>::max)()) {
                throw OverflowException("Value was either too large or too small for the target type.");
            }
            return (Target)value;
        }

        // Boolean
        bool Convert::ToBoolean(bool bValue) { return bValue; }
        bool Convert::ToBoolean(int iValue) { return iValue != 0; }
        bool Convert::ToBoolean(long long llValue) { return llValue != 0; }
        bool Convert::ToBoolean(double value) { return value != 0.0; }
        bool Convert::ToBoolean(const char* value) { return ToBoolean(String(value)); }
        bool Convert::ToBoolean(const String& sValue) {
            std::string s = (const char*)sValue;
            std::transform(s.begin(), s.end(), s.begin(), [](char c) { return (char)std::towlower(c); });
            if (s == "true") return true;
            if (s == "false") return false;
            throw FormatException("String was not recognized as a valid Boolean.");
        }

        // Byte
        unsigned char Convert::ToByte(bool bValue) { return bValue ? 1 : 0; }
        unsigned char Convert::ToByte(unsigned char chValue) { return chValue; }
        unsigned char Convert::ToByte(const char* value) { return ToByte(String(value)); }
        unsigned char Convert::ToByte(const char* value, int fromBase) { return ToByte(String(value), fromBase); }
        unsigned char Convert::ToByte(signed char chValue) { return CheckRange<unsigned char>(chValue); }
        unsigned char Convert::ToByte(short iValue) { return CheckRange<unsigned char>(iValue); }
        unsigned char Convert::ToByte(int iValue) { return CheckRange<unsigned char>(iValue); }
        unsigned char Convert::ToByte(long long llValue) { return CheckRange<unsigned char>(llValue); }
        unsigned char Convert::ToByte(double value) { return CheckRange<unsigned char>(value); }
        unsigned char Convert::ToByte(const String& sValue) { return ToByte(sValue, 10); }
        unsigned char Convert::ToByte(const String& sValue, int iFromBase) {
            try {
                size_t nPos;
                long long llVal = std::stoll((const char*)sValue, &nPos, iFromBase);
                if (nPos != sValue.GetLength()) throw FormatException("Input string was not in a correct format.");
                return CheckRange<unsigned char>(llVal);
            } catch (const std::invalid_argument&) {
                throw FormatException("Input string was not in a correct format.");
            } catch (const std::out_of_range&) {
                throw OverflowException("Value was either too large or too small for an unsigned byte.");
            }
        }

        // SByte
        signed char Convert::ToSByte(bool bValue) { return bValue ? 1 : 0; }
        signed char Convert::ToSByte(unsigned char chValue) { return CheckRange<signed char>(chValue); }
        signed char Convert::ToSByte(const char* value) { return ToSByte(String(value)); }
        signed char Convert::ToSByte(signed char chValue) { return chValue; }
        signed char Convert::ToSByte(short iValue) { return CheckRange<signed char>(iValue); }
        signed char Convert::ToSByte(int iValue) { return CheckRange<signed char>(iValue); }
        signed char Convert::ToSByte(long long llValue) { return CheckRange<signed char>(llValue); }
        signed char Convert::ToSByte(double value) { return CheckRange<signed char>(value); }
        signed char Convert::ToSByte(const String& sValue) {
            try {
                size_t nPos;
                long long llVal = std::stoll((const char*)sValue, &nPos, 10);
                if (nPos != sValue.GetLength()) throw FormatException("Input string was not in a correct format.");
                return CheckRange<signed char>(llVal);
            } catch (const std::invalid_argument&) {
                throw FormatException("Input string was not in a correct format.");
            } catch (const std::out_of_range&) {
                throw OverflowException("Value was either too large or too small for a signed byte.");
            }
        }

        // Char
        char Convert::ToChar(unsigned short iValue) { return (char)iValue; }
        char Convert::ToChar(int iValue) { return CheckRange<char>(iValue); }
        char Convert::ToChar(long long llValue) { return CheckRange<char>(llValue); }
        char Convert::ToChar(const char* value) { return ToChar(String(value)); }
        char Convert::ToChar(const String& sValue) {
            if (sValue.GetLength() != 1) throw FormatException("String must be exactly one character long.");
            return sValue[0];
        }

        // Double
        double Convert::ToDouble(int iValue) { return (double)iValue; }
        double Convert::ToDouble(long long llValue) { return (double)llValue; }
        double Convert::ToDouble(float value) { return (double)value; }
        double Convert::ToDouble(const char* value) { return ToDouble(String(value)); }
        double Convert::ToDouble(const String& sValue) {
            try {
                size_t nPos;
                double val = std::stod((const char*)sValue, &nPos);
                if (nPos != sValue.GetLength()) throw FormatException("Input string was not in a correct format.");
                return val;
            } catch (const std::invalid_argument&) {
                throw FormatException("Input string was not in a correct format.");
            } catch (const std::out_of_range&) {
                throw OverflowException("Value was either too large or too small for a Double.");
            }
        }

        // Single
        float Convert::ToSingle(int iValue) { return (float)iValue; }
        float Convert::ToSingle(long long llValue) { return (float)llValue; }
        float Convert::ToSingle(double value) { return (float)value; }
        float Convert::ToSingle(const char* value) { return ToSingle(String(value)); }
        float Convert::ToSingle(const String& sValue) {
            try {
                size_t nPos;
                float val = std::stof((const char*)sValue, &nPos);
                if (nPos != sValue.GetLength()) throw FormatException("Input string was not in a correct format.");
                return val;
            } catch (const std::invalid_argument&) {
                throw FormatException("Input string was not in a correct format.");
            } catch (const std::out_of_range&) {
                throw OverflowException("Value was either too large or too small for a Single.");
            }
        }

        // Int16
        short Convert::ToInt16(bool bValue) { return bValue ? 1 : 0; }
        short Convert::ToInt16(short iValue) { return iValue; }
        short Convert::ToInt16(int iValue) { return CheckRange<short>(iValue); }
        short Convert::ToInt16(long long llValue) { return CheckRange<short>(llValue); }
        short Convert::ToInt16(double value) { return CheckRange<short>(value); }
        short Convert::ToInt16(const char* value) { return ToInt16(String(value)); }
        short Convert::ToInt16(const char* value, int fromBase) { return ToInt16(String(value), fromBase); }
        short Convert::ToInt16(const String& sValue) { return ToInt16(sValue, 10); }
        short Convert::ToInt16(const String& sValue, int iFromBase) {
            try {
                size_t nPos;
                long long llVal = std::stoll((const char*)sValue, &nPos, iFromBase);
                if (nPos != sValue.GetLength()) throw FormatException("Input string was not in a correct format.");
                return CheckRange<short>(llVal);
            } catch (const std::invalid_argument&) {
                throw FormatException("Input string was not in a correct format.");
            } catch (const std::out_of_range&) {
                throw OverflowException("Value was either too large or too small for an Int16.");
            }
        }

        // Int32
        int Convert::ToInt32(bool bValue) { return bValue ? 1 : 0; }
        int Convert::ToInt32(int iValue) { return iValue; }
        int Convert::ToInt32(long long llValue) { return CheckRange<int>(llValue); }
        int Convert::ToInt32(double value) { return CheckRange<int>(value); }
        int Convert::ToInt32(const char* value) { return ToInt32(String(value)); }
        int Convert::ToInt32(const char* value, int fromBase) { return ToInt32(String(value), fromBase); }
        int Convert::ToInt32(const String& sValue) { return ToInt32(sValue, 10); }
        int Convert::ToInt32(const String& sValue, int iFromBase) {
            try {
                size_t nPos;
                long long llVal = std::stoll((const char*)sValue, &nPos, iFromBase);
                if (nPos != sValue.GetLength()) throw FormatException("Input string was not in a correct format.");
                return CheckRange<int>(llVal);
            } catch (const std::invalid_argument&) {
                throw FormatException("Input string was not in a correct format.");
            } catch (const std::out_of_range&) {
                throw OverflowException("Value was either too large or too small for an Int32.");
            }
        }

        // Int64
        long long Convert::ToInt64(bool bValue) { return bValue ? 1 : 0; }
        long long Convert::ToInt64(int iValue) { return (long long)iValue; }
        long long Convert::ToInt64(long long llValue) { return llValue; }
        long long Convert::ToInt64(double value) { return (long long)value; }
        long long Convert::ToInt64(const char* value) { return ToInt64(String(value)); }
        long long Convert::ToInt64(const char* value, int fromBase) { return ToInt64(String(value), fromBase); }
        long long Convert::ToInt64(const String& sValue) { return ToInt64(sValue, 10); }
        long long Convert::ToInt64(const String& sValue, int iFromBase) {
            try {
                size_t nPos;
                long long llVal = std::stoll((const char*)sValue, &nPos, iFromBase);
                if (nPos != sValue.GetLength()) throw FormatException("Input string was not in a correct format.");
                return llVal;
            } catch (const std::invalid_argument&) {
                throw FormatException("Input string was not in a correct format.");
            } catch (const std::out_of_range&) {
                throw OverflowException("Value was either too large or too small for an Int64.");
            }
        }

        // UInt16
        unsigned short Convert::ToUInt16(bool bValue) { return bValue ? 1 : 0; }
        unsigned short Convert::ToUInt16(int iValue) { return CheckRange<unsigned short>(iValue); }
        unsigned short Convert::ToUInt16(long long llValue) { return CheckRange<unsigned short>(llValue); }
        unsigned short Convert::ToUInt16(double value) { return CheckRange<unsigned short>(value); }
        unsigned short Convert::ToUInt16(const char* value) { return ToUInt16(String(value)); }
        unsigned short Convert::ToUInt16(const String& sValue) {
            try {
                size_t nPos;
                unsigned long long llVal = std::stoull((const char*)sValue, &nPos, 10);
                if (nPos != sValue.GetLength()) throw FormatException("Input string was not in a correct format.");
                return CheckRange<unsigned short>(llVal);
            } catch (const std::invalid_argument&) {
                throw FormatException("Input string was not in a correct format.");
            } catch (const std::out_of_range&) {
                throw OverflowException("Value was either too large or too small for a UInt16.");
            }
        }

        // UInt32
        unsigned int Convert::ToUInt32(bool bValue) { return bValue ? 1 : 0; }
        unsigned int Convert::ToUInt32(int iValue) { return CheckRange<unsigned int>(iValue); }
        unsigned int Convert::ToUInt32(long long llValue) { return CheckRange<unsigned int>(llValue); }
        unsigned int Convert::ToUInt32(double value) { return CheckRange<unsigned int>(value); }
        unsigned int Convert::ToUInt32(const char* value) { return ToUInt32(String(value)); }
        unsigned int Convert::ToUInt32(const String& sValue) {
            try {
                size_t nPos;
                unsigned long long llVal = std::stoull((const char*)sValue, &nPos, 10);
                if (nPos != sValue.GetLength()) throw FormatException("Input string was not in a correct format.");
                return CheckRange<unsigned int>(llVal);
            } catch (const std::invalid_argument&) {
                throw FormatException("Input string was not in a correct format.");
            } catch (const std::out_of_range&) {
                throw OverflowException("Value was either too large or too small for a UInt32.");
            }
        }

        // UInt64
        unsigned long long Convert::ToUInt64(bool bValue) { return bValue ? 1 : 0; }
        unsigned long long Convert::ToUInt64(int iValue) { return CheckRange<unsigned long long>(iValue); }
        unsigned long long Convert::ToUInt64(long long llValue) { return CheckRange<unsigned long long>(llValue); }
        unsigned long long Convert::ToUInt64(double value) { return (unsigned long long)value; }
        unsigned long long Convert::ToUInt64(const char* value) { return ToUInt64(String(value)); }
        unsigned long long Convert::ToUInt64(const String& sValue) {
            try {
                size_t nPos;
                unsigned long long llVal = std::stoull((const char*)sValue, &nPos, 10);
                if (nPos != sValue.GetLength()) throw FormatException("Input string was not in a correct format.");
                return llVal;
            } catch (const std::invalid_argument&) {
                throw FormatException("Input string was not in a correct format.");
            } catch (const std::out_of_range&) {
                throw OverflowException("Value was either too large or too small for a UInt64.");
            }
        }

        // String
        String Convert::ToString(bool bValue) { return bValue ? "True" : "False"; }
        String Convert::ToString(unsigned char chValue) { return String(std::to_string(chValue).c_str()); }
        String Convert::ToString(signed char chValue) { return String(std::to_string(chValue).c_str()); }
        String Convert::ToString(char chValue) { char buf[2] = { chValue, 0 }; return String(buf); }
        String Convert::ToString(double value) {
            char buf[64];
            snprintf(buf, sizeof(buf), "%g", value);
            return String(buf);
        }
        String Convert::ToString(float value) {
            char buf[64];
            snprintf(buf, sizeof(buf), "%g", static_cast<double>(value));
            return String(buf);
        }
        String Convert::ToString(short iValue) { return String(std::to_string(iValue).c_str()); }
        String Convert::ToString(int iValue) { return String(std::to_string(iValue).c_str()); }
        String Convert::ToString(long long llValue) { return String(std::to_string(llValue).c_str()); }
        String Convert::ToString(unsigned short iValue) { return String(std::to_string(iValue).c_str()); }
        String Convert::ToString(unsigned int iValue) { return String(std::to_string(iValue).c_str()); }
        String Convert::ToString(unsigned long long llValue) { return String(std::to_string(llValue).c_str()); }

        static String ToBaseString(unsigned long long llValue, int iToBase) {
            if (iToBase != 2 && iToBase != 8 && iToBase != 10 && iToBase != 16)
                throw ArgumentException("The base must be 2, 8, 10, or 16.");

            if (llValue == 0) return "0";

            std::string res;
            const char* digits = "0123456789ABCDEF";
            while (llValue > 0) {
                res += digits[llValue % iToBase];
                llValue /= iToBase;
            }
            std::reverse(res.begin(), res.end());
            return String(res.c_str());
        }

        String Convert::ToString(int iValue, int iToBase) {
            return ToBaseString((unsigned int)iValue, iToBase);
        }
        String Convert::ToString(long long llValue, int iToBase) {
            return ToBaseString((unsigned long long)llValue, iToBase);
        }

        static const char base64_chars[] = 
                     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                     "abcdefghijklmnopqrstuvwxyz"
                     "0123456789+/";

        static bool IsBase64(unsigned char c) {
            return (isalnum(c) || (c == '+') || (c == '/'));
        }

        static void EncodeBase64Chunk4(const unsigned char* char_array_3, std::string& ret) {
            unsigned char char_array_4[4];
            char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
            char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
            char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
            char_array_4[3] = char_array_3[2] & 0x3f;
            for (int i = 0; i < 4; i++) ret += base64_chars[char_array_4[i]];
        }

        static void EncodeBase64Remainder(unsigned char* char_array_3, int remainderLen, std::string& ret) {
            unsigned char char_array_4[4];
            for (int j = remainderLen; j < 3; ++j) char_array_3[j] = '\0';
            char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
            char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
            char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
            for (int j = 0; j < remainderLen + 1; ++j) ret += base64_chars[char_array_4[j]];
            for (int pad = remainderLen; pad < 3; ++pad) ret += '=';
        }

        String Convert::ToBase64String(const Array<char>& inArray) {
            int in_len = inArray.GetLength();
            if (in_len == 0) return String("");
            const unsigned char* bytes = reinterpret_cast<const unsigned char*>(inArray.GetData());
            std::string ret;
            int i = 0;
            unsigned char char_array_3[3];
            while (in_len--) {
                char_array_3[i++] = *(bytes++);
                if (i == 3) { EncodeBase64Chunk4(char_array_3, ret); i = 0; }
            }
            if (i) EncodeBase64Remainder(char_array_3, i, ret);
            return String(ret.c_str());
        }

        static void DecodeBase64Chunk4(unsigned char* char_array_4, std::vector<char>& decoded) {
            for (int k = 0; k < 4; k++) {
                const char* ptr = std::strchr(base64_chars, char_array_4[k]);
                if (ptr == nullptr) throw ArgumentException("Invalid base64 character");
                char_array_4[k] = static_cast<unsigned char>(ptr - base64_chars);
            }
            decoded.push_back((char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4));
            decoded.push_back(((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2));
            decoded.push_back(((char_array_4[2] & 0x3) << 6) + char_array_4[3]);
        }

        static void DecodeBase64Remainder(unsigned char* char_array_4, int i, std::vector<char>& decoded) {
            for (int j = 0; j < i; j++) {
                const char* ptr = std::strchr(base64_chars, char_array_4[j]);
                if (ptr == nullptr) throw ArgumentException("Invalid base64 character");
                char_array_4[j] = static_cast<unsigned char>(ptr - base64_chars);
            }
            unsigned char char_array_3[2];
            char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
            if (i > 2) {
                char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
                decoded.push_back(char_array_3[0]);
                decoded.push_back(char_array_3[1]);
            } else if (i > 1) {
                decoded.push_back(char_array_3[0]);
            }
        }

        static void DecodeBase64Loop(const std::string& sInput, std::vector<char>& decoded) {
            size_t in_len = sInput.size();
            int i = 0, in_ = 0;
            unsigned char char_array_4[4];
            while (in_len-- && (sInput[in_] != '=') && IsBase64(sInput[in_])) {
                char_array_4[i++] = sInput[in_++];
                if (i == 4) {
                    DecodeBase64Chunk4(char_array_4, decoded);
                    i = 0;
                }
            }
            if (i) DecodeBase64Remainder(char_array_4, i, decoded);
        }

        Array<char> Convert::FromBase64String(const char* s) {
            return FromBase64String(String(s));
        }

        Array<char> Convert::FromBase64String(const String& s) {
            std::string sInput(s.GetRawString() ? s.GetRawString() : "");
            std::vector<char> decoded;
            DecodeBase64Loop(sInput, decoded);
            Array<char> result(static_cast<int>(decoded.size()));
            for (size_t idx = 0; idx < decoded.size(); idx++) {
                result[static_cast<int>(idx)] = decoded[idx];
            }
            return result;
        }
    }
}

Generated by OpenCppCoverage (Version: 0.9.9.0)