#include "attribute.h"
#include "lables.h"
#include "zone.h"
#include "text_utils.h"

#include <saas/util/types/cast.h>

using namespace NSaas;

namespace {
    template <class T>
    inline void InsertAttributeValue(NJson::TJsonValue& json, const TString& value, bool binary, TToJsonContext& context) {
        Y_ENSURE(!binary, "unexpected binary flag for attribute value");

        T tValue;
        if (TryFromString<T>(value, tValue))
            json.InsertValue(NJsonFormat::ValueLable, tValue);
        else
            json.InsertValue(NJsonFormat::ValueLable, ConvertTextToUTF8(value, context.Encoding));
    }

    template <>
    inline void InsertAttributeValue<TString>(NJson::TJsonValue& json, const TString& value, bool binary, TToJsonContext& context) {
        if (binary) {
            json.InsertValue(NJsonFormat::ValueLable, InsertBinaryData(value, context));
        } else {
            json.InsertValue(NJsonFormat::ValueLable, InsertText(value, context));
        }
    }
}

template <>
NJsonFormat::TEntity::EType enum_cast<NJsonFormat::TEntity::EType, TAttributeValue::TAttributeValueType>(TAttributeValue::TAttributeValueType value) {
    switch (value) {
    case TAttributeValue::avtGrp: return NJsonFormat::TEntity::atGROUP;
    case TAttributeValue::avtGrpLit: return NJsonFormat::TEntity::atGROUP_LITERAL;
    case TAttributeValue::avtInt: return NJsonFormat::TEntity::atSEARCH_INTEGER;
    case TAttributeValue::avtLit: return NJsonFormat::TEntity::atSEARCH_LITERAL;
    case TAttributeValue::avtProp: return NJsonFormat::TEntity::atPROPERTY;
    case TAttributeValue::avtFSProp: return NJsonFormat::TEntity::atFSPROPERTY;
    case TAttributeValue::avtFactor: return NJsonFormat::TEntity::atFACTOR;
    case TAttributeValue::avtIntFactor: return NJsonFormat::TEntity::atINT_FACTOR;
    case TAttributeValue::avtSpecKey: return NJsonFormat::TEntity::atSPECKEY;
    case TAttributeValue::avtTIProp: return NJsonFormat::TEntity::atTRIGRAM_INDEXING;
    default:
        Y_FAIL("undefined");
    }
}

NJson::TJsonValue NSaas::TAttributeValue::ToJson(TToJsonContext& context) const {
    NJson::TJsonValue result;
    result.InsertValue(NJsonFormat::TypeLable, GetType());

    if (Types.find(TAttributeValue::avtFactor) != Types.end())
        InsertAttributeValue<double>(result, Value, Binary, context);
    else if (Types.find(TAttributeValue::avtIntFactor) != Types.end())
        InsertAttributeValue<i32>(result, Value, Binary, context);
    else if (Types.find(TAttributeValue::avtGrp) != Types.end())
        InsertAttributeValue<i64>(result, Value, Binary, context);
    else if (Types.find(TAttributeValue::avtGrpLit) != Types.end())
        InsertAttributeValue<TString>(result, Value, Binary, context);
    else if (Types.find(TAttributeValue::avtInt) != Types.end())
        InsertAttributeValue<ui32>(result, Value, Binary, context);
    else if (Types.find(TAttributeValue::avtLit) != Types.end() ||Types.find(TAttributeValue::avtSpecKey) != Types.end() ||
            Types.find(TAttributeValue::avtProp) != Types.end() || Types.find(TAttributeValue::avtFSProp) != Types.end())
        InsertAttributeValue<TString>(result, Value, Binary, context);
    else
        ythrow yexception() << "Unknown or empty attribute value type";
    return result;
}

TAttributeValue& TAttributeValue::AddType(TAttributeValueType type) {
    if (AddTypeInternal(type))
        Parent.OnTypeAdd(Value, type);
    return *this;
}

bool TAttributeValue::AddTypeInternal(TAttributeValueType type) {
    std::pair<TSet<TAttributeValueType>::iterator, bool> insertionResult = Types.insert(type);
    return insertionResult.second;
}

NJson::TJsonValue NSaas::TAttributeValue::GetType() const {
    TString result;
    result = "#";
    for (TSet<TAttributeValue::TAttributeValueType>::const_iterator i = Types.begin(), e = Types.end(); i != e; ++i) {
        NJsonFormat::TEntity::EType entityType = enum_cast<NJsonFormat::TEntity::EType>(*i);
        result.append(NJsonFormat::THashedEntities::GetByType(entityType).ShortName);
    }
    return result;
}

void NSaas::TAttributeValue::SetBinary(bool f) {
    Binary = f;
}

bool NSaas::TAttributeValue::IsBinary() const {
    return Binary;
}

void TAttribute::OnTypeAdd(const TString& value, TAttributeValue::TAttributeValueType type) {
    if (TAttributeValue::IsSearchAttribute(type))
        HasSearchAttiributeValue = true;
    Parent.OnAttribute(Name, value, type);
}

NJson::TJsonValue TAttribute::ToJson(TToJsonContext& context) const {
    NJson::TJsonValue result;
    for (TValues::const_iterator i = Values.begin(); i != Values.end(); i++) {
        result.AppendValue(i->second.ToJson(context));
    }
    return result;
}

TAttributeValue& TAttribute::AddValue(const TString& value) {
    std::pair<TValues::iterator, bool> insertionResult = Values.insert(TValues::value_type(value, TAttributeValue(value, *this)));
    return insertionResult.first->second;
}

void TAttribute::GetValues(TVector<const TAttributeValue*>& values) const {
    for (TValues::const_iterator curValue = Values.begin(); curValue != Values.end(); curValue++) {
        values.push_back(&curValue->second);
    }
}

template <>
NRTYServer::TAttribute::TAttributeType enum_cast<NRTYServer::TAttribute::TAttributeType, TAttributeValue::TAttributeValueType>(TAttributeValue::TAttributeValueType value) {
    switch(value) {
    case TAttributeValue::avtGrp:
    case TAttributeValue::avtInt:
        return NRTYServer::TAttribute::INTEGER_ATTRIBUTE;
    case TAttributeValue::avtLit:
    case TAttributeValue::avtGrpLit:
    case TAttributeValue::avtProp:
    case TAttributeValue::avtSpecKey:
    case TAttributeValue::avtTIProp:
    case TAttributeValue::avtFSProp:
        return NRTYServer::TAttribute::LITERAL_ATTRIBUTE;
    case TAttributeValue::avtFactor:
    case TAttributeValue::avtIntFactor:
        return NRTYServer::TAttribute::FLOAT_ATTRIBUTE;
    default:
        Y_FAIL("undefined");
    }
}
