#include "TessesFramework/Http/HttpUtils.hpp"
#include "TessesFramework/Filesystem/VFS.hpp"
#include <iostream>
#include <algorithm>
#include <sstream>
using VFSPath = Tesses::Framework::Filesystem::VFSPath;
namespace Tesses::Framework::Http {
    
    bool Uri::Relative(std::string url, Uri& uri)
    {
        auto index = url.find_first_of("//");
        if(index != std::string::npos)
        {
            if(Uri::TryParse(url,uri))
            {
                if(index == 0)
                    uri.scheme = this->scheme;
                return true;
            }
        }
        else if(!url.empty())
        {
            if(url[0] == '/')
            {
                
                auto thirdPart = HttpUtils::SplitString(url,"#",2);
                if(thirdPart.empty()) return false;
                if(thirdPart.size() == 2)
                {
                    uri.hash=thirdPart[1];
                }


                auto fourthPart = HttpUtils::SplitString(thirdPart[1],"?",2);
                
                VFSPath p = fourthPart[0];
                uri.path = p.CollapseRelativeParents().ToString(); //this should be safe
                if(fourthPart.size() == 2)
                {
                    HttpUtils::QueryParamsDecode(uri.query, fourthPart[1]);   
                }
            }
            else
            {
                auto thirdPart = HttpUtils::SplitString(url,"#",2);
                if(thirdPart.empty()) return false;
                if(thirdPart.size() == 2)
                {
                    uri.hash=thirdPart[1];
                }


                auto fourthPart = HttpUtils::SplitString(thirdPart[1],"?",2);
                
                VFSPath p = VFSPath(this->path,fourthPart[0]);
                uri.path = p.CollapseRelativeParents().ToString(); //this should be safe
                if(fourthPart.size() == 2)
                {
                    HttpUtils::QueryParamsDecode(uri.query, fourthPart[1]);   
                }
            }
            uri.scheme = this->scheme;
            uri.host = this->host;
            uri.port = this->port;
            return true;
        }
        return false;
    }
    std::string Uri::HostPort()
    {
        if(this->port != 0) return this->host + ":" + std::to_string(this->port);
        return this->host;
    }
    uint16_t Uri::GetPort()
    {
        if(this->port != 0) return this->port;

        if(this->scheme == "http:" || this->scheme == "ws:")
            return 80;
        if(this->scheme == "https:" || this->scheme == "wss:")
            return 443;
        if(this->scheme == "sftp:")
            return 22;
        if(this->scheme == "ftp:")
            return 21;
        if(this->scheme == "tftp:")
            return 69;
        return 0;
    }
    bool Uri::TryParse(std::string url, Uri& uri)
    {
        uri.scheme = "";
        uri.port=0;
        auto firstPart = HttpUtils::SplitString(url,"//",2);
        if(firstPart.size() == 2)
       
        uri.scheme=firstPart[0];
        else if(firstPart.empty())
        return false;

            auto secondPart = HttpUtils::SplitString(firstPart.size() == 2 ? firstPart[1] : firstPart[0] ,"/",2);

            if(secondPart.size() == 1)
            {
                uri.path = "/";
            }
            else if(secondPart.size() == 2)
            {
                auto thirdPart = HttpUtils::SplitString(secondPart[1],"#",2);
                if(thirdPart.empty()) return false;
                if(thirdPart.size() == 2)
                {
                    uri.hash=thirdPart[1];
                }


                auto fourthPart = HttpUtils::SplitString(thirdPart[0],"?",2);
                uri.path = "/" + fourthPart[0]; //this should be safe
                if(fourthPart.size() == 2)
                {
                    HttpUtils::QueryParamsDecode(uri.query, fourthPart[1]);   
                }
            }
            else
            {
                return false;
            }
            
            auto hostPortPart = HttpUtils::SplitString(secondPart[0],":",2);

            if(hostPortPart.empty()) return false;
            if(hostPortPart.size() == 2)
            {
                uri.port = (uint16_t)std::stoul(hostPortPart[1]);
            }
            uri.host = hostPortPart[0];
        
        return true;
    }
    Uri::Uri()
    {
        this->query.SetCaseSensitive(true);
    }
    std::string Uri::GetPathAndQuery()
    {
        return this->path + this->GetQuery();
    }
    std::string Uri::GetQuery()
    {
        if(this->query.kvp.empty()) return "";
        std::string queryStr = "?";
        queryStr.append(HttpUtils::QueryParamsEncode(query));
        return queryStr;
    }

    std::string Uri::ToString()
    {
        std::string uri = this->scheme;
        uri.append("//");
        uri.append(this->host);
        if(this->port > 0) 
        {
            uri.push_back(':');
            uri.append(std::to_string(this->port));
        }
        uri.append(this->GetPathAndQuery());
        return uri;
    }
    std::string HttpUtils::Replace(std::string text, std::string find, std::string replace)
    {
        std::string dest;
        while(text.length() > 0)
        {
           std::size_t index= text.find(find);

           

           if(index == std::string::npos)
           {
                dest.append(text);
                break;
           }
           else
           {
                std::string left = text.substr(0,index);

                text = text.substr(index+find.size());
                dest.append(left);
                dest.append(replace);

           }
        }
        return dest;
    }
    std::string HttpUtils::LeftPad(std::string text, int count, char c)
    {
        std::stringstream strm(std::ios_base::out);
        strm << std::setfill(c) << std::setw(count) << text;
        return strm.str();
    }
    char HttpUtils::NibbleToHex(uint8_t b, bool isUppercase)
    {
        if(isUppercase)
        {
            b %= 16;
            if(b >= 0 && b <= 9)
                return b + '0';
            if(b >= 10 && b <= 15)
                return b + ('A' - 10);
            return 0;
        }
        return NibbleToHex(b);
    }
    char HttpUtils::NibbleToHex(uint8_t b)
    {
        b %= 16;
        if(b >= 0 && b <= 9)
            return b + '0';
        if(b >= 10 && b <= 15)
            return b + ('a' - 10);
        return 0;
    }
    uint8_t HttpUtils::HexToNibble(char c)
    {
        if(c >= '0' && c <= '9')
            return (uint8_t)(c - '0');
        
        if(c >= 'A' && c <= 'F')
            return (uint8_t)(c - 55);
        
        if(c >= 'a' && c <= 'f')
            return (uint8_t)(c - 87);

        return 0;
    }

    std::string HttpUtils::MimeType(std::filesystem::path p)
    {
        std::string ext = p.extension().string();
        if(ext == ".html" || ext == ".htm")
        {
            return "text/html";
        }
        if(ext == ".txt" || ext == ".log" || ext == ".twss")
        {
            return "text/plain";
        }
        if(ext == ".woff")
        {
            return "application/x-font-woff";
        }
        if(ext == ".vtt")
        {
            return "text/vtt";
        }
        if(ext == ".svg")
        {
            return "image/svg+xml";
        }
        if(ext == ".webp")
        {
            return "image/webp";
        }
        if(ext == ".vcf")
        {
            return "text/v-card";
        }
        if(ext == ".rss" || ext == ".xml" || ext == ".atom" || ext == ".rdf")
        {
            return "application/xml";
        }
        if(ext == ".js")
        {
            return "text/javascript";
        }
        if(ext == ".json")
        {
            return "application/json";
        }
        if(ext == ".wasm")
        {
            return "application/wasm";
        }
        if(ext == ".png")
        {
            return "image/png";
        }
        if(ext == ".jpg" || ext == ".jpeg")
        {
            return "image/jpeg";
        }
        if(ext == ".css")
        {
            return "text/css";
        }
        if(ext == ".gif")
        {
            return "image/gif";
        }
        if(ext == ".mp4")
        {
            return "video/mp4";
        }
        if(ext == ".mov")
        {
            return "video/quicktime";
        }
        if(ext == ".m4a")
        {
            return "audio/mp4";
        }
        if(ext == ".webm")
        {
            return "video/webm";
        }
        if(ext == ".webmanifest")
        {
            return "application/manifest+json";
        }
        if(ext == ".ico")
        {
            return "image/x-icon";
        }

        return "application/octet-stream";
    }
    bool HttpUtils::Invalid(char c)
    {
        //just do windows because it is the strictist when it comes to windows, mac and linux
        if(c >= 0 && c < 32) return true;
        if(c == 127) return true;
        if(c == '\\') return true;
        if(c == '*') return true;
        if(c == '/') return true;
        if(c == '|') return true;
        if(c == ':') return true;
        if(c == '<') return true;
        if(c == '>') return true;
        if(c == '\"') return true;
        if(c == '?') return true;
        return false;
    }
    std::string HttpUtils::Sanitise(std::string text)
    {
        std::string myStr={};
        for(auto item : text)
        {
            if(Invalid(item)) continue;
            myStr.push_back(item);
        }
        return myStr;
    }

    void HttpUtils::QueryParamsDecode(HttpDictionary& dict,std::string query)
    {
        for(auto item : SplitString(query,"&"))
        {
            std::vector<std::string> ss=SplitString(item,"=",2);
            if(ss.size() >= 1)
            {
                std::string value = {};
                if(ss.size() == 2)
                {
                    value = UrlDecode(ss[1]);
                }
                dict.AddValue(UrlDecode(ss[0]),value);
            }
        }
    }    
    std::string HttpUtils::Join(std::string joinStr, std::vector<std::string> ents)
    {
        std::string str={};
        bool first=true;

        for(auto item : ents)
        {
            if(!first) str.append(joinStr);
            str.append(item);
            first=false;
        }
        return str;
    }
    std::string HttpUtils::QueryParamsEncode(HttpDictionary& dict)
    {
        std::string s={};
        bool first = true;
        for(auto item : dict.kvp)
        {
            for(auto item2 : item.second)
            {
                if(!first)
                {
                    s.push_back('&');
                }
                s.insert(s.size(),UrlEncode(item.first));
                s.push_back('=');
                s.insert(s.size(),UrlEncode(item2));
                first=false;
            }
        }
        return s;
    }

    std::string HttpUtils::UrlDecode(std::string v)
    {   
        std::string s = {};

        for(size_t i = 0;i<v.size();i++)
        {
            if(v[i] == '+')
                s.push_back(' ');
            else if(v[i] == '%')
            {
                i++;
                uint8_t n = HexToNibble(v[i])<<4;
                i++;
                n |= HexToNibble(v[i]);
                s.push_back((char)n);
            }
            else
                s.push_back(v[i]);

        }
        return s;
    }
    std::string HttpUtils::UrlPathEncode(std::string v,bool ignoreSpace)
    {
        std::string s = {};

        for(auto item : v)
        {
            if(item >= 'A' && item <= 'Z')
                s.push_back(item);
            else if(item >= 'a' && item <= 'z')
                s.push_back(item);
            else if(item >= '0' && item <= '9')
                s.push_back(item);
            else if(item == '-' || item == '_' || item == '.' || item == '~' || item == '/')
                s.push_back(item);
            else
            {
                if(item != ' ' || !ignoreSpace)
                {
                    s.push_back('%');
                    s.push_back(NibbleToHex((item >> 4) & 0xF));
                    s.push_back(NibbleToHex((item) & 0xF));
                }
                else
                {
                    s.push_back(' ');
                }
            }
        }
        return s;
    }
    std::string HttpUtils::UrlPathDecode(std::string v)
    {
        std::string s = {};

        for(size_t i = 0;i<v.size();i++)
        {
            if(v[i] == '%')
            {
                i++;
                uint8_t n = HexToNibble(v[i])<<4;
                i++;
                n |= HexToNibble(v[i]);
                s.push_back((char)n);
            }
            else
                s.push_back(v[i]);

        }
        return s;
    }

    std::string HttpUtils::UrlEncode(std::string v)
    {
        std::string s = {};

        for(auto item : v)
        {
            if(item == ' ')
                s.push_back('+');
            else if(item >= 'A' && item <= 'Z')
                s.push_back(item);
            else if(item >= 'a' && item <= 'z')
                s.push_back(item);
            else if(item >= '0' && item <= '9')
                s.push_back(item);
            else if(item == '-' || item == '_' || item == '.' || item == '~')
                s.push_back(item);
            else
            {
                s.push_back('%');
                s.push_back(NibbleToHex((item >> 4) & 0xF));
                s.push_back(NibbleToHex((item) & 0xF));
            }
        }
        return s;
    }

    std::vector<std::string> HttpUtils::SplitString(std::string text, std::string delimiter,std::size_t maxCnt)
    {
        std::vector<std::string> strs;
        std::size_t i = 1;
        while(text.length() > 0)
        {
            if(i == maxCnt)
           {
                strs.push_back(text);
                break;    
           }
           std::size_t index= text.find(delimiter);

           

           if(index == std::string::npos)
           {
                strs.push_back(text);
                break;
           }
           else
           {
                std::string left = text.substr(0,index);

                text = text.substr(index+delimiter.size());

                strs.push_back(left);

           }
           i++;
        }
        return strs;
    }
    std::string HttpUtils::HtmlDecodeOnlyEntityNumber(std::string v)
    {
        std::string buff={};
        int state = 0;
        uint64_t n=0;
        for(auto item : v)
        {
            switch(state)
            {
                case 0:
                    if(item == '&') state=1;
                    else buff.push_back(item);
                    break;
                case 1:
                    if(item == '#') {state = 2; n=0;}
                    else {state=0; buff.push_back('&'); buff.push_back(item); }
                    break;
                case 2:
                    if(item == ';') {
                        state = 0;
                        if(n <= 0x7F)
                        {
                            buff.push_back((char)n);
                        }
                        else if(n >= 0x80 && n <= 0x7FF)
                        {
                            uint8_t high = 0b11000000 | ((uint8_t)(n >> 6) & 0b00011111);
                            uint8_t low = 0b10000000 | ((uint8_t)(n) & 0b00111111);
                            buff.push_back((char)high);
                            buff.push_back((char)low);
                        }
                        else if(n >= 0x800 && n <= 0xFFFF)
                        {
                            uint8_t high = 0b11100000 | ((uint8_t)(n >> 12) & 0b00001111);
                            uint8_t low = 0b10000000 | ((uint8_t)(n >> 6) & 0b00111111);
                            uint8_t lowest = 0b10000000 | ((uint8_t)(n) & 0b00111111);
                            buff.push_back((char)high);
                            buff.push_back((char)low);
                            buff.push_back((char)lowest);
                        }
                        else if(n >= 0x010000 && n <= 0x10FFFF)
                        {
                            uint8_t highest = 0b11110000 | ((uint8_t)(n >> 18) & 0b00000111);
                            uint8_t high = 0b10000000 | ((uint8_t)(n >> 12) & 0b00111111);
                            uint8_t low = 0b10000000 | ((uint8_t)(n >> 6) & 0b00111111);
                            uint8_t lowest = 0b10000000 | ((uint8_t)(n) & 0b00111111);
                            buff.push_back((char)highest);
                            
                            buff.push_back((char)high);
                            buff.push_back((char)low);
                            buff.push_back((char)lowest);
                        }
                    }
                    else if(item >= '0' && item <= '9')
                    {
                        n *= 10;
                        n += item - '0';
                    }
            }
        }
        return buff;
    }
    std::string HttpUtils::HtmlP(std::string text)
    {
        std::string newText = "";
        std::string builder = "";

        auto flush = [&]()->void {
            if(!builder.empty())
            {
                if(builder.find("http://") == 0 || builder.find("https://") == 0 || builder.find("ftp://") == 0 || builder.find("ftps://") == 0 || builder.find("magnet:") == 0 || builder.find("btmh:") == 0)
                {
                    newText += "<a href=\"" + HttpUtils::HtmlEncode(builder) + "\">" + HttpUtils::HtmlEncode(builder) + "</a>";    
                }
                else if(builder.find("mailto:") == 0)
                {
                    newText += "<a href=\"" + HttpUtils::HtmlEncode(builder) + "\">" + HttpUtils::HtmlEncode(builder.substr(7)) + "</a>";    
                }
                else if(builder.find("tel:") == 0)
                {
                    newText += "<a href=\"" + HttpUtils::HtmlEncode(builder) + "\">" + HttpUtils::HtmlEncode(builder.substr(4)) + "</a>";    
                }
                else {
                    newText += HttpUtils::HtmlEncode(builder);
                }
                builder = "";
            }
        };


        for(auto item : text)
        {
            switch(item)
            {
                case ' ':
                    flush();
                    newText += " ";
                    break;
                case '\n':
                    flush();
                    newText += "<br>";
                    break;
                case '\t':
                    flush();
                    newText += "&tab;";
                    break;
                case '\r':
                    flush();
                    break;
                default:
                    builder += item;
                    break;
            }
        }
        flush();

        return newText;
    }
    std::string HttpUtils::HtmlEncode(std::string html)
    {
        std::string myHtml = {};
        for(auto item : html)
        {
            if(item == '\"')
            {
                myHtml.append("&quot;");
            }
            else if(item == '\'')
            {
                myHtml.append("&apos;");
            }
            else if(item == '&')
            {
                myHtml.append("&amp;");
            }
            else if(item == '<')
            {
                myHtml.append("&lt;");
            }
            else if(item == '>')
            {
                myHtml.append("&gt;");
            }
            else
            {
                myHtml.push_back(item);
            }
        }
        return myHtml;
    }
    std::string HttpUtils::StatusCodeString(StatusCode code)
    {
        switch(code)
        {
            case StatusCode::Continue:
                return "Continue";
            case StatusCode::SwitchingProtocols:
                return "Switching Protocols";
            case StatusCode::Processing:
                return "Processing";
            case StatusCode::EarlyHints:
                return "Early Hints";
            case StatusCode::OK:
                return "OK";
            case StatusCode::Created:
                return "Created";
            case StatusCode::Accepted:
                return "Accepted";
            case StatusCode::NonAuthoritativeInformation:
                return "Non-Authoritative Information";
            case StatusCode::NoContent:
                return "No Content";
            case StatusCode::ResetContent:
                return "Reset Content";
            case StatusCode::PartialContent:
                return "PartialContent";
            case StatusCode::MultiStatus:
                return "Multi-Status";
            case StatusCode::AlreadyReported:
                return "Already Reported";
            case StatusCode::IMUsed:
                return "IM Used";
            case StatusCode::MultipleChoices:
                return "Multiple Choices";
            case StatusCode::MovedPermanently:
                return "Moved Permanently";
            case StatusCode::Found:
                return "Found";
            case StatusCode::SeeOther:
                return "See Other";
            case StatusCode::NotModified:
                return "Not Modified";
            case StatusCode::UseProxy:
                return "Use Proxy";
            case StatusCode::TemporaryRedirect:
                return "Temporary Redirect";
            case StatusCode::PermanentRedirect:
                return "Permanent Redirect";
            case StatusCode::BadRequest:
                return "Bad Request";
            case StatusCode::Unauthorized:
                return "Unauthorized";
            case StatusCode::PaymentRequired:
                return "Payment Required";
            case StatusCode::Forbidden:
                return "Forbidden";
            case StatusCode::NotFound:
                return "Not Found";
            case StatusCode::MethodNotAllowed:
                return "Method Not Allowed";
            case StatusCode::NotAcceptable:
                return "Not Acceptable";
            case StatusCode::ProxyAuthenticationRequired:
                return "Proxy Authentication Required";
            case StatusCode::RequestTimeout:
                return "Request Timeout";
            case StatusCode::Conflict:
                return "Conflict";
            case StatusCode::Gone:
                return "Gone";
            case StatusCode::LengthRequired:
                return "Length Required";
            case StatusCode::PreconditionFailed:
                return "Precondition Failed";
            case StatusCode::PayloadTooLarge:
                return "Payload Too Large";
            case StatusCode::URITooLong:
                return "URI Too Long";
            case StatusCode::UnsupportedMediaType:
                return "Unsupported Media Type";
            case StatusCode::RangeNotSatisfiable:
                return "Range Not Satisfiable";
            case StatusCode::ExpectationFailed:
                return "Expectation Failed";
            case StatusCode::ImATeapot:
                return "I'm a teapot";
            case StatusCode::MisdirectedRequest:
                return "Misdirected Request";
            case StatusCode::UnprocessableContent:
                return "Unprocessable Content";
            case StatusCode::Locked:
                return "Locked";
            case StatusCode::FailedDependency:
                return "Failed Dependency";
            case StatusCode::TooEarly:
                return "Too Early";
            case StatusCode::UpgradeRequired:
                return "Upgrade Required";
            case StatusCode::PreconditionRequired:
                return "Precondition Required";
            case StatusCode::TooManyRequests:
                return "Too Many Requests";
            case StatusCode::RequestHeaderFieldsTooLarge:
                return "Request Header Fields Too Large";
            case StatusCode::UnavailableForLegalReasons:
                return "Unavailable For Legal Reasons";
            case StatusCode::InternalServerError:
                return "Internal Server Error";
            case StatusCode::NotImplemented:
                return "Not Implemented";
            case StatusCode::ServiceUnavailable:
                return "Service Unavailable";
            case StatusCode::GatewayTimeout:
                return "Gateway Timeout";
            case StatusCode::HTTPVersionNotSupported:
                return "HTTP Version Not Supported";
            case StatusCode::VariantAlsoNegotiates:
                return "Variant Also Negotiates";
            case StatusCode::InsufficientStorage:
                return "Insufficient Storage";  
            case StatusCode::LoopDetected:
                return "Loop Detected";
            case StatusCode::NotExtended:
                return "Not Extended";
            case StatusCode::NetworkAuthenticationRequired:
                return "Network Authentication Required";
            default:
                return "";
        }
    }
    CaseInsensitiveLess::CaseInsensitiveLess(const CaseInsensitiveLess& str)
    {
        this->caseSensitive = str.caseSensitive;
        this->offset = this;
    }
    CaseInsensitiveLess::CaseInsensitiveLess()
    {
        this->caseSensitive=false;
        this->offset = this;
    }
    void HttpDictionary::SetCaseSensitive(bool isCaseSensitive)
    {
        this->kvp.key_comp().offset->caseSensitive=isCaseSensitive;
    }
    HttpDictionary::HttpDictionary(bool isCaseSensitive)
    {
        this->SetCaseSensitive(isCaseSensitive);
    }
    HttpDictionary::HttpDictionary() : HttpDictionary(false)
    {

    }
    
    bool HttpDictionary::AnyEquals(std::string key, std::string value)
    {
        if(this->kvp.count(key) > 0)
            for(auto v : this->kvp[key]) if(v == value) return true;
        // for(auto v : item.second) if(v == value) return true;
        return false;
    }

    void HttpDictionary::Clear()
    {
        kvp.clear();
    }
    void HttpDictionary::Clear(std::string key, bool kvpExistsAfter)
    {
        if(kvpExistsAfter)
        {
            kvp[key].clear();
        }
        else
        {
            if(kvp.count(key) == 0) return;
            kvp[key].clear();
            kvp.erase(key);
        }
    }
    void HttpDictionary::SetValue(std::string key, std::string value)
    {
        kvp[key] = {value};
    }
    void HttpDictionary::SetValue(std::string key, int64_t value)
    {
        kvp[key] = {std::to_string(value)};
    }
    void HttpDictionary::SetValue(std::string key, double value)
    {
        kvp[key] = {std::to_string(value)};
    }
    void HttpDictionary::SetValue(std::string key, Date::DateTime value)
    {
        kvp[key] = {value.ToHttpDate()};
    }
    void HttpDictionary::SetValue(std::string key, std::vector<std::string> value)
    {
        kvp[key] = value;
    }
    void HttpDictionary::AddValue(std::string key, std::string value)
    {
        kvp[key].push_back(value);
    }
    void HttpDictionary::AddValue(std::string key, int64_t value)
    {
        kvp[key].push_back(std::to_string(value));
    }

    void HttpDictionary::AddValue(std::string key, double value)
    {
        kvp[key].push_back(std::to_string(value));
    }

    void HttpDictionary::AddValue(std::string key, Date::DateTime value)
    {
        kvp[key].push_back(value.ToHttpDate());
    }
    void HttpDictionary::AddValue(std::string key, std::vector<std::string> value)
    {
        auto& ls = kvp[key];
        ls.insert(ls.end(), value.begin(), value.end());
    }
    bool HttpDictionary::TryGetFirst(std::string key, std::string& value)
    {
        if(kvp.count(key) == 0) return false;
        auto& ls = kvp[key];
        if(ls.empty()) return false;
        value = ls.front();

        return true;
    }

    bool HttpDictionary::TryGetFirstInt(std::string key, int64_t& value)
    {
        std::string val;
        if(!TryGetFirst(key,val)) return false;
        try{
            size_t off = 0;
            auto v = std::stoll(val,&off);
            if(off != val.size()) return false;
            value = v;
        } 
        catch(std::exception& ex)
        {
            return false;
        }
        return true;
    }
    bool HttpDictionary::TryGetFirstDate(std::string key, Date::DateTime& dt)
    {
        std::string val;
        if(!TryGetFirst(key,val)) return false;
        return Date::DateTime::TryParseHttpDate(val,dt);
    }
    bool HttpDictionary::TryGetFirstDouble(std::string key, double& value)
    {
        std::string val;
        if(!TryGetFirst(key,val)) return false;
        try{
            size_t off = 0;
            auto v = std::stod(val,&off);
            if(off != val.size()) return false;
            value = v;
        } 
        catch(std::exception& ex)
        {
            return false;
        }
        return true;
    }
     bool CaseInsensitiveLess::operator() (const std::string& s1, const std::string& s2) const {
        if(this->caseSensitive) return s1 == s2;
        return  HttpUtils::ToLower(s1) < HttpUtils::ToLower(s2);
        
    }

    std::string HttpUtils::ToLower(std::string str)
    {
        std::string str1(str.length(),' ');
        std::transform(str.begin(), str.end(), str1.begin(), tolower);
        return str1;
    }

    std::string HttpUtils::ToUpper(std::string str)
    {
        std::string str1(str.length(),' ');
        std::transform(str.begin(), str.end(), str1.begin(), toupper);
        return str1;
    }

    bool HttpDictionary::GetFirstBoolean(std::string key)
    {
        std::string val;
        if(!TryGetFirst(key,val)) return false;
        return val == "true" || val == "on";
    }
    std::string HttpUtils::BytesToHex(const std::vector<uint8_t>& data)
    {
        std::string text;
        BytesToHex(text,data);
        return text;
    }
    std::string HttpUtils::BytesToHex(const std::vector<uint8_t>& data, bool isUpper)
    {
        std::string text;
        BytesToHex(text,data, isUpper);
        return text;
    }
    void HttpUtils::BytesToHex(std::string& text,const std::vector<uint8_t>& data)
    {
        if(data.empty()) {
            text.clear();
            return;
        }
        text.resize(data.size()*2);
        for(size_t i = 0; i < data.size(); i++)
        {
            text[i*2] = NibbleToHex(data[i] >> 4);
            text[i*2+1] += NibbleToHex(data[i]);
        }
    }
    
    void HttpUtils::BytesToHex(std::string& text,const std::vector<uint8_t>& data, bool isUpper)
    {
        if(data.empty()) {
            text.clear();
            return;
        }
        text.resize(data.size()*2);
        for(size_t i = 0; i < data.size(); i++)
        {
            text[i*2] = NibbleToHex(data[i] >> 4, isUpper);
            text[i*2+1] += NibbleToHex(data[i], isUpper);
        }
    }
    std::vector<uint8_t> HttpUtils::HexToBytes(const std::string& text)
    {
        std::vector<uint8_t> data;
        HexToBytes(data,text);
        return data;
    }
    void HttpUtils::HexToBytes(std::vector<uint8_t>& data,const std::string& text)
    {
        if(text.empty()) { data.clear();  return;}
        if(text.size() % 2 != 0) throw std::runtime_error("Text length is not even");
        
    
        data.resize(text.size()/2);
        for(size_t i = 0; i < text.size(); i+=2)
        {
            uint8_t b = HexToNibble(text[i]) << 4;
            b |= HexToNibble(text[i+1]);
            data[i/2] = b;
        }
    }
}
