/*!
 * Check if the actual type can conform to requirement type where actual type is declared inside an owner type
 */
static bool checkTypeConform(const TypePtr& ownerType, TypePtr requirementType, const TypePtr& actualType)
{
    assert(actualType != nullptr);
    if(requirementType->getCategory() == Type::Alias)
    {
        wstring name = requirementType->getName();
        if(name == L"Self")
            requirementType = ownerType;
        else
            requirementType = ownerType->getAssociatedType(name);
        if(requirementType != nullptr)
            requirementType = requirementType->resolveAlias();
        if(requirementType == nullptr && actualType && actualType->getCategory() != Type::Alias)
        {
            //do typealias infer, the required type is not existing in owner type, we implicitly declare it as associated type
            requirementType = actualType;
            TypeBuilderPtr type = static_pointer_cast<TypeBuilder>(ownerType);
            type->addMember(name, actualType);
        }
        if(requirementType == nullptr)
            return false;
    }
    Type::Category category = requirementType->getCategory();
    if(category != actualType->getCategory())
        return false;
    switch(category)
    {
    case Type::Class:
    case Type::Struct:
    case Type::Protocol:
    case Type::Enum:
    case Type::Aggregate:
        return actualType == requirementType;
    case Type::GenericParameter:
        return actualType->getName() == requirementType->getName();
    case Type::MetaType:
        return checkTypeConform(ownerType, requirementType->getInnerType(), actualType->getInnerType());
    case Type::Tuple:
    {
        if(actualType->numElementTypes() != requirementType->numElementTypes())
            return false;
        int num = actualType->numElementTypes();
        for(int i = 0; i < num; i++)
        {
            TypePtr req = requirementType->getElementType(i);
            TypePtr act = actualType->getElementType(i);
            if(!checkTypeConform(ownerType, req, act))
                return false;
        }
        return true;
    }
    case Type::Function:
    {
        size_t num = requirementType->getParameters().size();
        if(num != actualType->getParameters().size())
            return false;
        if(!checkTypeConform(ownerType, requirementType->getReturnType(), actualType->getReturnType()))
            return false;
        auto iter = requirementType->getParameters().begin();
        auto iter2 = actualType->getParameters().begin();
        for(; iter != requirementType->getParameters().end(); iter++, iter2++)
        {
            if(iter->name != iter2->name)
                return false;
            if(iter->inout != iter2->inout)
                return false;
            TypePtr req = iter->type;
            TypePtr act = iter2->type;
            if(!checkTypeConform(ownerType, req, act))
                return false;
        }
        return true;
    }
    case Type::Specialized:
    {
        if(!Type::equals(requirementType->getInnerType(), actualType->getInnerType()))
            return false;
        GenericArgumentPtr greq = requirementType->getGenericArguments();
        GenericArgumentPtr gact = actualType->getGenericArguments();
        if(greq->size() != gact->size())
            return false;
        size_t size = greq->size();
        //TODO: check for parent generic arguments
        for(size_t i = 0; i < size; i++)
        {
            TypePtr req = greq->get(i);
            TypePtr act = gact->get(i);
            if(!checkTypeConform(ownerType, req, act))
                return false;
        }
        return true;

    }
    default:
        assert(0 && "Unsupported type category");
        return false;

    }
}
bool DeclarationAnalyzer::verifyProtocolConform(const TypePtr& type, const TypePtr& protocol, bool supressError)
{
    for(auto entry : protocol->getDeclaredMembers())
    {
        SymbolPtr requirement = entry.second;
        if(FunctionOverloadedSymbolPtr funcs = std::dynamic_pointer_cast<FunctionOverloadedSymbol>(requirement))
        {
            //verify function
            for(auto func : *funcs)
            {
                bool success = verifyProtocolFunction(type, protocol, func, supressError);
                if(!success)
                    return false;
            }
        }
        else if(FunctionSymbolPtr func = std::dynamic_pointer_cast<FunctionSymbol>(requirement))
        {
            //verify function
            bool success = verifyProtocolFunction(type, protocol, func, supressError);
            if(!success)
                return false;
        }
        /*
         else if(requirement == Type::getPlaceHolder())
         {
         //verify inner type
         SymbolPtr sym = type->getAssociatedType(entry.first);
         if(!(std::dynamic_pointer_cast<Type>(sym)))
         {
         //Type %0 does not conform to protocol %1, unimplemented type %2
         error(type->getReference(), Errors::E_TYPE_DOES_NOT_CONFORM_TO_PROTOCOL_UNIMPLEMENTED_TYPE_3, type->getName(), protocol->getName(), entry.first);
         }
         }*/
        else if(TypePtr t = std::dynamic_pointer_cast<Type>(requirement))
        {
            //if type doesn't declare a type alias but the protocol has defined it with a full definition, then we will implicitly declare it
            wstring name = t->getName();
            TypePtr originalType = t->resolveAlias();
            if(type->getAssociatedType(name) == nullptr && originalType != nullptr && originalType->getCategory() != Type::Alias)
            {
                TypeBuilderPtr builder = static_pointer_cast<TypeBuilder>(type);
                builder->addMember(name, originalType);
            }
            //typealias checking is moved to second stage, when all other members verified
            continue;
        }
        else if(dynamic_pointer_cast<ComputedPropertySymbol>(requirement) || dynamic_pointer_cast<SymbolPlaceHolder>(requirement))
        {
            //verify computed properties
            SymbolPtr sym = type->getMember(entry.first);
            //ComputedPropertySymbolPtr sp = std::dynamic_pointer_cast<ComputedPropertySymbol>(sym);
            if(!sym || !checkTypeConform(type, requirement->getType(), sym->getType()))
            {
                if(!supressError)
                {
                    error(type->getReference(), Errors::E_TYPE_DOES_NOT_CONFORM_TO_PROTOCOL_UNIMPLEMENTED_PROPERTY_3, type->getName(), protocol->getName(), entry.first);
                }
                return false;
            }
            bool expectedSetter = requirement->hasFlags(SymbolFlagWritable);
            bool actualSetter = sym->hasFlags(SymbolFlagWritable);
            if(expectedSetter && !actualSetter)
            {
                if(!supressError)
                {
                    error(type->getReference(), Errors::E_TYPE_DOES_NOT_CONFORM_TO_PROTOCOL_UNWRITABLE_PROPERTY_3, type->getName(), protocol->getName(), entry.first);
                }
                return false;
            }
        }
    }
    //check again for associated types
    for(auto entry : protocol->getAssociatedTypes())
    {
        if(entry.second->resolveAlias() != nullptr)
            continue;
        TypePtr t = type->getAssociatedType(entry.first);
        if(t == nullptr)
        {
            //undefined type alias
            if(!supressError)
            {
                error(type->getReference(), Errors::E_TYPE_DOES_NOT_CONFORM_TO_PROTOCOL_2_, type->getName(), protocol->getName());
            }
            return false;
        }
    }
    return true;
}