package com.oguerreiro.resilient.activity;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;

/**
 * @param <AI> the interface that defines the SELF bean. Will be used to automatically infer the bean and supply the
 *             self() bean reference
 */
public abstract class AbstractSelfEnabler<SELF extends Object> {
  private SELF self;

  @Autowired
  private ApplicationContext applicationContext;

  /**
   * Use this, to make calls within the Task instance to another method in the this same Task, forcing the call to be
   * done to the proxy.
   * Specifically, this should be used so the annotations @Async and @Transaction work between calls of method sin the
   * same beans instance.
   * ATTENTION: The bean MUST be defined by an interface. If not, it doesn't work.
   * 
   * @return
   */
  protected SELF self() {
    if (this.self == null) {
      this.self = this.getBean(getGenericTypeClass());
    }
    return this.self;
  }

  private <B> B getBean(Class<B> beanClass) {
    return applicationContext.getBean(beanClass);
  }

  /**
   * At runtime retrieves the actual Interface passed to the Generic <AI>
   * 
   * @return
   */
  @SuppressWarnings("unchecked")
  private Class<SELF> getGenericTypeClass() {
    Type type = getClass().getGenericSuperclass();
    ParameterizedType parameterizedType = (ParameterizedType) type;
    return (Class<SELF>) parameterizedType.getActualTypeArguments()[2];	// Get the third Generica arg
  }
}
