package com.bumptech.glide.load.data;

import android.support.annotation.NonNull;
import com.bumptech.glide.util.Preconditions;
import java.util.HashMap;
import java.util.Map;

/**
 * Stores a mapping of data class to {@link com.bumptech.glide.load.data.DataRewinder.Factory} and
 * allows registration of new types and factories.
 */
public class DataRewinderRegistry {
  private final Map<Class<?>, DataRewinder.Factory<?>> rewinders = new HashMap<>();
  private static final DataRewinder.Factory<?> DEFAULT_FACTORY =
      new DataRewinder.Factory<Object>() {
        @NonNull
        @Override
        public DataRewinder<Object> build(@NonNull Object data) {
          return new DefaultRewinder(data);
        }

        @NonNull
        @Override
        public Class<Object> getDataClass() {
          throw new UnsupportedOperationException("Not implemented");
        }
      };

  public synchronized void register(@NonNull DataRewinder.Factory<?> factory) {
    rewinders.put(factory.getDataClass(), factory);
  }

  @NonNull
  @SuppressWarnings("unchecked")
  public synchronized <T> DataRewinder<T> build(@NonNull T data) {
    Preconditions.checkNotNull(data);
    DataRewinder.Factory<T> result = (DataRewinder.Factory<T>) rewinders.get(data.getClass());
    if (result == null) {
      for (DataRewinder.Factory<?> registeredFactory : rewinders.values()) {
        if (registeredFactory.getDataClass().isAssignableFrom(data.getClass())) {
          result = (DataRewinder.Factory<T>) registeredFactory;
          break;
        }
      }
    }

    if (result == null) {
      result = (DataRewinder.Factory<T>) DEFAULT_FACTORY;
    }
    return result.build(data);
  }

  private static final class DefaultRewinder implements DataRewinder<Object> {
    private final Object data;

    DefaultRewinder(@NonNull Object data) {
      this.data = data;
    }

    @NonNull
    @Override
    public Object rewindAndGet() {
      return data;
    }

    @Override
    public void cleanup() {
      // Do nothing.
    }
  }
}
