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

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

using namespace NSaas;

TZone& NSaas::TAbstractZone::AddZone(const TString& name) {
    TAttributes::const_iterator attribute = Attributes.find(name);
    if (attribute != Attributes.end() && attribute->second.IsSearchAttribute())
       throw yexception() << "cannot add a zone: zone " << GetName()
                          << " already has search attribute with name " << name;

    TSimpleSharedPtr<TZone> newZone = new TZone(AddSubZone());
    if (name != NJsonFormat::NoNameZone)
        newZone->SetName(name);
    SubZones.insert(std::make_pair(name, newZone));

    return *newZone;
}

bool NSaas::TAbstractZone::HasZone(const TString& name) const {
    return SubZones.contains(name);
}

TZone& NSaas::TAbstractZone::GetZone(const TString& name) {
    TZones::const_iterator i = SubZones.find(name);
    if (i == SubZones.end())
        throw yexception() << "zone " << name << " does not exist";

    return static_cast<TZone&>(*i->second);
}

void NSaas::TAbstractZone::AddZoneValue(NJson::TJsonValue& target, const TString& name, const NJson::TJsonValue& object) {
    if (!target.Has(name)) {
        target.InsertValue(name, object);
        return;
    }

    if (!target[name].IsArray()) {
        NJson::TJsonValue previous = target[name];
        target.EraseValue(name);
        target.InsertValue(name, NJson::JSON_ARRAY).AppendValue(previous);
    }

    target[name].AppendValue(object);
}

TAttribute& TAbstractZone::AddAttribute(const TString& name) {
    if (!name)
        throw yexception() << "cannot add an attribute with empty name";
    std::pair<TAttributes::iterator, bool> insertionResult = Attributes.insert(TAttributes::value_type(name, TAttribute(name, *this)));
    return insertionResult.first->second;
}

bool NSaas::TAbstractZone::HasAttribute(const TString& name) const {
    return Attributes.contains(name);
}

const TAttribute& TAbstractZone::GetAttribute(const TString& name) const {
    if (!name)
        throw yexception() << "cannot get an attribute with empty name";

    TAttributes::const_iterator attribute = Attributes.find(name);
    if (attribute == Attributes.end())
        throw yexception() << "no attribute with name " << name;

    return (attribute->second);
}

void TAbstractZone::RegisterAttribute(const TString& name, const TString& value, TAttributeValue::TAttributeValueType type) {
    AddAttribute(name.data()).AddValue(value).AddTypeInternal(type);
}

TAbstractZone& TAbstractZone::AddSearchAttribute(const TString& name, const TString& value) {
    AddAttribute(name).AddValue(value).AddType(TAttributeValue::avtLit);
    return *this;
}

TAbstractZone& TAbstractZone::AddSearchAttribute(const TString& name, ui32 value) {
    AddAttribute(name).AddValue(value).AddType(TAttributeValue::avtInt);
    return *this;
}

void TZone::OnAttribute(const TString& name, const TString& value, TAttributeValue::TAttributeValueType type) {
    NRTYServer::TAttribute::TAttributeType attributeType = NRTYServer::TAttribute::LITERAL_ATTRIBUTE;
    switch(type) {
    case TAttributeValue::avtInt:
    case TAttributeValue::avtLit:
        if (SubZones.count(name))
            throw yexception() << "cannot add an attribute: zone " << GetName() << " already has zone with name " << name;

        attributeType = enum_cast<NRTYServer::TAttribute::TAttributeType>(type);
        break;
    default:
        throw yexception() << "only search attributes are supported within a zone";
    }
    NRTYServer::TAttribute& attribute = * InternalZone.AddSearchAttributes();
    attribute.SetName(name);
    attribute.SetValue(value);
    attribute.SetType(attributeType);
}

NJson::TJsonValue TZone::ToJson(TToJsonContext& context) const {
    NJson::TJsonValue result;
    result.InsertValue(NJsonFormat::ValueLable, InsertText(GetText(), context));
    result.InsertValue(NJsonFormat::TypeLable, "#z");
    if (!SubZones.empty() || !Attributes.empty()) {
        NJson::TJsonValue& children = result["children"];
        for (TAttributes::const_iterator i = Attributes.begin(); i != Attributes.end(); ++i) {
            AddZoneValue(children, i->first, i->second.ToJson(context));
        }

        for (TZone::TZones::const_iterator i = SubZones.begin(); i != SubZones.end(); ++i) {
            TZone* subzone = static_cast<TZone*>(i->second.Get());
            AddZoneValue(children, subzone->GetName(), subzone->ToJson(context));
        }
    }
    return result;
}
