import { Injectable } from '@angular/core';

import { Observable } from 'rxjs';
import { map, take, takeWhile } from 'rxjs/operators';

import { ProtocolService } from '@ucap-webmessenger/protocol';
import {
  SVC_TYPE_ROOM,
  SSVC_TYPE_ROOM_OPEN_REQ,
  SSVC_TYPE_ROOM_OPEN2_REQ,
  SSVC_TYPE_ROOM_OPEN3_REQ,
  SSVC_TYPE_ROOM_OPEN4_REQ,
  SSVC_TYPE_ROOM_INVITE_REQ,
  SSVC_TYPE_ROOM_INFO_ROOM,
  SSVC_TYPE_ROOM_INFO_RES,
  SSVC_TYPE_ROOM_INFO_REQ,
  SSVC_TYPE_ROOM_INFO_USER,
  SSVC_TYPE_ROOM_INFO_USER2,
  SSVC_TYPE_ROOM_EXIT_REQ,
  SSVC_TYPE_ROOM_EXIT_ALL_REQ,
  SSVC_TYPE_ROOM_EXIT_FORCING_REQ,
  SSVC_TYPE_ROOM_USER_STATUS_REQ,
  SSVC_TYPE_ROOM_UPD_REQ,
  SSVC_TYPE_ROOM_INFO_UPD_REQ,
  SSVC_TYPE_ROOM_FONT_UPD_REQ
} from '../types/service';
import {
  RoomOpenRequest,
  encodeRoomOpen,
  decodeRoomOpen,
  RoomOpenResponse,
  RoomOpen2Request,
  RoomOpen2Response,
  decodeRoomOpen2,
  encodeRoomOpen2,
  RoomOpen3Request,
  RoomOpen3Response,
  encodeRoomOpen3,
  decodeRoomOpen3,
  RoomOpen4Request,
  RoomOpen4Response,
  encodeRoomOpen4,
  decodeRoomOpen4
} from '../models/open';
import {
  RoomInviteRequest,
  RoomInviteResponse,
  encodeRoomInvite,
  decodeRoomInvite
} from '../models/invite';
import {
  RoomInfoRequest,
  RoomInfoResponse,
  RoomInfoDataResponse,
  RoomUserShortDataResponse,
  RoomUserDataResponse,
  encodeRoomInfo,
  decodeRoomInfoData,
  decodeRoomInfo,
  decodeRoomUserShortData,
  decodeRoomUserData,
  RoomUserStatusOfflineRequest,
  RoomUserStatusOfflineResponse,
  encodeRoomUserStatusOffline,
  decodeRoomUserStatusOffline
} from '../models/info';
import {
  RoomExitRequest,
  RoomExitResponse,
  encodeRoomExit,
  decodeRoomExit,
  RoomExitAllRequest,
  encodeRoomAllExit,
  RoomExitForcingRequest,
  RoomExitForcingResponse,
  encodeRoomExitForcing,
  decodeRoomExitForcing
} from '../models/exit';
import {
  RoomUpdateRequest,
  RoomUpdateResponse,
  encodeRoomUpdate,
  decodeRoomUpdate,
  RoomUpdateTimerSetRequest,
  RoomUpdateTimerSetResponse,
  encodeRoomUpdateTimerSet,
  decodeRoomUpdateTimerSet,
  RoomUpdateFontRequest,
  RoomUpdateFontResponse,
  encodeRoomUpdateFont,
  decodeRoomUpdateFont
} from '../models/update';

@Injectable({
  providedIn: 'root'
})
export class RoomProtocolService {
  constructor(private protocolService: ProtocolService) {}

  public roomOpen(req: RoomOpenRequest): Observable<RoomOpenResponse> {
    return this.protocolService
      .call(SVC_TYPE_ROOM, SSVC_TYPE_ROOM_OPEN_REQ, ...encodeRoomOpen(req))
      .pipe(
        take(1),
        map(res => decodeRoomOpen(res))
      );
  }
  public roomOpen2(req: RoomOpen2Request): Observable<RoomOpen2Response> {
    return this.protocolService
      .call(SVC_TYPE_ROOM, SSVC_TYPE_ROOM_OPEN2_REQ, ...encodeRoomOpen2(req))
      .pipe(
        take(1),
        map(res => decodeRoomOpen2(res))
      );
  }
  public roomOpen3(req: RoomOpen3Request): Observable<RoomOpen3Response> {
    return this.protocolService
      .call(SVC_TYPE_ROOM, SSVC_TYPE_ROOM_OPEN3_REQ, ...encodeRoomOpen3(req))
      .pipe(
        take(1),
        map(res => decodeRoomOpen3(res))
      );
  }
  public roomOpen4(req: RoomOpen4Request): Observable<RoomOpen4Response> {
    return this.protocolService
      .call(SVC_TYPE_ROOM, SSVC_TYPE_ROOM_OPEN4_REQ, ...encodeRoomOpen4(req))
      .pipe(
        take(1),
        map(res => decodeRoomOpen4(res))
      );
  }

  public roomInvite(req: RoomInviteRequest): Observable<RoomInviteResponse> {
    return this.protocolService
      .call(SVC_TYPE_ROOM, SSVC_TYPE_ROOM_INVITE_REQ, ...encodeRoomInvite(req))
      .pipe(
        take(1),
        map(res => decodeRoomInvite(res))
      );
  }

  public roomInfo(
    req: RoomInfoRequest
  ): Observable<
    | RoomInfoResponse
    | RoomInfoDataResponse
    | RoomUserShortDataResponse
    | RoomUserDataResponse
  > {
    return this.protocolService
      .call(SVC_TYPE_ROOM, SSVC_TYPE_ROOM_INFO_REQ, ...encodeRoomInfo(req))
      .pipe(
        takeWhile(res => SSVC_TYPE_ROOM_INFO_RES !== res.subServiceType, true),
        map(res => {
          if (SSVC_TYPE_ROOM_INFO_ROOM === res.subServiceType) {
            return decodeRoomInfoData(res);
          } else if (SSVC_TYPE_ROOM_INFO_USER === res.subServiceType) {
            return decodeRoomUserShortData(res);
          } else if (SSVC_TYPE_ROOM_INFO_USER2 === res.subServiceType) {
            return decodeRoomUserData(res);
          }

          return decodeRoomInfo(res);
        })
      );
  }
  public roomUserStatusOffline(
    req: RoomUserStatusOfflineRequest
  ): Observable<RoomUserStatusOfflineResponse> {
    return this.protocolService
      .call(
        SVC_TYPE_ROOM,
        SSVC_TYPE_ROOM_USER_STATUS_REQ,
        ...encodeRoomUserStatusOffline(req)
      )
      .pipe(
        map(res => {
          take(1);
          return decodeRoomUserStatusOffline(res);
        })
      );
  }

  public roomExit(req: RoomExitRequest): Observable<RoomExitResponse> {
    return this.protocolService
      .call(SVC_TYPE_ROOM, SSVC_TYPE_ROOM_EXIT_REQ, ...encodeRoomExit(req))
      .pipe(
        map(res => {
          take(1);
          return decodeRoomExit(res);
        })
      );
  }
  public roomExitAll(req: RoomExitAllRequest): Observable<void> {
    return this.protocolService
      .call(
        SVC_TYPE_ROOM,
        SSVC_TYPE_ROOM_EXIT_ALL_REQ,
        ...encodeRoomAllExit(req)
      )
      .pipe(
        map(res => {
          take(1);
        })
      );
  }
  public roomExitForcing(
    req: RoomExitForcingRequest
  ): Observable<RoomExitForcingResponse> {
    return this.protocolService
      .call(
        SVC_TYPE_ROOM,
        SSVC_TYPE_ROOM_EXIT_FORCING_REQ,
        ...encodeRoomExitForcing(req)
      )
      .pipe(
        map(res => {
          take(1);
          return decodeRoomExitForcing(res);
        })
      );
  }

  public roomUpdate(req: RoomUpdateRequest): Observable<RoomUpdateResponse> {
    return this.protocolService
      .call(SVC_TYPE_ROOM, SSVC_TYPE_ROOM_UPD_REQ, ...encodeRoomUpdate(req))
      .pipe(
        map(res => {
          take(1);
          return decodeRoomUpdate(res);
        })
      );
  }
  public roomUpdateTimerSet(
    req: RoomUpdateTimerSetRequest
  ): Observable<RoomUpdateTimerSetResponse> {
    return this.protocolService
      .call(
        SVC_TYPE_ROOM,
        SSVC_TYPE_ROOM_INFO_UPD_REQ,
        ...encodeRoomUpdateTimerSet(req)
      )
      .pipe(
        map(res => {
          take(1);
          return decodeRoomUpdateTimerSet(res);
        })
      );
  }
  public roomUpdateFont(
    req: RoomUpdateFontRequest
  ): Observable<RoomUpdateFontResponse> {
    return this.protocolService
      .call(
        SVC_TYPE_ROOM,
        SSVC_TYPE_ROOM_FONT_UPD_REQ,
        ...encodeRoomUpdateFont(req)
      )
      .pipe(
        map(res => {
          take(1);
          return decodeRoomUpdateFont(res);
        })
      );
  }
}
