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

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

import { ProtocolService } from '@ucap-webmessenger/protocol';
import {
  BuddyRequest,
  BuddyResponse,
  BuddyData,
  encodeBuddy,
  decodeBuddyData,
  decodeBuddy,
  decodeBuddyDetailData,
  BuddyDetailData
} from '../protocols/buddy';
import {
  SVC_TYPE_SYNC_BUDDY,
  SSVC_TYPE_SYNC_BUDDY_REQ,
  SSVC_TYPE_SYNC_BUDDY_RES,
  SSVC_TYPE_SYNC_BUDDY_DATA,
  SSVC_TYPE_SYNC_BUDDY2_REQ,
  SSVC_TYPE_SYNC_BUDDY2_RES,
  SSVC_TYPE_SYNC_BUDDY2_DATA,
  SVC_TYPE_SYNC_GROUP,
  SSVC_TYPE_SYNC_GROUP_REQ,
  SSVC_TYPE_SYNC_GROUP_RES,
  SSVC_TYPE_SYNC_GROUP_DATA,
  SSVC_TYPE_SYNC_GROUP_REQ2,
  SSVC_TYPE_SYNC_GROUP_RES2,
  SSVC_TYPE_SYNC_GROUP_DATA2,
  SVC_TYPE_SYNC_ROOM,
  SSVC_TYPE_SYNC_ROOM_REQ,
  SSVC_TYPE_SYNC_ROOM_RES,
  SSVC_TYPE_SYNC_ROOM_DATA,
  SSVC_TYPE_SYNC_ROOM_USER,
  SSVC_TYPE_SYNC_ROOM_USER2,
  SSVC_TYPE_SYNC_ROOM2_REQ,
  SSVC_TYPE_SYNC_ROOM2_RES,
  SSVC_TYPE_SYNC_ROOM2_DATA,
  SSVC_TYPE_SYNC_ROOM2_USER,
  SSVC_TYPE_SYNC_ROOM2_USER2
} from '../types/service';
import {
  GroupRequest,
  GroupResponse,
  GroupData,
  encodeGroup,
  decodeGroupDetailData,
  decodeGroup,
  decodeGroupData,
  GroupDetailData
} from '../protocols/group';
import {
  RoomRequest,
  RoomResponse,
  encodeRoom,
  decodeRoom,
  RoomUserData,
  RoomUserDetailData,
  RoomData,
  decodeRoomData,
  decodeRoomUserData,
  decodeRoomUserDataDetail
} from '../protocols/room';

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

  public buddy(req: BuddyRequest): Observable<BuddyResponse | BuddyData> {
    return this.protocolService
      .call(SVC_TYPE_SYNC_BUDDY, SSVC_TYPE_SYNC_BUDDY_REQ, ...encodeBuddy(req))
      .pipe(
        takeWhile(res => SSVC_TYPE_SYNC_BUDDY_RES !== res.subServiceType, true),
        map(res => {
          if (SSVC_TYPE_SYNC_BUDDY_DATA === res.subServiceType) {
            return decodeBuddyData(res);
          }

          return decodeBuddy(res);
        })
      );
  }

  public buddy2(
    req: BuddyRequest
  ): Observable<BuddyResponse | BuddyDetailData> {
    return this.protocolService
      .call(SVC_TYPE_SYNC_BUDDY, SSVC_TYPE_SYNC_BUDDY2_REQ, ...encodeBuddy(req))
      .pipe(
        takeWhile(
          res => SSVC_TYPE_SYNC_BUDDY2_RES !== res.subServiceType,
          true
        ),
        map(res => {
          if (SSVC_TYPE_SYNC_BUDDY2_DATA === res.subServiceType) {
            return decodeBuddyDetailData(res);
          }

          return decodeBuddy(res);
        })
      );
  }

  public group(req: GroupRequest): Observable<GroupResponse | GroupData> {
    return this.protocolService
      .call(SVC_TYPE_SYNC_GROUP, SSVC_TYPE_SYNC_GROUP_REQ, ...encodeGroup(req))
      .pipe(
        takeWhile(res => SSVC_TYPE_SYNC_GROUP_RES !== res.subServiceType, true),
        map(res => {
          if (SSVC_TYPE_SYNC_GROUP_DATA === res.subServiceType) {
            return decodeGroupData(res);
          }

          return decodeGroup(res);
        })
      );
  }

  public group2(
    req: GroupRequest
  ): Observable<GroupResponse | GroupDetailData> {
    return this.protocolService
      .call(SVC_TYPE_SYNC_GROUP, SSVC_TYPE_SYNC_GROUP_REQ2, ...encodeGroup(req))
      .pipe(
        takeWhile(
          res => SSVC_TYPE_SYNC_GROUP_RES2 !== res.subServiceType,
          true
        ),
        map(res => {
          if (SSVC_TYPE_SYNC_GROUP_DATA2 === res.subServiceType) {
            return decodeGroupDetailData(res);
          }

          return decodeGroup(res);
        })
      );
  }

  public room(
    req: RoomRequest
  ): Observable<RoomResponse | RoomData | RoomUserData | RoomUserDetailData> {
    return this.protocolService
      .call(SVC_TYPE_SYNC_ROOM, SSVC_TYPE_SYNC_ROOM_REQ, ...encodeRoom(req))
      .pipe(
        takeWhile(res => SSVC_TYPE_SYNC_ROOM_RES !== res.subServiceType, true),
        map(res => {
          if (SSVC_TYPE_SYNC_ROOM_DATA === res.subServiceType) {
            return decodeRoomData(res);
          } else if (SSVC_TYPE_SYNC_ROOM_USER === res.subServiceType) {
            return decodeRoomUserData(res);
          } else if (SSVC_TYPE_SYNC_ROOM_USER2 === res.subServiceType) {
            return decodeRoomUserDataDetail(res);
          }

          return decodeRoom(res);
        })
      );
  }

  public room2(
    req: RoomRequest
  ): Observable<RoomResponse | RoomData | RoomUserData | RoomUserDetailData> {
    return this.protocolService
      .call(SVC_TYPE_SYNC_ROOM, SSVC_TYPE_SYNC_ROOM2_REQ, ...encodeRoom(req))
      .pipe(
        takeWhile(res => SSVC_TYPE_SYNC_ROOM2_RES !== res.subServiceType, true),
        map(res => {
          if (SSVC_TYPE_SYNC_ROOM2_DATA === res.subServiceType) {
            return decodeRoomData(res);
          } else if (SSVC_TYPE_SYNC_ROOM2_USER === res.subServiceType) {
            return decodeRoomUserData(res);
          } else if (SSVC_TYPE_SYNC_ROOM2_USER2 === res.subServiceType) {
            return decodeRoomUserDataDetail(res);
          }

          return decodeRoom(res);
        })
      );
  }
}
