import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { Subject } from 'rxjs';
import { first, takeUntil } from 'rxjs/operators';
import { environment } from '../../../environments/environment';
import { AvalonStage, AVALON_EVIL_KNOWN_TO_EVIL, AVALON_EVIL_KNOWN_TO_MERLIN, AVALON_KNOWN_TO_PERCIVAL, GameInfo, Player, PlayerAction, QuestInfo, Role, RoleInfo } from '../../entity/Avalon';
import { AvalonService } from '../../services/avalon.service';
import * as signalR from "@microsoft/signalr"
import { StringUtils } from '../../utilities/string-utils';
import { ActivatedRoute } from '@angular/router';
import { MsgBoxService } from '../../services/msg-box.service';
import { UuidUtils } from '../../utilities/uuid-utils';
import { ObjectUtils } from '../../utilities/object-utils';
import { NbToastrService } from '@nebular/theme';
import { AvalonBase } from './avalonBase';
import { HeaderService } from '../../services/header.service';
import { ADIcon, ADButtons } from '../../ui/alert-dlg/alert-dlg.model';

const minimumPlayers = 5;
const maximumPlayers = 10;
const players_good: number[] = [3, 4, 4, 5, 6, 6];
const players_evil: number[] = [2, 2, 3, 3, 3, 4];
const teamSize: number[][] = [
  [2, 3, 2, 3, 3],
  [2, 3, 4, 3, 4],
  [2, 3, 3, 4, 4],
  [3, 4, 4, 5, 5],
  [3, 4, 4, 5, 5],
  [3, 4, 4, 5, 5]
];
const fourthQuestNeed2Failed = 7;
const SIGNAL_R_URL = (id: string = null) => { return `${environment.signalRUrl}/${id}Hub` }
//const SIGNAL_R_URL = (id: string = null) => { return `http://localhost:12071/hub` }
//const SIGNAL_R_URL = (id: string = null) => { return `http://golife.love:8088/${id}hub` }

@Component({
  selector: 'ngx-avalon',
  templateUrl: './avalon.component.html',
  styleUrls: ['./avalon.component.scss'],
  changeDetection: ChangeDetectionStrategy.Default,
})
export class AvalonComponent extends AvalonBase implements OnInit {

  AvalonStage = AvalonStage;
  cardFlipped: boolean = false;
  gameSize: number = 0;
  private destroy$: Subject<void> = new Subject<void>();

  private hubConnection: signalR.HubConnection;
  broadcastFromAPI$ = new Subject<Player>();
  refreshStage$ = new Subject<AvalonStage>();
  refreshPlayers$ = new Subject<Player[]>();


  constructor(
    private route: ActivatedRoute,
    private msgBoxService: MsgBoxService,
    private toastrService: NbToastrService,
    private headerService: HeaderService,
    protected cdRef: ChangeDetectorRef,
    protected avalonService: AvalonService,
  ) {
    super(cdRef, avalonService);
    // avalonService.refreshStage$.pipe(takeUntil(this.destroy$)).subscribe(result => {
    //   this.stage = result;

    // });
  }

  ngOnInit(): void {
    this.headerService.setHeader('Avalon - 阿瓦隆');


    this.avalonService.data = {
      stage: AvalonStage.JoinGame,
      players: [],
      roles: [
        {
          enabled: true,
          isGood: true,
          role: Role.Merlin,
          name: 'Merlin (梅林)',
          images: '',
          description: '好人- 梅林知道在場所有的壞東西<br>Merlin knows all the Evil players',
        },
        {
          enabled: true,
          isGood: true,
          role: Role.Percival,
          name: 'Percival (波希瓦)',
          images: '',
          description: '好人- 波希瓦知道梅林與魔甘娜<br>Percival\'s special power is knowledge of Merlin at the star of the game.',
        },
        {
          enabled: true,
          isGood: true,
          role: Role.ArthurKnight,
          name: 'Loyal Servant of Arthur (亞瑟王的忠貞小夥伴)',
          images: '',
          description: '好人- 亞瑟王的忠貞小夥伴<br>Loyal Servant of Arthur',
        },
        {
          enabled: true,
          isGood: false,
          role: Role.Morgana,
          name: 'Morgana (魔甘娜)',
          images: '',
          description: '壞人- 能假裝是梅林被波希瓦知道<br>She is on the side of evil. Her power is that she appears to be Merlin - revealing herself to Percival as Merlin.',
        },
        {
          enabled: true,
          isGood: false,
          role: Role.Assassin,
          name: 'Assassin (刺客)',
          images: '',
          description: '壞人- 能最後決定刺死誰',
        },
        {
          enabled: false,
          isGood: false,
          role: Role.Oberon,
          name: 'Oberon (奧伯龍)',
          images: '',
          description: '壞人- 不認識自己人也不被自己人認識的邊緣人<br>He is on the side of Evil. His special power is that he does not reveal himself to the other evil players, mor does he gain knowledge of the other evil players at the start of the game.',
        },
        {
          enabled: false,
          isGood: false,
          role: Role.Mordred,
          name: 'Mordred (默德里德)',
          images: '',
          description: '壞人- 不被梅林察覺<br>He is on the side of Evil. His special pwoer is that his identity is not revealed to Merlin at the start of the game.',
        },
        {
          enabled: true,
          isGood: false,
          role: Role.Minion,
          name: 'Minion (壞雜魚)',
          images: '',
          description: '壞人- 不要懷疑，你就是個雜魚<br>He is on the side of Evil.',
        },
      ],
      questInfos: []
    };

    this.route.url.pipe(first()).subscribe(url => {

      this.avalonService.isHost = url[0].path.toLowerCase() == 'avalonhost';
    });



    this.avalonService.broadcastFromSignalR$.pipe(takeUntil(this.destroy$)).subscribe(result => {
      this.broadcastFromSignalR(result.action, result.jsonString);
      //this.cdRef.detectChanges();
    });
    this.avalonService.applyCdChange$.pipe(takeUntil(this.destroy$)).subscribe(result => {
      this.cdRef.detectChanges();
    });
  }

  ngOnDestroy() {
    this.broadcastMe('LeaveGame');
    this.destroy$.next();
    this.destroy$.complete();
  }


  public get hasRole(): boolean {
    return this.me && this.me.role > 0;
  }




  //#region Actions


  getRoleInfoByRole(role: Role) {
    return this.avalonService.data.roles.find(r => r.role == role);
  }

  public showMyRole() {
    let roleInfo = this.getRoleInfoByRole(this.me.role);
    this.msgBoxService.show(roleInfo.name, { text: roleInfo.description, icon: ADIcon.INFO });
  }
  public showMySecret() {
    //let roleInfo = this.getRoleInfoByRole(this.me.role);

    let description = '';
    switch (this.me.role) {
      case Role.Merlin:

        description = `場上壞壞的有 ` + StringUtils.getHtmlBadges(this.players.filter(p => AVALON_EVIL_KNOWN_TO_MERLIN.includes(p.role)).map(p => p.username), 'danger');
        break;

      case Role.Morgana:
      case Role.Mordred:
      case Role.Minion:
      case Role.Assassin:
        description = `你的隊友是 ` + StringUtils.getHtmlBadges(this.players.filter(p => AVALON_EVIL_KNOWN_TO_EVIL.includes(p.role)).map(p => p.username), 'info');
        break;
      case Role.Percival:

        description = `他倆之中有梅林 ` + StringUtils.getHtmlBadges(this.players.filter(p => AVALON_KNOWN_TO_PERCIVAL.includes(p.role)).map(p => p.username), 'info');
        break;
      default:
        description = '想太多囉~ 你只是個雜魚~~';
        break;
    }

    this.msgBoxService.show('噓...', { text: description, icon: ADIcon.INFO });
  }

  private initializeQuestInfo() {
    this.avalonService.data.questInfos = [];

    for (let i = 0; i < 5; i++) {
      this.avalonService.data.questInfos.push(
        {
          seq: i + 1,
          teamSize: teamSize[this.gameSize][i],
          need2Failed: i == 3 && this.players.length >= fourthQuestNeed2Failed,
          teamMembers: [],
          success: 0,
          failed: 0
        } as QuestInfo
      )
    }
  }

  private rngAssignRoles() {
    let enabledRoles: Role[] = [];
    for (let i = 0; i < this.avalonService.data.players.length; i++) {
      const player = this.avalonService.data.players[i];
      player.role = Role.NA;
    }

    enabledRoles.push(...this.avalonService.data.roles.filter(r => r.enabled).map(r => r.role));
    while (enabledRoles.length < this.avalonService.data.players.length) {
      enabledRoles.push(Role.ArthurKnight);
    }
    while (this.avalonService.data.players.filter(p => p.role == Role.NA).length > 0) {
      let index = Math.floor(Math.random() * this.avalonService.data.players.filter(p => p.role == Role.NA).length);
      this.avalonService.data.players.filter(p => p.role == Role.NA)[index].role = enabledRoles.pop();
      //enabledRoles.push(Role.ArthurKnight);
    }


  }


  previousStage() {

    this.msgBoxService.show('上一棟', { text: '你確定?', icon: ADIcon.QUESTION, buttons: ADButtons.YesNo }).pipe(first()).subscribe(result => {
      if (result) {
        this.avalonService.data.stage -= 1;
        this.clearVoteStatus();
        this.broadcastAll();
      }
    });
  }

  nextStage() {
    switch (this.avalonService.data.stage) {
      case AvalonStage.JoinGame:
        if (this.players.length < 5) {
          this.msgBoxService.show("人數不足!", { text: "遊戲最少需要五人" });
          return;
        }

        this.gameSize = Math.max(0, this.players.length - 6);

        if (this.avalonService.data.roles.filter(r => r.enabled && false == r.isGood).length != players_evil[this.gameSize]) {
          this.msgBoxService.show("反派人數錯誤!", { text: "反派人數需要 " + players_evil[this.gameSize].toString() + " 人" });
          return;
        }

        this.initializeQuestInfo();

        if (this.avalonService.rngAssignRole) {
          this.rngAssignRoles();
          this.avalonService.data.stage = AvalonStage.PickTeammate;
        } else {
          this.avalonService.data.stage = AvalonStage.NewGame;
        }

        break;
      case AvalonStage.TeamVoteResult:
        let approve = this.players.filter(p => p.teamVote == true).length;
        if (approve > (this.players.length - approve)) {
          this.avalonService.data.stage = AvalonStage.QuestVote;
        } else {
          this.avalonService.data.stage = AvalonStage.PickTeammate;
        }
        break;

      case AvalonStage.QuestVoteResult:
        this.avalonService.data.stage = AvalonStage.PickTeammate;
        break;

      default:
        this.avalonService.data.stage += 1;
        break;
    }
    this.clearVoteStatus();
    this.broadcastAll();
  }

  broadcastFromSignalR(action, jsonString) {
    switch (action) {
      case 'LeaveGame':
        const index = this.players.indexOf(this.players.find(p => p.connectionId == jsonString));
        if (index > -1) {
          this.players.splice(index, 1);
          this.cdRef.detectChanges();
        }
        break;

      case 'RefreshPlayers':
        ObjectUtils.CopyValue(JSON.parse(jsonString), this.avalonService.data.players, [], true);
        //ObjectUtils.CopyValue(JSON.parse(jsonString), this.players);
        //this.cdRef.detectChanges();
        break;
      case 'RefreshStage':
        this.avalonService.data.stage = JSON.parse(jsonString);
        this.clearVoteStatus();
        break;
      case 'RefreshAll':
        //let newData = JSON.parse(jsonString) as GameInfo;
        //this.avalonService.data.stage = newData.stage;
        //this.avalonService.data.stage = newData.stage;
        //ObjectUtils.CopyValue(newData, this.avalonService.data, [], true);
        this.avalonService.data = JSON.parse(jsonString);
        break;
      case 'JoinGame':
      case 'VoteTeamMate':
      case 'VoteQuest':
      default:
        this.clonePlayerInfoFromJsonString(jsonString);
        this.detectMoveToNextStage(action);

        break;
    }

    this.cdRef.detectChanges();
  }

  clonePlayerInfoFromJsonString(jsonString) {
    let player = JSON.parse(jsonString);
    let existPlayer = this.players.find(p => p.connectionId == player.connectionId);

    if (existPlayer) {
      Object.assign(existPlayer, player)
    }
    else {
      this.players.push(player);
    }
    this.cdRef.detectChanges();
  }



  public get title(): string {
    switch (this.stage) {
      case AvalonStage.JoinGame:
        return '阿瓦隆-加入遊戲';
      case AvalonStage.NewGame:
        return '選擇抽到的腳色卡';
      case AvalonStage.PickTeammate:
        return '隊長提拔出任務人選';
      case AvalonStage.TeamVote:
        return '出任務人選投票階段';
      case AvalonStage.QuestVote:
        return '任務投票階段';
      case AvalonStage.QuestVoteResult:
      case AvalonStage.TeamVoteResult:
        return '投票結果';
        break;

      default:
        return '';
        break;
    }
  }

}
