mirrored_repositories/CineReel/lib
import 'dart:async';

import 'package:cine_reel/api/tmdb_api.dart';
import 'package:cine_reel/bloc/bloc_provider.dart';
import 'package:cine_reel/models/tmdb_genres.dart';
import 'package:cine_reel/models/tmdb_movie_basic.dart';
import 'package:cine_reel/models/tmdb_movies_response.dart';
import 'package:cine_reel/ui/list_screen/movie_state.dart';
import 'package:cine_reel/ui/tabs/tab_object.dart';
import 'package:intl/intl.dart';
import 'package:rxdart/rxdart.dart';

class MovieBloc extends BlocBase {
  TMDBApi api;
  int page = 0;
  String region;

  //an instance of the MoviesPopulated state that will be used for each Bloc implementation
  MoviesPopulated moviesPopulated = MoviesPopulated([]);

  // This is the internal object whose stream/sink is provided by this component
  var _streamController = BehaviorSubject<MoviesState>();

  //the tab key with which we can identify the specific bloc implementation
  TabKey tabKey;

  //The genre used only in the cases when loading a list of movies by genre
  TMDBGenre genre;

  // This is the stream of movies. Use this to show the contents
  Stream<MoviesState> get stream {
    if (_streamController.isClosed) {
      print('stream closed, resetting it');
      _streamController = BehaviorSubject<MoviesState>();
    }
    return _streamController.stream;
  }

  final _nextPageController = StreamController();

  Sink get nextPage => _nextPageController.sink;

  MovieBloc({this.api, TabKey this.tabKey, TMDBGenre this.genre, String this.region}) {
    _nextPageController.stream.listen(fetchNextPage);
    init();
  }

  @override
  void dispose() {
    print("close streamController for ${this.tabKey}");
    _streamController.close();
  }

  void init() {
    print('initialising bloc for $tabKey');
    if (page == 0) {
      fetchNextPage();
    }
  }

  fetchNextPage([event]) {
    _streamController.addStream(fetchMoviesFromNetwork());
  }

  Stream<MoviesState> fetchMoviesFromNetwork() async* {
    if (_hasNoExistingData()) {
      yield MoviesLoading();
    }

    page += 1;
    try {
      final result = await _getApiCall(page);
      if (result.isEmpty && _hasNoExistingData()) {
        yield MoviesEmpty();
      } else {
        yield moviesPopulated.update(newMovies: result.results);
      }
    } catch (e) {
      print('error $e');
      yield MoviesError(e.toString());
    }
  }

  //sorts movies by release date in ascending order (i.e going from now to the future)
  List<TMDBMovieBasic> sortMoviesByReleaseDate() {
    moviesPopulated.movies.sort((a, b) {
      var releaseDateA = DateFormat("yyyy-M-dd").parse(a.releaseDate);
      var releaseDateB = DateFormat("yyyy-M-dd").parse(b.releaseDate);
      if (releaseDateA.isAfter(releaseDateB)) {
        return 1;
      } else if (releaseDateA.isBefore(releaseDateB)) {
        return -1;
      } else {
        return 0;
      }
    });
    return moviesPopulated.movies;
  }

  bool _hasNoExistingData() => moviesPopulated.movies?.isEmpty ?? true;

  Future<TMDBMoviesResponse> _getApiCall(int page) {
    Future<TMDBMoviesResponse> apiCall;
    switch (tabKey) {
      case TabKey.kNowPlaying:
        apiCall = api.nowPlayingMovies(page: page);
        break;
      case TabKey.kUpcoming:
        apiCall = api.upcomingMovies(page: page, region: region);
        break;
      case TabKey.kPopular:
        apiCall = api.popularMovies(page: page);
        break;
      case TabKey.kTopRated:
        apiCall = api.topRated(page: page);
        break;
      case TabKey.kGenres:
        apiCall = api.moviesForGenre(genre: genre, page: page);
        break;
      default:
      //
    }
    return apiCall;
  }

  String getVoteAverageForMovie(int movieId) {
    TMDBMovieBasic movie = moviesPopulated.movies.firstWhere((movieBasic) => movieBasic.id == movieId);
    if (movie.voteAverage > 0) {
      return movie.voteAverage.toString();
    } else {
      return "";
    }
  }
}
