package net.simonvt.cathode.api;
import android.content.Context;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSyntaxException;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import dagger.Module;
import dagger.Provides;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.inject.Singleton;
import net.simonvt.cathode.api.entity.IsoTime;
import net.simonvt.cathode.api.enumeration.Action;
import net.simonvt.cathode.api.enumeration.CommentType;
import net.simonvt.cathode.api.enumeration.Department;
import net.simonvt.cathode.api.enumeration.Gender;
import net.simonvt.cathode.api.enumeration.GrantType;
import net.simonvt.cathode.api.enumeration.HiddenSection;
import net.simonvt.cathode.api.enumeration.ItemType;
import net.simonvt.cathode.api.enumeration.ItemTypes;
import net.simonvt.cathode.api.enumeration.Privacy;
import net.simonvt.cathode.api.enumeration.Scope;
import net.simonvt.cathode.api.enumeration.ShowStatus;
import net.simonvt.cathode.api.enumeration.TokenType;
import net.simonvt.cathode.api.service.AuthorizationService;
import net.simonvt.cathode.api.service.CheckinService;
import net.simonvt.cathode.api.service.CommentsService;
import net.simonvt.cathode.api.service.EpisodeService;
import net.simonvt.cathode.api.service.MoviesService;
import net.simonvt.cathode.api.service.PeopleService;
import net.simonvt.cathode.api.service.RecommendationsService;
import net.simonvt.cathode.api.service.SearchService;
import net.simonvt.cathode.api.service.SeasonService;
import net.simonvt.cathode.api.service.ShowsService;
import net.simonvt.cathode.api.service.SyncService;
import net.simonvt.cathode.api.service.UsersService;
import net.simonvt.cathode.api.util.OkHttpUtils;
import net.simonvt.cathode.api.util.TimeUtils;
import okhttp3.Cache;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
@Module(library = true, complete = false)
public class TraktModule {
public static final String API_URL = "https://api.trakt.tv";
@Provides @Singleton @Trakt Retrofit provideRestAdapter(@Trakt OkHttpClient client,
@Trakt Gson gson) {
return new Retrofit.Builder() //
.baseUrl(API_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
}
@Provides @Singleton @Trakt OkHttpClient provideOkHttpClient(Context context,
TraktSettings settings, @Trakt List<Interceptor> interceptors) {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(15, TimeUnit.SECONDS);
builder.readTimeout(20, TimeUnit.SECONDS);
final File cacheDir = OkHttpUtils.getCacheDir(context);
builder.cache(new Cache(cacheDir, OkHttpUtils.getCacheSize(cacheDir)));
builder.interceptors().addAll(interceptors);
builder.interceptors().add(new ApiInterceptor(settings));
builder.interceptors().add(new AuthInterceptor(settings));
builder.authenticator(new TraktAuthenticator(settings));
return builder.build();
}
@Provides @Singleton AuthorizationService provideAuthorizationService(@Trakt Retrofit adapter) {
return adapter.create(AuthorizationService.class);
}
@Provides @Singleton CheckinService provideCheckinService(@Trakt Retrofit adapter) {
return adapter.create(CheckinService.class);
}
@Provides @Singleton CommentsService provideCommentsService(@Trakt Retrofit adapter) {
return adapter.create(CommentsService.class);
}
@Provides @Singleton EpisodeService provideEpisodeService(@Trakt Retrofit adapter) {
return adapter.create(EpisodeService.class);
}
@Provides @Singleton MoviesService provideMoviesService(@Trakt Retrofit adapter) {
return adapter.create(MoviesService.class);
}
@Provides @Singleton PeopleService providePeopleService(@Trakt Retrofit adapter) {
return adapter.create(PeopleService.class);
}
@Provides @Singleton RecommendationsService provideRecommendationsService(
@Trakt Retrofit adapter) {
return adapter.create(RecommendationsService.class);
}
@Provides @Singleton SearchService provideSearchService(@Trakt Retrofit adapter) {
return adapter.create(SearchService.class);
}
@Provides @Singleton SeasonService provideSeasonService(@Trakt Retrofit adapter) {
return adapter.create(SeasonService.class);
}
@Provides @Singleton ShowsService provideShowsService(@Trakt Retrofit adapter) {
return adapter.create(ShowsService.class);
}
@Provides @Singleton SyncService provideSyncService(@Trakt Retrofit adapter) {
return adapter.create(SyncService.class);
}
@Provides @Singleton UsersService provideUsersService(@Trakt Retrofit adapter) {
return adapter.create(UsersService.class);
}
@Provides @Singleton @Trakt Gson provideGson() {
GsonBuilder builder = new GsonBuilder();
builder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
builder.registerTypeAdapter(int.class, new IntTypeAdapter());
builder.registerTypeAdapter(Integer.class, new IntTypeAdapter());
builder.registerTypeAdapter(IsoTime.class, new JsonDeserializer<IsoTime>() {
@Override
public IsoTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
String iso = json.getAsString();
if (iso == null) {
return null;
}
long timeInMillis = TimeUtils.getMillis(iso);
return new IsoTime(iso, timeInMillis);
}
});
builder.registerTypeAdapter(GrantType.class, new JsonDeserializer<GrantType>() {
@Override public GrantType deserialize(JsonElement jsonElement, Type type,
JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
return GrantType.fromValue(jsonElement.getAsString());
}
});
builder.registerTypeAdapter(TokenType.class, new JsonDeserializer<TokenType>() {
@Override public TokenType deserialize(JsonElement jsonElement, Type type,
JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
return TokenType.fromValue(jsonElement.getAsString());
}
});
builder.registerTypeAdapter(Scope.class, new JsonDeserializer<Scope>() {
@Override public Scope deserialize(JsonElement jsonElement, Type type,
JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
return Scope.fromValue(jsonElement.getAsString());
}
});
builder.registerTypeAdapter(ItemType.class, new JsonDeserializer<ItemType>() {
@Override public ItemType deserialize(JsonElement jsonElement, Type type,
JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
return ItemType.fromValue(jsonElement.getAsString());
}
});
builder.registerTypeAdapter(Action.class, new JsonDeserializer<Action>() {
@Override public Action deserialize(JsonElement jsonElement, Type type,
JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
return Action.fromValue(jsonElement.getAsString());
}
});
builder.registerTypeAdapter(ShowStatus.class, new JsonDeserializer<ShowStatus>() {
@Override public ShowStatus deserialize(JsonElement jsonElement, Type type,
JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
return ShowStatus.fromValue(jsonElement.getAsString());
}
});
builder.registerTypeAdapter(Gender.class, new JsonDeserializer<Gender>() {
@Override public Gender deserialize(JsonElement jsonElement, Type type,
JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
return Gender.fromValue(jsonElement.getAsString());
}
});
builder.registerTypeAdapter(Privacy.class, new JsonDeserializer<Privacy>() {
@Override public Privacy deserialize(JsonElement jsonElement, Type type,
JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
return Privacy.fromValue(jsonElement.getAsString());
}
});
builder.registerTypeAdapter(HiddenSection.class, new JsonDeserializer<HiddenSection>() {
@Override public HiddenSection deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
return HiddenSection.fromValue(json.getAsString());
}
});
builder.registerTypeAdapter(CommentType.class, new JsonDeserializer<CommentType>() {
@Override public CommentType deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
return CommentType.fromValue(json.getAsString());
}
});
builder.registerTypeAdapter(ItemTypes.class, new JsonDeserializer<ItemTypes>() {
@Override public ItemTypes deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
return ItemTypes.fromValue(json.getAsString());
}
});
builder.registerTypeAdapter(Department.class, new JsonDeserializer<Department>() {
@Override public Department deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
return Department.fromValue(json.getAsString());
}
});
return builder.create();
}
public static class IntTypeAdapter extends TypeAdapter<Number> {
@Override public void write(JsonWriter out, Number value) throws IOException {
out.value(value);
}
@Override public Number read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
try {
String result = in.nextString();
if ("".equals(result)) {
return null;
}
return Integer.parseInt(result);
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
}
}