import ' dart:async ' ;
import ' package:appinio_swiper/appinio_swiper.dart ' ;
import ' package:flutter/cupertino.dart ' ;
import ' package:flutter/foundation.dart ' ;
import ' package:flutter/material.dart ' ;
import ' package:mohem_flutter_app/api/marathon/marathon_api_client.dart ' ;
import ' package:mohem_flutter_app/classes/utils.dart ' ;
import ' package:mohem_flutter_app/config/routes.dart ' ;
import ' package:mohem_flutter_app/models/marathon/marathon_model.dart ' ;
import ' package:mohem_flutter_app/models/marathon/question_model.dart ' ;
import ' package:mohem_flutter_app/ui/marathon/widgets/question_card.dart ' ;
import ' package:video_player/video_player.dart ' ;
class MarathonProvider extends ChangeNotifier {
//****************VARIABLES**********
final AppinioSwiperController swiperController = AppinioSwiperController ( ) ;
MarathonDetailModel marathonDetailModel = MarathonDetailModel ( ) ;
List < CardContent > cardContentList = < CardContent > [ ] ;
QuestionModel currentQuestion = QuestionModel ( ) ;
List < QuestionCardStatus > answerStatusesList = < QuestionCardStatus > [ ] ;
QuestionCardStatus questionCardStatus = QuestionCardStatus . question ;
int ? selectedOptionIndex ;
String ? selectedOptionId ;
int totalQualifiers = 0 ;
bool _isLoading = false ;
bool get isLoading = > _isLoading ;
set isLoading ( bool value ) {
_isLoading = value ;
notifyListeners ( ) ;
}
bool _isUpComingMarathon = true ;
bool get isUpComingMarathon = > _isUpComingMarathon ;
set isUpComingMarathon ( bool value ) {
_isUpComingMarathon = value ;
notifyListeners ( ) ;
}
bool _itsMarathonTime = false ;
bool get itsMarathonTime = > _itsMarathonTime ;
set itsMarathonTime ( bool value ) {
_itsMarathonTime = value ;
notifyListeners ( ) ;
}
bool _isMarathonCompleted = false ;
bool get isMarathonCompleted = > _isMarathonCompleted ;
set isMarathonCompleted ( bool value ) {
_isMarathonCompleted = value ;
notifyListeners ( ) ;
}
bool isUserOutOfGame = false ;
set updateIsUserOutOfGame ( bool value ) {
isUserOutOfGame = value ;
notifyListeners ( ) ;
}
int _currentQuestionNumber = 0 ;
int get currentQuestionNumber = > _currentQuestionNumber ;
set currentQuestionNumber ( int value ) {
_currentQuestionNumber = value ;
notifyListeners ( ) ;
}
int _totalMarathoners = 0 ;
int get totalMarathoners = > _totalMarathoners ;
set totalMarathoners ( int value ) {
_totalMarathoners = value ;
notifyListeners ( ) ;
}
//****************SPONSOR VIDEO PLAYER**********
late VideoPlayerController videoController ;
Future < void > initializeVideoPlayer ( ) async {
// videoController = VideoPlayerController.network(marathonDetailModel.sponsors!.first.video!)..initialize();
videoController = VideoPlayerController . network ( " http://clips.vorwaerts-gmbh.de/VfE_html5.mp4 " ) ;
await videoController . initialize ( ) ;
await videoController . play ( ) ;
await videoController . setVolume ( 1.0 ) ;
await videoController . setLooping ( false ) ;
totalSponsorVideoSeconds = videoController . value . duration . inSeconds ;
notifyListeners ( ) ;
}
void disposeVideoPlayer ( ) {
videoController . dispose ( ) ;
notifyListeners ( ) ;
}
//****************TIMERS**********
int totalSponsorVideoSeconds = 0 ;
Timer timerForSponsorVideo = Timer . periodic ( const Duration ( seconds: 1 ) , ( Timer timer ) { } ) ;
void startTimerForSponsorVideo ( ) {
const Duration oneSec = Duration ( seconds: 1 ) ;
timerForSponsorVideo = Timer . periodic (
oneSec ,
( Timer timer ) async {
if ( totalSponsorVideoSeconds = = 0 ) {
timer . cancel ( ) ;
notifyListeners ( ) ;
return ;
} else {
totalSponsorVideoSeconds - - ;
}
notifyListeners ( ) ;
} ,
) ;
}
int totalSecondsToWaitForMarathon = 20 ;
Timer timerToWaitForMarathon = Timer . periodic ( const Duration ( seconds: 1 ) , ( Timer timer ) { } ) ;
void startTimerToMarathon ( BuildContext context ) {
const Duration oneSec = Duration ( seconds: 1 ) ;
timerToWaitForMarathon = Timer . periodic (
oneSec ,
( Timer timer ) async {
if ( totalSecondsToWaitForMarathon = = 0 ) {
} else {
totalSecondsToWaitForMarathon - - ;
}
notifyListeners ( ) ;
} ,
) ;
}
int totalCurrentQuestionTime = 0 ;
int currentGapTime = 0 ;
Timer timerForQuestion = Timer . periodic ( const Duration ( seconds: 1 ) , ( Timer timer ) { } ) ;
void startTimerForQuestion ( ) {
const Duration oneSec = Duration ( seconds: 1 ) ;
timerForQuestion = Timer . periodic (
oneSec ,
( Timer timer ) async {
// This 2 is just to show the color of answer tile for 2 seconds and then update card status
if ( totalCurrentQuestionTime - currentGapTime = = currentQuestion . questionTime ! - 2 ) {
getCorrectAnswerAndUpdateAnswerColor ( ) ;
}
if ( totalCurrentQuestionTime = = currentGapTime ) {
updateCardStatusToAnswer ( ) ;
await callSubmitOptionApi ( ) . then ( ( bool value ) async {
if ( value ) {
await callNextQuestionApi ( ) ;
}
} ) ;
}
if ( totalCurrentQuestionTime = = 0 ) {
updateCardData ( ) ;
if ( currentQuestionNumber = = marathonDetailModel . totalQuestions ! - 1 ) {
updateQuestionCardStatus ( QuestionCardStatus . findingWinner ) ;
timer . cancel ( ) ;
cancelTimer ( ) ;
notifyListeners ( ) ;
}
return ;
} else {
totalCurrentQuestionTime - - ;
}
notifyListeners ( ) ;
} ,
) ;
}
int totalSecondsToWaitForWinner = 30 ;
Timer timerForWinnerSelection = Timer . periodic ( const Duration ( seconds: 1 ) , ( Timer timer ) { } ) ;
void startTimerForWinnerSelection ( ) {
const Duration oneSec = Duration ( seconds: 1 ) ;
timerForWinnerSelection = Timer . periodic (
oneSec ,
( Timer timer ) async {
if ( totalSecondsToWaitForWinner = = 0 ) {
timer . cancel ( ) ;
updateQuestionCardStatus ( QuestionCardStatus . winnerFound ) ;
return ;
} else {
totalSecondsToWaitForWinner - - ;
}
notifyListeners ( ) ;
} ,
) ;
}
//****************FUNCTIONS*********
Future < bool > callSubmitOptionApi ( ) async {
return await MarathonApiClient ( ) . submitSelectedOption ( selectedAnswerId: selectedOptionId ) ;
}
// TODO: here I need to add a logic where I should call this function for Api but for the 1st question it should behave differently
// TODO: Verify the callings!!!
Future < void > callNextQuestionApi ( ) async {
if ( currentQuestionNumber < marathonDetailModel . totalQuestions ! ) {
if ( currentQuestionNumber = = 0 ) {
currentQuestion = ( await MarathonApiClient ( ) . getNextQuestion ( questionId: null , marathonId: marathonDetailModel . id ! ) ) ! ;
if ( Utils . isLoading ) {
Utils . hideLoading ( AppRoutes . navigatorKey . currentContext ! ) ;
}
startTimerForQuestion ( ) ;
updateCardData ( ) ;
Navigator . pushReplacementNamed ( AppRoutes . navigatorKey . currentContext ! , AppRoutes . marathonScreen ) ;
} else {
currentQuestion = ( await MarathonApiClient ( ) . getNextQuestion ( questionId: currentQuestion . id , marathonId: marathonDetailModel . id ! ) ) ! ;
}
notifyListeners ( ) ;
}
}
void updateCardData ( ) {
if ( currentQuestionNumber > 0 ) {
print ( " swiped it away!! " ) ;
swipeCardLeft ( ) ;
}
selectedOptionIndex = null ;
currentQuestionNumber + + ;
cardContentList . add ( const CardContent ( ) ) ;
totalCurrentQuestionTime = currentQuestion . questionTime ! + currentQuestion . nextQuestGap ! ;
currentGapTime = currentQuestion . nextQuestGap ! ;
totalMarathoners = currentQuestion . remainingParticipantCount ! ;
questionCardStatus = QuestionCardStatus . question ;
}
void populateQuestionStatusesList ( ) {
if ( marathonDetailModel . totalQuestions ! = null ) {
for ( int i = 0 ; i < marathonDetailModel . totalQuestions ! - 1 ; i + + ) {
answerStatusesList . add ( QuestionCardStatus . question ) ;
}
notifyListeners ( ) ;
}
}
void updateAnswerStatusesList ( QuestionCardStatus status ) {
answerStatusesList [ currentQuestionNumber - 1 ] = status ;
notifyListeners ( ) ;
}
void addItemToList ( CardContent value ) {
cardContentList . add ( value ) ;
notifyListeners ( ) ;
}
void updateCurrentQuestionOptionStatus ( QuestionsOptionStatus status , int index ) {
for ( int i = 0 ; i < currentQuestion . questionOptions ! . length ; i + + ) {
currentQuestion . questionOptions ! [ i ] . optionStatus = QuestionsOptionStatus . unSelected ;
}
currentQuestion . questionOptions ! [ index ] . optionStatus = status ;
selectedOptionIndex = index ;
notifyListeners ( ) ;
}
void updateQuestionCardStatus ( QuestionCardStatus status ) {
if ( status = = QuestionCardStatus . wrongAnswer | | status = = QuestionCardStatus . skippedAnswer ) {
updateIsUserOutOfGame = true ;
}
questionCardStatus = status ;
notifyListeners ( ) ;
}
void getCorrectAnswerAndUpdateAnswerColor ( ) {
if ( selectedOptionIndex ! = null ) {
if ( currentQuestion . questionOptions ! [ selectedOptionIndex ! ] . isCorrectOption ! ) {
updateCurrentQuestionOptionStatus ( QuestionsOptionStatus . correct , selectedOptionIndex ! ) ;
} else {
updateCurrentQuestionOptionStatus ( QuestionsOptionStatus . wrong , selectedOptionIndex ! ) ;
}
}
}
void updateCardStatusToAnswer ( ) {
if ( currentQuestionNumber = = 0 ) {
return ;
}
if ( selectedOptionIndex ! = null ) {
if ( currentQuestion . questionOptions ! [ selectedOptionIndex ! ] . isCorrectOption ! ) {
updateQuestionCardStatus ( QuestionCardStatus . correctAnswer ) ;
updateAnswerStatusesList ( QuestionCardStatus . correctAnswer ) ;
} else {
updateQuestionCardStatus ( QuestionCardStatus . wrongAnswer ) ;
updateAnswerStatusesList ( QuestionCardStatus . wrongAnswer ) ;
}
} else {
updateQuestionCardStatus ( QuestionCardStatus . skippedAnswer ) ;
updateAnswerStatusesList ( QuestionCardStatus . skippedAnswer ) ;
}
}
void swipeCardLeft ( ) {
swiperController . swipeLeft ( ) ;
notifyListeners ( ) ;
}
void resetValues ( ) async {
_currentQuestionNumber = 0 ;
cardContentList . clear ( ) ;
timerForWinnerSelection . cancel ( ) ;
timerForQuestion . cancel ( ) ;
_isMarathonCompleted = false ;
totalCurrentQuestionTime = 0 ;
currentQuestion = QuestionModel ( ) ;
notifyListeners ( ) ;
}
void cancelTimer ( ) {
timerForQuestion . cancel ( ) ;
notifyListeners ( ) ;
}
Future < void > getMarathonDetailsFromApi ( ) async {
isLoading = true ;
notifyListeners ( ) ;
await MarathonApiClient ( ) . getMarathonToken ( ) . whenComplete ( ( ) async {
marathonDetailModel = await MarathonApiClient ( ) . getMarathonDetails ( ) ;
if ( marathonDetailModel . id = = null ) {
isUpComingMarathon = false ;
notifyListeners ( ) ;
return ;
}
populateQuestionStatusesList ( ) ;
isLoading = false ;
notifyListeners ( ) ;
} ) ;
}
Future < void > buildConnectionWithSignalR ( BuildContext context ) async {
Utils . showLoading ( context ) ;
try {
resetValues ( ) ;
// await MarathonApiClient().buildHubConnection(context, marathonDetailModel.sponsors!.first.sponsorPrizes!.first.id!);
} catch ( e ) {
if ( kDebugMode ) {
print ( " error in buildConnectionWithSignalR: ${ e . toString ( ) } " ) ;
}
Utils . hideLoading ( context ) ;
Utils . confirmDialog ( context , e . toString ( ) ) ;
}
}
Future < void > onJoinMarathonPressed ( BuildContext context ) async {
//TODO: here we need to put a check to make sure we should not display sponsor when remaining time to marathon is less than 30 seconds plus video duration e.g. 30 seconds + video duration time
// if (marathonDetailModel.sponsors!.first.video != null && marathonDetailModel.sponsors!.first.video != "") {
if ( false ) {
await initializeVideoPlayer ( ) . then ( ( _ ) {
startTimerForSponsorVideo ( ) ;
Navigator . pushNamed ( context , AppRoutes . marathonSponsorVideoScreen ) ;
} ) ;
} else {
try {
Utils . showLoading ( context ) ;
bool isJoined = await MarathonApiClient ( ) . joinMarathonAsParticipant ( ) ;
if ( isJoined ) {
print ( " joined " ) ;
callNextQuestionApi ( ) ;
}
} catch ( e , s ) {
Utils . hideLoading ( context ) ;
Utils . confirmDialog ( context , e . toString ( ) ) ;
}
}
}
}