content: package im.actor.core.modules.calls;
  
  import org.jetbrains.annotations.NotNull;
  
  import im.actor.core.api.rpc.RequestDoCall;
  import im.actor.core.api.rpc.RequestGetCallInfo;
  import im.actor.core.api.rpc.RequestJoinCall;
  import im.actor.core.api.rpc.RequestRejectCall;
  import im.actor.core.entity.Peer;
  import im.actor.core.modules.ModuleContext;
  import im.actor.core.modules.calls.peers.AbsCallActor;
  import im.actor.core.modules.calls.peers.CallBusActor;
  import im.actor.core.viewmodel.CallState;
  import im.actor.core.viewmodel.CallVM;
  import im.actor.core.viewmodel.CommandCallback;
  import im.actor.core.viewmodel.generics.ArrayListMediaTrack;
  import im.actor.runtime.actors.messages.PoisonPill;
  import im.actor.runtime.power.WakeLock;
  import im.actor.runtime.webrtc.WebRTCMediaTrack;
  import im.actor.runtime.webrtc.WebRTCTrackType;
  
  import static im.actor.core.entity.EntityConverter.convert;
  
  public class CallActor extends AbsCallActor {
  
      private final boolean isMaster;
      private final WakeLock wakeLock;
      private long callId;
      private Peer peer;
  
      private CallVM callVM;
      private CommandCallback<Long> callback;
  
      private boolean isActive;
      private boolean isAnswered;
      private boolean isRejected;
  
      private boolean isVideoInitiallyEnabled;
  
      public CallActor(long callId, WakeLock wakeLock, ModuleContext context) {
          super(context);
          this.wakeLock = wakeLock;
          this.isMaster = false;
          this.callId = callId;
          this.isAnswered = false;
          this.isActive = false;
      }
  
      public CallActor(Peer peer, CommandCallback<Long> callback, WakeLock wakeLock, boolean isVideoInitiallyEnabled, ModuleContext context) {
          super(context);
          this.wakeLock = wakeLock;
          this.isMaster = true;
          this.callback = callback;
          this.peer = peer;
          this.isAnswered = true;
          this.isActive = false;
          this.isVideoInitiallyEnabled = isVideoInitiallyEnabled;
      }
  
      @Override
      public void preStart() {
          super.preStart();
          if (isMaster) {
              api(new RequestDoCall(buidOutPeer(peer), CallBusActor.TIMEOUT, false, false, isVideoInitiallyEnabled)).then(responseDoCall -> {
                  callId = responseDoCall.getCallId();
                  callBus.joinMasterBus(responseDoCall.getEventBusId(), responseDoCall.getDeviceId());
                  callBus.changeVideoEnabled(isVideoInitiallyEnabled);
                  callBus.startOwn();
                  callVM = callViewModels.spawnNewOutgoingVM(responseDoCall.getCallId(), peer, isVideoInitiallyEnabled,
                          isVideoInitiallyEnabled);
              }).failure(e -> self().send(PoisonPill.INSTANCE));
          } else {
              api(new RequestGetCallInfo(callId)).then(responseGetCallInfo -> {
                  peer = convert(responseGetCallInfo.getPeer());
                  callBus.joinBus(responseGetCallInfo.getEventBusId());
                  if (responseGetCallInfo.isVideoPreferred() != null) {
                      isVideoInitiallyEnabled = responseGetCallInfo.isVideoPreferred();
                      callBus.changeVideoEnabled(isVideoInitiallyEnabled);
                  }
                  callVM = callViewModels.spawnNewIncomingVM(callId, peer, isVideoInitiallyEnabled,
                          isVideoInitiallyEnabled, CallState.RINGING);
              }).failure(e -> self().send(PoisonPill.INSTANCE));
          }
      }
  
  
      //
      // Call lifecycle
      //
  
      @Override
      public void onBusStarted(@NotNull String busId) {
          if (isMaster) {
              callManager.send(new CallManagerActor.DoCallComplete(callId), self());
  
              callback.onResult(callId);
              callback = null;
          } else {
              callManager.send(new CallManagerActor.IncomingCallReady(callId), self());
          }
      }
  
      @Override
      public void onCallConnected() {
          // callVM.getState().change()
      }
  
      @Override
      public void onCallEnabled() {
          isActive = true;
          if (isAnswered) {
              callVM.getState().change(CallState.IN_PROGRESS);
              callVM.setCallStart(im.actor.runtime.Runtime.getCurrentTime());
          }
          if (isMaster) {
              callManager.send(new CallManagerActor.OnCallAnswered(callId), self());
          }
      }
  
      public void onAnswerCall() {
          if (!isAnswered && !isRejected) {
              isAnswered = true;
              callBus.startOwn();
              request(new RequestJoinCall(callId));
  
              if (isActive) {
                  callVM.getState().change(CallState.IN_PROGRESS);
                  callVM.setCallStart(im.actor.runtime.Runtime.getCurrentTime());
              } else {
                  callVM.getState().change(CallState.CONNECTING);
              }
          }
      }
  
      public void onRejectCall() {
          if (!isAnswered && !isRejected) {
              isRejected = true;
              request(new RequestRejectCall(callId));
              self().send(PoisonPill.INSTANCE);
          }
      }
  
      @Override
      public void onBusStopped() {
          self().send(PoisonPill.INSTANCE);
      }
  
  
      //
      // Track Events
      //
      @Override
      public void onTrackAdded(long deviceId, WebRTCMediaTrack track) {
          if (track.getTrackType() == WebRTCTrackType.AUDIO) {
              ArrayListMediaTrack tracks = new ArrayListMediaTrack(callVM.getTheirAudioTracks().get());
              tracks.add(track);
              callVM.getTheirAudioTracks().change(tracks);
          } else if (track.getTrackType() == WebRTCTrackType.VIDEO) {
              ArrayListMediaTrack tracks = new ArrayListMediaTrack(callVM.getTheirVideoTracks().get());
              tracks.add(track);
              callVM.getTheirVideoTracks().change(tracks);
          } else {
              // Unknown track type
          }
      }
  
      @Override
      public void onTrackRemoved(long deviceId, WebRTCMediaTrack track) {
          if (track.getTrackType() == WebRTCTrackType.AUDIO) {
              ArrayListMediaTrack tracks = new ArrayListMediaTrack(callVM.getTheirAudioTracks().get());
              tracks.remove(track);
              callVM.getTheirAudioTracks().change(tracks);
          } else if (track.getTrackType() == WebRTCTrackType.VIDEO) {
              ArrayListMediaTrack tracks = new ArrayListMediaTrack(callVM.getTheirVideoTracks().get());
              tracks.remove(track);
              callVM.getTheirVideoTracks().change(tracks);
          } else {
              // Unknown track type
          }
      }
  
      @Override
      public void onOwnTrackAdded(WebRTCMediaTrack track) {
          if (track.getTrackType() == WebRTCTrackType.AUDIO) {
              ArrayListMediaTrack tracks = new ArrayListMediaTrack(callVM.getOwnAudioTracks().get());
              tracks.add(track);
              callVM.getOwnAudioTracks().change(tracks);
          } else if (track.getTrackType() == WebRTCTrackType.VIDEO) {
              ArrayListMediaTrack tracks = new ArrayListMediaTrack(callVM.getOwnVideoTracks().get());
              tracks.add(track);
              callVM.getOwnVideoTracks().change(tracks);
          } else {
              // Unknown track type
          }
      }
  
      @Override
      public void onOwnTrackRemoved(WebRTCMediaTrack track) {
          if (track.getTrackType() == WebRTCTrackType.AUDIO) {
              ArrayListMediaTrack tracks = new ArrayListMediaTrack(callVM.getOwnAudioTracks().get());
              tracks.remove(track);
              callVM.getOwnAudioTracks().change(tracks);
          } else if (track.getTrackType() == WebRTCTrackType.VIDEO) {
              ArrayListMediaTrack tracks = new ArrayListMediaTrack(callVM.getOwnVideoTracks().get());
              tracks.remove(track);
              callVM.getOwnVideoTracks().change(tracks);
          } else {
              // Unknown track type
          }
      }
  
      @Override
      public void onAudioEnableChanged(boolean enabled) {
          super.onAudioEnableChanged(enabled);
          callVM.getIsAudioEnabled().change(enabled);
      }
  
      @Override
      public void onVideoEnableChanged(boolean enabled) {
          super.onVideoEnableChanged(enabled);
          callVM.getIsVideoEnabled().change(enabled);
      }
  
  
      //
      // Cleanup
      //
  
      @Override
      public void postStop() {
          super.postStop();
          if (callVM != null) {
              callVM.getState().change(CallState.ENDED);
              callVM.setCallEnd(im.actor.runtime.Runtime.getCurrentTime());
          }
          callBus.kill();
          if (callId != 0) {
              callManager.send(new CallManagerActor.OnCallEnded(callId), self());
          }
          wakeLock.releaseLock();
      }
  
      //
      // Messages
      //
  
      @Override
      public void onReceive(Object message) {
          if (message instanceof AnswerCall) {
              onAnswerCall();
          } else if (message instanceof RejectCall) {
              onRejectCall();
          } else {
              super.onReceive(message);
          }
      }
  
      public static class AnswerCall {
  
      }
  
      public static class RejectCall {
  
      }
  }
  