/*
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef FRUIT_PROVIDER_DEFN_H
#define FRUIT_PROVIDER_DEFN_H

#include <fruit/impl/injector/injector_storage.h>

// Redundant, but makes KDevelop happy.
#include <fruit/provider.h>

namespace fruit {

template <typename C>
inline Provider<C>::Provider(fruit::impl::InjectorStorage* storage,
                             fruit::impl::InjectorStorage::Graph::node_iterator itr)
    : storage(storage), itr(itr) {}

template <typename C>
inline C* Provider<C>::get() {
  return get<C*>();
}

namespace impl {
namespace meta {

template <typename C>
struct ProviderImplHelper {

  template <typename T>
  using CheckGet = Eval<PropagateError(
      CheckInjectableType(RemoveAnnotations(Type<T>)),
      If(Not(IsSame(GetClassForType(Type<T>), RemoveConstFromType(Type<C>))),
         ConstructError(Id<TypeNotProvidedErrorTag>, Type<T>),
         If(And(TypeInjectionRequiresNonConstBinding(Type<T>), Not(IsSame(Id<GetClassForType(Type<T>)>, Type<C>))),
            ConstructError(TypeProvidedAsConstOnlyErrorTag, Type<T>), None)))>;
};

} // namespace meta
} // namespace impl

template <typename C>
template <typename T>
inline T Provider<C>::get() {
  using E = typename fruit::impl::meta::ProviderImplHelper<C>::template CheckGet<T>;
  (void)typename fruit::impl::meta::CheckIfError<E>::type();
  return storage->template get<T>(itr);
}

template <typename C>
template <typename T>
inline Provider<C>::operator T() {
  return get<T>();
}

} // namespace fruit

#endif // FRUIT_PROVIDER_DEFN_H