import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../data/data_sources/remote/geviscope_remote_data_source.dart'; import 'geviscope_event.dart'; import 'geviscope_state.dart'; class GeViScopeBloc extends Bloc { final GeViScopeRemoteDataSource remoteDataSource; GeViScopeBloc({required this.remoteDataSource}) : super(const GeViScopeState()) { on(_onConnect); on(_onDisconnect); on(_onCheckStatus); on(_onLoadChannels); on(_onRefreshChannels); on(_onSendCrossSwitch); on(_onCameraPan); on(_onCameraTilt); on(_onCameraZoom); on(_onCameraStop); on(_onCameraPreset); on(_onCloseContact); on(_onOpenContact); on(_onSendCustomAction); on(_onSendAction); on(_onClearActionResult); } Future _onConnect( ConnectGeViScopeEvent event, Emitter emit, ) async { emit(state.copyWith( connectionStatus: GeViScopeConnectionStatus.connecting, isLoading: true, clearErrorMessage: true, )); try { final result = await remoteDataSource.connect( address: event.address, username: event.username, password: event.password, ); if (result['success'] == true) { emit(state.copyWith( connectionStatus: GeViScopeConnectionStatus.connected, serverAddress: event.address, username: event.username, channelCount: result['channelCount'] ?? 0, connectedAt: DateTime.now(), isLoading: false, lastActionResult: 'Connected to GeViScope at ${event.address}', lastActionSuccess: true, )); // Auto-load channels after connection add(const LoadChannelsEvent()); } else { emit(state.copyWith( connectionStatus: GeViScopeConnectionStatus.error, isLoading: false, errorMessage: result['message'] ?? result['error'] ?? 'Connection failed', lastActionResult: result['message'] ?? result['error'] ?? 'Connection failed', lastActionSuccess: false, )); } } catch (e) { emit(state.copyWith( connectionStatus: GeViScopeConnectionStatus.error, isLoading: false, errorMessage: e.toString(), lastActionResult: 'Connection error: $e', lastActionSuccess: false, )); } } Future _onDisconnect( DisconnectGeViScopeEvent event, Emitter emit, ) async { emit(state.copyWith(isLoading: true)); try { await remoteDataSource.disconnect(); emit(state.copyWith( connectionStatus: GeViScopeConnectionStatus.disconnected, isLoading: false, channelCount: 0, channels: const [], clearServerAddress: true, clearUsername: true, clearConnectedAt: true, lastActionResult: 'Disconnected from GeViScope', lastActionSuccess: true, )); } catch (e) { emit(state.copyWith( isLoading: false, errorMessage: e.toString(), lastActionResult: 'Disconnect error: $e', lastActionSuccess: false, )); } } Future _onCheckStatus( CheckGeViScopeStatusEvent event, Emitter emit, ) async { try { final result = await remoteDataSource.getStatus(); final isConnected = result['is_connected'] == true; emit(state.copyWith( connectionStatus: isConnected ? GeViScopeConnectionStatus.connected : GeViScopeConnectionStatus.disconnected, serverAddress: result['address']?.toString(), username: result['username']?.toString(), channelCount: result['channel_count'] ?? 0, )); } catch (e) { emit(state.copyWith( connectionStatus: GeViScopeConnectionStatus.error, errorMessage: e.toString(), )); } } Future _onLoadChannels( LoadChannelsEvent event, Emitter emit, ) async { try { final result = await remoteDataSource.getChannels(); if (result['channels'] != null) { final channelsList = (result['channels'] as List) .map((c) => MediaChannel.fromJson(c as Map)) .toList(); emit(state.copyWith( channels: channelsList, channelCount: result['count'] ?? channelsList.length, )); } } catch (e) { emit(state.copyWith( lastActionResult: 'Load channels error: $e', lastActionSuccess: false, )); } } Future _onRefreshChannels( RefreshChannelsEvent event, Emitter emit, ) async { emit(state.copyWith(isLoading: true)); try { await remoteDataSource.refreshChannels(); add(const LoadChannelsEvent()); emit(state.copyWith( isLoading: false, lastActionResult: 'Channels refreshed', lastActionSuccess: true, )); } catch (e) { emit(state.copyWith( isLoading: false, lastActionResult: 'Refresh channels error: $e', lastActionSuccess: false, )); } } Future _onSendCrossSwitch( SendGeViScopeCrossSwitchEvent event, Emitter emit, ) async { emit(state.copyWith(isLoading: true)); try { final result = await remoteDataSource.crossSwitch( videoInput: event.videoInput, videoOutput: event.videoOutput, switchMode: event.switchMode, ); final message = 'CrossSwitch(${event.videoInput}, ${event.videoOutput}, ${event.switchMode})'; _addToLog(emit, message); emit(state.copyWith( isLoading: false, lastActionResult: result['success'] == true ? 'CrossSwitch sent successfully' : result['message'] ?? 'CrossSwitch failed', lastActionSuccess: result['success'] == true, )); } catch (e) { emit(state.copyWith( isLoading: false, lastActionResult: 'CrossSwitch error: $e', lastActionSuccess: false, )); } } Future _onCameraPan( CameraPanEvent event, Emitter emit, ) async { emit(state.copyWith(isLoading: true)); try { final result = await remoteDataSource.cameraPan( camera: event.camera, direction: event.direction, speed: event.speed, ); final message = 'CameraPan(${event.camera}, ${event.direction}, ${event.speed})'; _addToLog(emit, message); emit(state.copyWith( isLoading: false, lastActionResult: result['success'] == true ? 'Camera pan ${event.direction}' : result['message'] ?? 'Camera pan failed', lastActionSuccess: result['success'] == true, )); } catch (e) { emit(state.copyWith( isLoading: false, lastActionResult: 'Camera pan error: $e', lastActionSuccess: false, )); } } Future _onCameraTilt( CameraTiltEvent event, Emitter emit, ) async { emit(state.copyWith(isLoading: true)); try { final result = await remoteDataSource.cameraTilt( camera: event.camera, direction: event.direction, speed: event.speed, ); final message = 'CameraTilt(${event.camera}, ${event.direction}, ${event.speed})'; _addToLog(emit, message); emit(state.copyWith( isLoading: false, lastActionResult: result['success'] == true ? 'Camera tilt ${event.direction}' : result['message'] ?? 'Camera tilt failed', lastActionSuccess: result['success'] == true, )); } catch (e) { emit(state.copyWith( isLoading: false, lastActionResult: 'Camera tilt error: $e', lastActionSuccess: false, )); } } Future _onCameraZoom( CameraZoomEvent event, Emitter emit, ) async { emit(state.copyWith(isLoading: true)); try { final result = await remoteDataSource.cameraZoom( camera: event.camera, direction: event.direction, speed: event.speed, ); final message = 'CameraZoom(${event.camera}, ${event.direction}, ${event.speed})'; _addToLog(emit, message); emit(state.copyWith( isLoading: false, lastActionResult: result['success'] == true ? 'Camera zoom ${event.direction}' : result['message'] ?? 'Camera zoom failed', lastActionSuccess: result['success'] == true, )); } catch (e) { emit(state.copyWith( isLoading: false, lastActionResult: 'Camera zoom error: $e', lastActionSuccess: false, )); } } Future _onCameraStop( CameraStopEvent event, Emitter emit, ) async { emit(state.copyWith(isLoading: true)); try { final result = await remoteDataSource.cameraStop(camera: event.camera); final message = 'CameraStop(${event.camera})'; _addToLog(emit, message); emit(state.copyWith( isLoading: false, lastActionResult: result['success'] == true ? 'Camera stopped' : result['message'] ?? 'Camera stop failed', lastActionSuccess: result['success'] == true, )); } catch (e) { emit(state.copyWith( isLoading: false, lastActionResult: 'Camera stop error: $e', lastActionSuccess: false, )); } } Future _onCameraPreset( CameraPresetEvent event, Emitter emit, ) async { emit(state.copyWith(isLoading: true)); try { final result = await remoteDataSource.cameraPreset( camera: event.camera, preset: event.preset, ); final message = 'CameraPreset(${event.camera}, ${event.preset})'; _addToLog(emit, message); emit(state.copyWith( isLoading: false, lastActionResult: result['success'] == true ? 'Camera moved to preset ${event.preset}' : result['message'] ?? 'Camera preset failed', lastActionSuccess: result['success'] == true, )); } catch (e) { emit(state.copyWith( isLoading: false, lastActionResult: 'Camera preset error: $e', lastActionSuccess: false, )); } } Future _onCloseContact( GeViScopeCloseContactEvent event, Emitter emit, ) async { emit(state.copyWith(isLoading: true)); try { final result = await remoteDataSource.digitalIoClose(contactId: event.contactId); final message = 'DigitalIO_Close(${event.contactId})'; _addToLog(emit, message); emit(state.copyWith( isLoading: false, lastActionResult: result['success'] == true ? 'Digital output ${event.contactId} closed' : result['message'] ?? 'Close contact failed', lastActionSuccess: result['success'] == true, )); } catch (e) { emit(state.copyWith( isLoading: false, lastActionResult: 'Close contact error: $e', lastActionSuccess: false, )); } } Future _onOpenContact( GeViScopeOpenContactEvent event, Emitter emit, ) async { emit(state.copyWith(isLoading: true)); try { final result = await remoteDataSource.digitalIoOpen(contactId: event.contactId); final message = 'DigitalIO_Open(${event.contactId})'; _addToLog(emit, message); emit(state.copyWith( isLoading: false, lastActionResult: result['success'] == true ? 'Digital output ${event.contactId} opened' : result['message'] ?? 'Open contact failed', lastActionSuccess: result['success'] == true, )); } catch (e) { emit(state.copyWith( isLoading: false, lastActionResult: 'Open contact error: $e', lastActionSuccess: false, )); } } Future _onSendCustomAction( SendGeViScopeCustomActionEvent event, Emitter emit, ) async { emit(state.copyWith(isLoading: true)); try { final result = await remoteDataSource.sendCustomAction( typeId: event.typeId, text: event.text, ); final message = 'CustomAction(${event.typeId}, "${event.text}")'; _addToLog(emit, message); emit(state.copyWith( isLoading: false, lastActionResult: result['success'] == true ? 'CustomAction sent' : result['message'] ?? 'CustomAction failed', lastActionSuccess: result['success'] == true, )); } catch (e) { emit(state.copyWith( isLoading: false, lastActionResult: 'CustomAction error: $e', lastActionSuccess: false, )); } } Future _onSendAction( SendGeViScopeActionEvent event, Emitter emit, ) async { emit(state.copyWith(isLoading: true)); try { final result = await remoteDataSource.sendAction(event.action); _addToLog(emit, event.action); emit(state.copyWith( isLoading: false, lastActionResult: result['success'] == true ? 'Action sent' : result['message'] ?? 'Action failed', lastActionSuccess: result['success'] == true, )); } catch (e) { emit(state.copyWith( isLoading: false, lastActionResult: 'Send action error: $e', lastActionSuccess: false, )); } } void _onClearActionResult( ClearGeViScopeActionResultEvent event, Emitter emit, ) { emit(state.copyWith(clearLastActionResult: true, clearErrorMessage: true)); } void _addToLog(Emitter emit, String message) { final timestamp = DateTime.now().toIso8601String().substring(11, 19); final logEntry = '[$timestamp] $message'; final newLog = [...state.messageLog, logEntry]; // Keep only last 100 messages if (newLog.length > 100) { newLog.removeRange(0, newLog.length - 100); } emit(state.copyWith(messageLog: newLog)); } }