import ' dart:async ' ;
import ' dart:developer ' ;
import ' package:appinio_swiper/appinio_swiper.dart ' ;
import ' package:easy_localization/easy_localization.dart ' ;
import ' package:flutter/cupertino.dart ' ;
import ' package:flutter/material.dart ' ;
import ' package:mohem_flutter_app/api/marathon/demo_marathon_repo.dart ' ;
import ' package:mohem_flutter_app/api/marathon/marathon_api_client.dart ' ;
import ' package:mohem_flutter_app/app_state/app_state.dart ' ;
import ' package:mohem_flutter_app/classes/consts.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_generic_model.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/models/marathon/winner_model.dart ' ;
import ' package:mohem_flutter_app/models/privilege_list_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 ( ) ;
MarathonDetailModel demoMarathonDetailModel = MarathonDetailModel ( ) ;
List < CardContent > cardContentList = < CardContent > [ ] ;
QuestionModel currentQuestion = QuestionModel ( ) ;
List < QuestionCardStatus > answerStatusesList = < QuestionCardStatus > [ ] ;
QuestionCardStatus questionCardStatus = QuestionCardStatus . question ;
List < WinnerModel > ? selectedWinners ;
int ? selectedOptionIndex ;
String ? selectedOptionId ;
int ? totalQualifiers ;
Locale savedLocale = const Locale ( " en " , " US " ) ;
String ? gapTimeImage ;
String ? gapTimeText ;
int ? gapTimeType ;
bool iAmWinner = false ;
bool isGettingQualifiers = false ;
bool isPrivilegedWithMarathon = false ;
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 _canPlayDemo = true ;
bool get canPlayDemo = > _canPlayDemo ;
set canPlayDemo ( bool value ) {
_canPlayDemo = value ;
notifyListeners ( ) ;
}
bool _isButtonEnabled = false ;
bool get isButtonEnabled = > _isButtonEnabled ;
set isButtonEnabled ( bool value ) {
_isButtonEnabled = value ;
notifyListeners ( ) ;
}
bool _isUserWaiting = false ;
bool get isUserWaiting = > _isUserWaiting ;
set isUserWaiting ( bool value ) {
_isUserWaiting = 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 ( ApiConsts . marathonBaseUrlServices + marathonDetailModel . sponsors ! . first . video ! ) ;
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 ( ) ;
}
// 9c47d281-c5b5-4b5d-a90a-08dacb8cbdb6
// MarathonI
//************************************************ TIMERS **********************************************************
int sponsorsSecondsCounter = 0 ;
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 {
sponsorsSecondsCounter + + ;
if ( totalSponsorVideoSeconds = = 0 ) {
timer . cancel ( ) ;
notifyListeners ( ) ;
return ;
} else {
totalSponsorVideoSeconds - - ;
}
notifyListeners ( ) ;
} ,
) ;
}
int totalSecondsToWaitForMarathon = 30 ;
set updateTotalSecondsToWaitForMarathon ( int value ) {
totalSecondsToWaitForMarathon = value ;
notifyListeners ( ) ;
}
Timer timerToWaitForMarathon = Timer . periodic ( const Duration ( seconds: 1 ) , ( Timer timer ) { } ) ;
void startTimerToWaitForMarathon ( ) {
const Duration oneSec = Duration ( seconds: 1 ) ;
timerToWaitForMarathon = Timer . periodic (
oneSec ,
( Timer timer ) async {
if ( totalSecondsToWaitForMarathon = = 0 ) {
timer . cancel ( ) ;
if ( isUserWaiting ) {
await callNextQuestionApi ( ) ;
} else {
isButtonEnabled = false ;
}
return ;
} else {
totalSecondsToWaitForMarathon - - ;
}
notifyListeners ( ) ;
} ,
) ;
}
int totalCurrentQuestionTime = 0 ;
int currentGapTime = 0 ;
Timer timerForQuestion = Timer . periodic ( const Duration ( seconds: 1 ) , ( Timer timer ) { } ) ;
int callCountThreshold = 0 ;
void startTimerForQuestion ( ) {
const Duration oneSec = Duration ( seconds: 1 ) ;
timerForQuestion = Timer . periodic (
oneSec ,
( Timer timer ) async {
// This 1 is just to show the color of answer tile for 1 and then update card status
if ( totalCurrentQuestionTime - currentGapTime = = 0 ) {
if ( callCountThreshold = = 0 ) {
getCorrectAnswerAndUpdateAnswerColor ( ) ;
}
}
if ( totalCurrentQuestionTime - currentGapTime = = - 2 ) {
if ( callCountThreshold = = 0 ) {
updateCardStatusToAnswer ( ) ;
}
// scheduleMicrotask(() async {
// if (AppState().getIsDemoMarathon || isUserOutOfGame) {
// await callNextQuestionApi();
// } else {
// await callSubmitOptionApi().then((bool value) async {
// updateIsUserOutOfGame = !value;
// await callNextQuestionApi();
// });
// }
// });
if ( currentQuestionNumber = = ( AppState ( ) . getIsDemoMarathon ? demoMarathonDetailModel . totalQuestions ! : marathonDetailModel . totalQuestions ! ) ) {
isGettingQualifiers = true ;
updateQuestionCardStatus ( QuestionCardStatus . findingWinner ) ;
timer . cancel ( ) ;
cancelTimer ( ) ;
notifyListeners ( ) ;
}
totalCurrentQuestionTime - - ;
callCountThreshold = 0 ;
}
if ( totalCurrentQuestionTime = = 0 ) {
gapTimeImage = currentQuestion . gapImage ;
gapTimeText = currentQuestion . gapText ;
gapTimeType = currentQuestion . gapType ;
updateCardData ( ) ;
return ;
} else {
if ( totalCurrentQuestionTime - currentGapTime ! = - 2 ) {
totalCurrentQuestionTime - - ;
callCountThreshold = 0 ;
}
}
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 = = 1 ) {
timer . cancel ( ) ;
await callGetSelectedWinnersApi ( ) . whenComplete ( ( ) = > updateQuestionCardStatus ( QuestionCardStatus . winnerFound ) ) ;
return ;
} else if ( totalSecondsToWaitForWinner = = 15 ) {
totalSecondsToWaitForWinner - - ;
await callGetQualifiersApi ( ) ;
} else {
totalSecondsToWaitForWinner - - ;
}
notifyListeners ( ) ;
} ,
) ;
}
//************************************************ FUNCTIONS **********************************************************
void updateLanguageAsPerMarathon ( BuildContext context , MarathonDetailModel detailModel ) {
savedLocale = context . locale ;
if ( detailModel . selectedLanguage = = 1 ) {
context . setLocale ( const Locale ( " en " , " US " ) ) ;
} else if ( detailModel . selectedLanguage = = 2 ) {
context . setLocale ( const Locale ( " ar " , " SA " ) ) ;
} else if ( detailModel . selectedLanguage = = 3 ) {
} else {
context . setLocale ( const Locale ( " en " , " US " ) ) ;
}
}
Future < bool > callSubmitOptionApi ( ) async {
return await MarathonApiClient ( ) . submitSelectedOption ( marathonId: marathonDetailModel . id ! , questionId: currentQuestion . id , selectedAnswerId: selectedOptionId ) ;
}
Future < void > callGetQualifiersApi ( ) async {
if ( AppState ( ) . getIsDemoMarathon ) {
totalQualifiers = isUserOutOfGame ? 0 : 1 ;
} else {
totalQualifiers = await MarathonApiClient ( ) . getQualifiers ( marathonId: marathonDetailModel . id ! ) ;
}
isGettingQualifiers = false ;
notifyListeners ( ) ;
}
Future < void > callGetSelectedWinnersApi ( ) async {
if ( AppState ( ) . getIsDemoMarathon ) {
return ;
}
selectedWinners = await MarathonApiClient ( ) . getSelectedWinner ( marathonId: marathonDetailModel . id ! ) ;
if ( selectedWinners ! = null ) {
selectedWinners ! . removeWhere ( ( WinnerModel element ) {
if ( element . employeeId = = AppState ( ) . memberInformationList ! . eMPLOYEENUMBER ) {
iAmWinner = true ;
return true ;
} else {
return false ;
}
} ) ;
}
notifyListeners ( ) ;
}
Future < void > callNextQuestionApi ( ) async {
if ( currentQuestionNumber < ( AppState ( ) . getIsDemoMarathon ? demoMarathonDetailModel . totalQuestions ! : marathonDetailModel . totalQuestions ! ) ) {
if ( currentQuestionNumber = = 0 ) {
if ( ! AppState ( ) . getIsDemoMarathon ) {
Utils . showLoading ( AppRoutes . navigatorKey . currentContext ! ) ;
}
currentQuestion = AppState ( ) . getIsDemoMarathon
? await DemoMarathonRepo ( ) . getDemoNextQuestion ( currentQuestionNumber: currentQuestionNumber )
: ( await MarathonApiClient ( ) . getNextQuestion ( questionId: null , marathonId: marathonDetailModel . id ! ) ) ;
gapTimeImage = currentQuestion . gapImage ;
gapTimeText = currentQuestion . gapText ;
gapTimeType = currentQuestion . gapType ;
startTimerForQuestion ( ) ;
updateCardData ( ) ;
if ( Utils . isLoading ) {
Utils . hideLoading ( AppRoutes . navigatorKey . currentContext ! ) ;
}
if ( ! AppState ( ) . getIsDemoMarathon ) {
totalMarathoners = await MarathonApiClient ( ) . getMarathonersCount ( marathonId: marathonDetailModel . id ! ) ;
}
Navigator . pushReplacementNamed ( AppRoutes . navigatorKey . currentContext ! , AppRoutes . marathonScreen ) ;
} else {
currentQuestion = AppState ( ) . getIsDemoMarathon
? await DemoMarathonRepo ( ) . getDemoNextQuestion ( currentQuestionNumber: currentQuestionNumber )
: ( await MarathonApiClient ( ) . getNextQuestion ( questionId: currentQuestion . id , marathonId: marathonDetailModel . id ! ) ) ;
}
notifyListeners ( ) ;
}
}
void updateCardData ( ) {
if ( currentQuestionNumber > 0 ) {
// swiperController.swipeLeft();
}
selectedOptionIndex = null ;
currentQuestionNumber + + ;
cardContentList . add ( const CardContent ( ) ) ;
totalCurrentQuestionTime = currentQuestion . questionTime ! + currentQuestion . nextQuestGap ! ;
currentGapTime = currentQuestion . nextQuestGap ! ;
totalMarathoners = currentQuestion . remainingParticipantCount ! ;
questionCardStatus = QuestionCardStatus . question ;
notifyListeners ( ) ;
}
void populateQuestionStatusesList ( ) {
answerStatusesList . clear ( ) ;
if ( demoMarathonDetailModel . totalQuestions ! = null ) {
for ( int i = 0 ; i < demoMarathonDetailModel . totalQuestions ! ; i + + ) {
answerStatusesList . add ( QuestionCardStatus . question ) ;
}
notifyListeners ( ) ;
}
if ( isUpComingMarathon ) {
if ( marathonDetailModel . totalQuestions ! = null ) {
for ( int i = 0 ; i < marathonDetailModel . totalQuestions ! ; 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 ( { required QuestionsOptionStatus status , required int selectedOptIndex , required int correctOptionIndex } ) {
for ( int i = 0 ; i < currentQuestion . questionOptions ! . length ; i + + ) {
currentQuestion . questionOptions ! [ i ] . optionStatus = QuestionsOptionStatus . unSelected ;
}
if ( status = = QuestionsOptionStatus . wrong ) {
currentQuestion . questionOptions ! [ correctOptionIndex ] . optionStatus = QuestionsOptionStatus . correct ; // if person's answer is wrong we have to show him the actual right answer
}
currentQuestion . questionOptions ! [ selectedOptIndex ] . optionStatus = status ;
selectedOptionId = currentQuestion . questionOptions ! [ selectedOptIndex ] . id ;
selectedOptionIndex = selectedOptIndex ;
notifyListeners ( ) ;
}
void updateQuestionCardStatus ( QuestionCardStatus status ) {
if ( status = = QuestionCardStatus . wrongAnswer | | status = = QuestionCardStatus . skippedAnswer ) {
if ( AppState ( ) . getIsDemoMarathon ) {
updateIsUserOutOfGame = true ;
}
}
questionCardStatus = status ;
notifyListeners ( ) ;
}
void getCorrectAnswerAndUpdateAnswerColor ( ) {
log ( " correctOptionIndex " ) ;
callCountThreshold = 1 ;
int correctOptionIndex = - 1 ;
if ( demoMarathonDetailModel . displayCorrectAnswer ? ? false ) {
correctOptionIndex = currentQuestion . questionOptions ! . indexWhere ( ( QuestionOptions element ) = > element . isCorrectOption ? ? false ) ;
log ( " correctOptionIndex: $ correctOptionIndex " ) ;
}
if ( selectedOptionIndex ! = null ) {
scheduleMicrotask ( ( ) async {
if ( AppState ( ) . getIsDemoMarathon ) {
if ( currentQuestion . questionOptions ! [ selectedOptionIndex ! ] . isCorrectOption ! ) {
updateCurrentQuestionOptionStatus ( status: QuestionsOptionStatus . correct , selectedOptIndex: selectedOptionIndex ! , correctOptionIndex: correctOptionIndex ) ;
} else {
updateCurrentQuestionOptionStatus ( status: QuestionsOptionStatus . wrong , selectedOptIndex: selectedOptionIndex ! , correctOptionIndex: correctOptionIndex ) ;
}
} else {
await callSubmitOptionApi ( ) . then ( ( bool value ) async {
updateIsUserOutOfGame = ! value ;
if ( value ) {
updateCurrentQuestionOptionStatus ( status: QuestionsOptionStatus . correct , selectedOptIndex: selectedOptionIndex ! , correctOptionIndex: correctOptionIndex ) ;
} else {
updateCurrentQuestionOptionStatus ( status: QuestionsOptionStatus . wrong , selectedOptIndex: selectedOptionIndex ! , correctOptionIndex: correctOptionIndex ) ;
}
} ) ;
}
} ) ;
} else {
if ( ! AppState ( ) . getIsDemoMarathon ) {
scheduleMicrotask ( ( ) async {
await callSubmitOptionApi ( ) . then ( ( bool value ) async {
updateIsUserOutOfGame = ! value ;
} ) ;
} ) ;
}
}
}
void updateCardStatusToAnswer ( ) {
if ( currentQuestionNumber = = 0 ) {
return ;
}
callCountThreshold = 1 ;
scheduleMicrotask ( ( ) async {
await callNextQuestionApi ( ) ;
} ) ;
if ( selectedOptionIndex = = null ) {
updateQuestionCardStatus ( QuestionCardStatus . skippedAnswer ) ;
updateAnswerStatusesList ( QuestionCardStatus . skippedAnswer ) ;
return ;
}
if ( AppState ( ) . getIsDemoMarathon ) {
if ( currentQuestion . questionOptions ! [ selectedOptionIndex ! ] . isCorrectOption ! ) {
updateQuestionCardStatus ( QuestionCardStatus . correctAnswer ) ;
updateAnswerStatusesList ( QuestionCardStatus . correctAnswer ) ;
} else {
updateQuestionCardStatus ( QuestionCardStatus . wrongAnswer ) ;
updateAnswerStatusesList ( QuestionCardStatus . wrongAnswer ) ;
}
return ;
}
if ( ! isUserOutOfGame ) {
updateQuestionCardStatus ( QuestionCardStatus . correctAnswer ) ;
updateAnswerStatusesList ( QuestionCardStatus . correctAnswer ) ;
return ;
}
updateQuestionCardStatus ( QuestionCardStatus . wrongAnswer ) ;
updateAnswerStatusesList ( QuestionCardStatus . wrongAnswer ) ;
}
void resetValues ( ) async {
_currentQuestionNumber = 0 ;
iAmWinner = false ;
cardContentList . clear ( ) ;
itsMarathonTime = false ;
timerForWinnerSelection . cancel ( ) ;
timerForSponsorVideo . cancel ( ) ;
timerToWaitForMarathon . cancel ( ) ;
timerForQuestion . cancel ( ) ;
_isMarathonCompleted = false ;
isUserOutOfGame = false ;
isButtonEnabled = false ;
isUserWaiting = false ;
canPlayDemo = true ;
totalCurrentQuestionTime = 0 ;
sponsorsSecondsCounter = 0 ;
totalSponsorVideoSeconds = 0 ;
totalSecondsToWaitForWinner = 30 ;
totalSecondsToWaitForMarathon = 30 ;
AppState ( ) . setIsDemoMarathon = false ;
currentGapTime = 0 ;
currentQuestion = QuestionModel ( ) ;
if ( answerStatusesList . isNotEmpty ) {
for ( int i = 0 ; i < answerStatusesList . length ; i + + ) {
answerStatusesList [ i ] = QuestionCardStatus . question ;
}
}
AppRoutes . navigatorKey . currentContext ! . setLocale ( savedLocale ) ;
notifyListeners ( ) ;
}
void cancelTimer ( ) {
timerForQuestion . cancel ( ) ;
notifyListeners ( ) ;
}
bool checkIfPrivilegedForMarathon ( ) {
for ( PrivilegeListModel element in AppState ( ) . privilegeListModel ! ) {
if ( element . serviceName = = " Marathon " ) {
if ( element . previlege ! = null ) {
return element . previlege ! ;
}
}
}
return false ;
}
Future < void > getMarathonDetailsFromApi ( ) async {
isLoading = true ;
notifyListeners ( ) ;
isPrivilegedWithMarathon = checkIfPrivilegedForMarathon ( ) ;
demoMarathonDetailModel = await DemoMarathonRepo ( ) . getDemoMarathonDetails ( ) ;
if ( isPrivilegedWithMarathon ) {
marathonDetailModel = await MarathonApiClient ( ) . getMarathonDetails ( ) ;
updateTotalSecondsToWaitForMarathon = marathonDetailModel . marathonBufferTime ? ? 30 ;
if ( marathonDetailModel . id = = null ) {
isUpComingMarathon = false ;
isLoading = false ;
populateQuestionStatusesList ( ) ;
notifyListeners ( ) ;
return ;
}
isUpComingMarathon = true ;
if ( DateTime . parse ( marathonDetailModel . startTime ! ) . isAfter ( DateTime . now ( ) ) ) {
itsMarathonTime = false ;
}
populateQuestionStatusesList ( ) ;
}
isLoading = false ;
notifyListeners ( ) ;
}
Future < void > onJoinMarathonPressed ( BuildContext context ) async {
try {
Utils . showLoading ( AppRoutes . navigatorKey . currentContext ! ) ;
MarathonApiClient ( ) . joinMarathonAsParticipant ( ) . then ( ( MarathonGenericModel ? marathonGenericModel ) async {
Utils . hideLoading ( AppRoutes . navigatorKey . currentContext ! ) ;
if ( marathonGenericModel ! = null & & marathonGenericModel . data ! = null ) {
isUserWaiting = true ;
Navigator . pushReplacementNamed ( context , AppRoutes . marathonWaitingScreen ) ;
} else if ( marathonGenericModel ! . data = = null ) {
Utils . showToast ( marathonGenericModel . message . toString ( ) ) ;
}
} ) ;
} catch ( e ) {
Utils . hideLoading ( AppRoutes . navigatorKey . currentContext ! ) ;
Utils . confirmDialog ( context , e . toString ( ) ) ;
}
}
Future < void > onJoinDemoMarathonPressed ( BuildContext context ) async {
AppState ( ) . setIsDemoMarathon = true ;
await callNextQuestionApi ( ) ;
}
}