#include "common.h"

#include <util/generic/map.h>
#include <util/generic/singleton.h>

namespace NUri {
    static_assert(TFeature::FeatureMAX <= sizeof(unsigned long) * 8, "expect TFeature::FeatureMAX <= sizeof(unsigned long) * 8");

    const TSchemeInfo TSchemeInfo::Registry[] = {
        TSchemeInfo(TScheme::SchemeEmpty, TStringBuf()), // scheme is empty and inited
        TSchemeInfo(TScheme::SchemeHTTP, TStringBuf("http"), TField::FlagHost | TField::FlagPath, 80),
        TSchemeInfo(TScheme::SchemeHTTPS, TStringBuf("https"), TField::FlagHost | TField::FlagPath, 443),
        TSchemeInfo(TScheme::SchemeFTP, TStringBuf("ftp"), TField::FlagHost | TField::FlagPath, 20),
        TSchemeInfo(TScheme::SchemeFILE, TStringBuf("file"), TField::FlagPath),
        TSchemeInfo(TScheme::SchemeWS, TStringBuf("ws"), TField::FlagHost | TField::FlagPath, 80),
        TSchemeInfo(TScheme::SchemeWSS, TStringBuf("wss"), TField::FlagHost | TField::FlagPath, 443),
        // add above
        TSchemeInfo(TScheme::SchemeUnknown, TStringBuf()) // scheme is empty and uninited
    };

    namespace {
        struct TLessNoCase {
            bool operator()(const TStringBuf& lt, const TStringBuf& rt) const {
                return 0 > CompareNoCase(lt, rt);
            }
        };

        class TSchemeInfoMap {
            typedef TMap<TStringBuf, TScheme::EKind, TLessNoCase> TdMap;
            TdMap Map_;

        public:
            TSchemeInfoMap() {
                for (int i = TScheme::SchemeEmpty; i < TScheme::SchemeUnknown; ++i) {
                    const TSchemeInfo& info = TSchemeInfo::Get(TScheme::EKind(i));
                    Map_.insert(std::make_pair(info.Str, info.Kind));
                }
            }

            TScheme::EKind Get(const TStringBuf& scheme) const {
                const TdMap::const_iterator it = Map_.find(scheme);
                return Map_.end() == it ? TScheme::SchemeUnknown : it->second;
            }

            static const TSchemeInfoMap& Instance() {
                return *Singleton<TSchemeInfoMap>();
            }
        };

    }

    const TSchemeInfo& TSchemeInfo::Get(const TStringBuf& scheme) {
        return Registry[TSchemeInfoMap::Instance().Get(scheme)];
    }

    const char* ParsedStateToString(const TState::EParsed& t) {
        switch (t) {
            case TState::ParsedOK:
                return "ParsedOK";
            case TState::ParsedEmpty:
                return "ParsedEmpty";
            case TState::ParsedRootless:
                return "ParsedRootless";
            case TState::ParsedBadFormat:
                return "ParsedBadFormat";
            case TState::ParsedBadPath:
                return "ParsedBadPath";
            case TState::ParsedTooLong:
                return "ParsedTooLong";
            case TState::ParsedBadPort:
                return "ParsedBadPort";
            case TState::ParsedBadAuth:
                return "ParsedBadAuth";
            case TState::ParsedBadScheme:
                return "ParsedBadScheme";
            case TState::ParsedBadHost:
                return "ParsedBadHost";
            default:
                return "Parsed[Unknown]";
        }
    }

    const char* FieldToString(const TField::EField& t) {
        switch (t) {
            case TField::FieldScheme:
                return "scheme";
            case TField::FieldUser:
                return "username";
            case TField::FieldPass:
                return "password";
            case TField::FieldHost:
                return "host";
            case TField::FieldHostAscii:
                return "hostascii";
            case TField::FieldPort:
                return "port";
            case TField::FieldPath:
                return "path";
            case TField::FieldQuery:
                return "query";
            case TField::FieldFrag:
                return "fragment";
            default:
                return "Field[Unknown]";
        }
    }

    const char* SchemeKindToString(const TScheme::EKind& t) {
        const TSchemeInfo& info = TSchemeInfo::Get(t);
        if (!info.Str.empty())
            return info.Str.data();
        return TScheme::SchemeEmpty == t ? "empty" : "unknown";
    }

}
