import { asyncDelay, onHomeworkFinish, RandomInt } from "../script/util";
import { MyCocosSceneComponent } from "../script/MyCocosSceneComponent";
import { GameServer } from "../network/Server";
import { NetworkHelper } from "../network/NetworkHelper";

const { ccclass, property } = cc._decorator;

@ccclass
export default class SceneComponent extends MyCocosSceneComponent {

  addPreloadImage() {
    this._imageResList.push({ url: this.data.audioOnEnterMatch });
    this._imageResList.push({ url: this.data.audioOnEnterGame });
    this._imageResList.push({ url: this.data.auidoDuringGame });
    this._imageResList.push({ url: this.data.audioOnFirstTest });
    this._imageResList.push({ url: this.data.audioDuringTest });
    this._imageResList.push({ url: this.data.audioRight });
    this._imageResList.push({ url: this.data.audioWrong });
    this._imageResList.push({ url: this.data.audioWinInTime });
    this._imageResList.push({ url: this.data.audioLossInTime });
    this._imageResList.push({ url: this.data.audioWinOutTime });
    this._imageResList.push({ url: this.data.audioLossOutTime });
    this._imageResList.push({ url: this.data.audioAfterGame });
  }

  onLoadEnd() {
    this.initData();
    this.initView();
  }

  playerId;
  isRoomOwner;
  server: GameServer;
  networkHelper: NetworkHelper;
  loadingLayer: any;
  _status: any;
  async initData() {
    this._status = {
      round: 0,// 0: 未开始, 1: 回合1, 2: 回合2, 3: 已结束
      boats: [],
    };
    for (let i = 0; i < 16; i++) {
      this._status.boats.push({
        show: false,
        player1: '', // 'right', 'wrong', ''
        player2: '',
      });
    }

    this.networkHelper = new NetworkHelper();
    this.loadingLayer = cc.find('Canvas/LoadingLayer').getComponent('OP_Boardgame_L3R4_LoadingLayer');
    this.loadingLayer.setMaxPlayerNumber(2);
    this.initNetworkListener();
    do {
      this.playerId = await this.networkHelper.init("OP_Boardgame_L3R4", 2);
      if (this.playerId === null) {
        console.log("onDestroy");
        this.networkHelper.onDestroy();
      }
      console.log("this.playerId = " + this.playerId);
    } while (this.playerId === null);

    const room = this.networkHelper.room;
    if (this.playerId == room.roomInfo.owner) {
      console.log("房主");
      this.isRoomOwner = true;
      const serverParams = {
        testDuration: this.data.testDuration,
        roundTime: this.data.roundTime,
      };
      this.server = new GameServer(2, this.networkHelper, serverParams);
      await this.networkHelper.startFrameSync();
      this.server.addPlayer({
        uuid: this.playerId,
        isAI: false,
      });
      this.server.startMatch();
    }
    await this.initHeadImgAndName();
  }

  loadPlayerHeadImage() {
    const player1 = cc.find("Canvas/bg/frame/headImg");
    const player2 = cc.find("Canvas/bg/frame2/headImg");
    this.allPlayerList.forEach((player) => {
      this.getSpriteFrimeByUrl(player.headUrl, (sf) => {
        if (player.color == "Red") {
          player1.getComponent(cc.Sprite).spriteFrame = sf;
          player1.width = 111;
          player1.height = 111;
        } else {
          player2.getComponent(cc.Sprite).spriteFrame = sf;
          player2.width = 111;
          player2.height = 111;
        }
      });
    });
  }

  allPlayerList: Array<any> = [];
  initNetworkListener() {
    this.networkHelper.on("playerJoin", (event) => {
      if (this.server) {
        this.server.onPlayerJoin({ data: { joinPlayerId: event.playerId } });
      }
      cc.log("playerJoin" + JSON.stringify(event));
    });
    this.networkHelper.on("playerLeave", (event) => {
      cc.log("playerLeave" + JSON.stringify(event));
      cc.find("Canvas/PlayerOffLineLayer").active = true;
      this.networkHelper.leaveRoom();
    });
    this.networkHelper.on("playerOffLine", (event) => {
      cc.find("Canvas/PlayerOffLineLayer").active = true;
      this.networkHelper.leaveRoom();
      this.log("playerOffLine" + JSON.stringify(event));
    });
    this.networkHelper.on("gameStart", (event) => { });
    this.networkHelper.on("frameEvent", (event) => {
      event.data.frame.items.forEach(async (item) => {
        if (this.server) {
          this.server.onFrameEvent(item.data);
        }

        if (item.data.type.startsWith('SERVER_')) {
          console.log(`${item.data.type}: ${JSON.stringify(item.data)}`);
        }

        if (item.data.type == "SERVER_allPlayerInfo") {
          item.data.playerData.forEach((player) => {
            if (
              !this.allPlayerList.find(
                (listPlayer) => listPlayer.uuid == player.uuid
              )
            ) {
              this.allPlayerList.push({
                uuid: player.uuid,
                name: player.name,
                color: player.color,
                headUrl: player.headUrl,
              });
            }

            this.loadPlayerHeadImage();

            this.loadingLayer.addPlayer(
              player.name,
              player.uuid == this.playerId,
              player.headUrl,
              player.uuid
            );
          });
        } else if (item.data.type == "SERVER_updateStatus") {
          const serverStatus = JSON.parse(JSON.stringify(item.data.status));
          serverStatus.boats.forEach((boat, index) => {
            this._status.boats[index] = { ...boat };
          });
          if (this._status.round == 0 && serverStatus.round == 1) {
            this._status.round = serverStatus.round;
            this.coolCatSay(this.data.audioOnEnterGame);
            this.initCountDown(this.onTimerFinish.bind(this));
            this.initBoats();
          } else {
            this._status.round = serverStatus.round;
            this.updateBoats();
          }
          this.updateProgress();
        } else if (item.data.type == "SERVER_playerWin") {
          if (item.data.winnerId == this.playerId) {
            this.showWin();
            onHomeworkFinish({ isWin: true });
          } else {
            this.showLoss();
            onHomeworkFinish({ isWin: false });
          }
        }
      });
    });
  }

  showWin() {
    if (this._status.boats.every(boat => boat[this.playerId])) {
      this.coolCatSay(this.data.audioWinInTime);
    } else {
      this.coolCatSay(this.data.audioWinOutTime);
    }
    const youWin = cc.find('Canvas/bg/youWin');
    youWin.active = true;
    const bg = cc.find('Canvas/bg/youWin/bg');
    cc.tween(bg)
      .set({ opacity: 0 })
      .to(0.5, { opacity: 100 })
      .start();
    const frame = cc.find('Canvas/bg/youWin/frame');
    frame.scale = bg.width / frame.width;
  }
  showLoss() {
    if (this._status.boats.every(boat => boat[this.playerId])) {
      this.coolCatSay(this.data.audioLossInTime);
    } else {
      this.coolCatSay(this.data.audioLossOutTime);
    }
    const youLoss = cc.find('Canvas/bg/youLoss');
    youLoss.active = true;
    const bg = cc.find('Canvas/bg/youLoss/bg');
    cc.tween(bg)
      .set({ opacity: 0 })
      .to(0.5, { opacity: 100 })
      .start();
    const frame = cc.find('Canvas/bg/youLoss/frame');
    frame.scale = bg.width / frame.width;
  }

  async initHeadImgAndName() {
    const playerInfo: any = {
      playerHeadUrl: "http://staging-teach.cdn.ireadabc.com/0751c28419a0e8ffb1f0e84435b081ce.png",
      playerName: "test",
    };
    const middleLayer = cc.find("middleLayer");
    if (middleLayer) {
      const middleLayerComponent = middleLayer.getComponent("middleLayer");
      if (middleLayerComponent.getUserInfo && middleLayerComponent.getHeadUrl) {
        const kidInfo = await middleLayerComponent.getUserInfo();
        const playerHeadUrl = middleLayerComponent.getHeadUrl(kidInfo.avatar_url || kidInfo.id);
        playerInfo.playerHeadUrl = playerHeadUrl;
        playerInfo.playerName = kidInfo.nick_name || '拼读达人';
      }
    }
    this.networkHelper.sendFrame({
      type: "playerInfo",
      playerId: this.playerId,
      playerName: playerInfo.playerName,
      playerHeadUrl: playerInfo.playerHeadUrl,
    });
  }

  async initView() {
    this.coolCatSay(this.data.audioOnEnterMatch);
    this.initProgress();
    this.initBubbles();
    this.initLoadingLayer();
  }

  initLoadingLayer() {
    const LoadingLayer = cc.find('Canvas/LoadingLayer');
    LoadingLayer.active = true;
  }

  onTimerFinish() {
    // this._status.round = 2;
  }

  startQuestionFirstTime = true;
  async startQuestion(idx: number) {
    this.showTestFrame();
    this.showTestImage(idx);
    const words = [
      'toad', 'new', 'coat', 'boot',
      'yellow', 'fruit', 'cube', 'window',
      'moon', 'zoo', 'elbow', 'bow',
      'suit', 'boat', 'blue', 'goat'
    ];

    this.startTest(words[idx]);

    if (this.startQuestionFirstTime) {
      this.startQuestionFirstTime = false;
      await this.testCoolCatSay(this.data.audioOnFirstTest);
    }
    this.testCoolCatSay(this.data.startAudio);
    await this.testCountDown(this.data.testDuration);
    const score = await this.stopTest();
    if (score > 80) {
      await this.showGoodJob();
      this.networkHelper.sendFrame({
        type: "right",
        playerId: this.playerId,
        boatIdx: idx
      });
    } else {
      await this.showNiceTry();
      this.networkHelper.sendFrame({
        type: "wrong",
        playerId: this.playerId,
        boatIdx: idx
      });
    }
    this.hideTestFrame();
  }

  startTest(word) {
    (<any>window).courseware?.startTest(word);
  }

  stopTest() {
    return new Promise<number>((resolve, reject) => {
      if ((<any>window).courseware) {
        (<any>window).courseware.stopTest((res) => {
          if (typeof (res) == 'string') {
            resolve(parseInt(JSON.parse(res).result.overall));
          } else {
            resolve(parseInt(res.result.overall));
          }
        });
      } else {
        resolve(Math.random() > 0.5 ? 100 : 0);
      }
    });
  }

  showTestImage(idx: number) {
    const img = cc.find(`images/img_${idx}`);
    const testImg = cc.find('Canvas/testBg/img_bg/img');
    testImg.getComponent(cc.Sprite).spriteFrame = img.getComponent(cc.Sprite).spriteFrame;
    testImg.scale = Math.min(
      (testImg.parent.width * 0.6) / testImg.width,
      (testImg.parent.height * 0.6) / testImg.height
    );
  }
  showTestFrame() {
    const testBg = cc.find('Canvas/testBg');
    testBg.active = true;
    const result = cc.find('Canvas/testBg/result');
    result.active = false;
    const bg = cc.find('Canvas/testBg/bg');
    cc.tween(bg)
      .set({ opacity: 0 })
      .to(0.5, { opacity: 100 })
      .start();
  }
  hideTestFrame() {
    const testBg = cc.find('Canvas/testBg');
    testBg.active = false;
  }
  testCoolCatSay(audioUrl: string) {
    return new Promise((resolve) => {
      const coolcat = cc.find('Canvas/testBg/coolcatbg/coolcat');
      const display = coolcat.getComponent(dragonBones.ArmatureDisplay);
      display.playAnimation('begin', -1);
      this.playAudioByUrl(audioUrl, () => {
        display.playAnimation('normal', -1);
        resolve(null);
      });
    });
  }
  coolCatSay(audioUrl: string) {
    return new Promise((resolve) => {
      const coolCat = cc.find('Canvas/bg/coolCatBase/coolCat');
      const display = coolCat.getComponent(dragonBones.ArmatureDisplay);
      display.playAnimation('talk', -1);
      this.playAudioByUrl(audioUrl, () => {
        display.playAnimation('normal', -1);
        resolve(null);
      });
    });
  }

  async testCountDown(time = 5) {
    return new Promise((resolve) => {
      const countDown = cc.find('Canvas/testBg/clock/countDown');
      const countDownComponent = countDown.getComponent(cc.Sprite);
      cc.tween(countDown)
        .set({ opacity: 150 })
        .to(time * 0.7, { opacity: 255 })
        .start();
      cc.tween(countDownComponent)
        .set({ fillRange: 1 })
        .to(time, { fillRange: 0 })
        .call(() => {
          resolve(null);
        })
        .start();
    });
  }

  showGoodJob() {
    return new Promise((resolve) => {
      if (this.data.audioRight) {
        this.playAudioByUrl(this.data.audioRight);
      } else {

      }
      const result = cc.find('Canvas/testBg/result');
      result.active = true;
      const goodjob = cc.find('Canvas/testBg/result/goodjob');
      goodjob.active = true;
      const niceTry = cc.find('Canvas/testBg/result/niceTry');
      niceTry.active = false;
      const bg = cc.find('Canvas/testBg/result/bg');
      bg.stopAllActions();
      cc.tween(bg)
        .by(0.5, { angle: 90 })
        .call(() => {
          result.active = false;
          goodjob.active = false;
        })
        .call(resolve)
        .start();
    });
  }

  showNiceTry() {
    return new Promise((resolve) => {
      this.playAudioByUrl(this.data.audioWrong);
      const result = cc.find('Canvas/testBg/result');
      result.active = true;
      const goodjob = cc.find('Canvas/testBg/result/goodjob');
      goodjob.active = false;
      const niceTry = cc.find('Canvas/testBg/result/niceTry');
      niceTry.active = true;
      const bg = cc.find('Canvas/testBg/result/bg');
      bg.stopAllActions();
      cc.tween(bg)
        .by(0.5, { angle: 90 })
        .call(() => {
          result.active = false;
          niceTry.active = false;
        })
        .call(resolve)
        .start();
    });
  }

  initBoats() {
    const words = [
      'toad', 'new', 'coat', 'boot',
      'yellow', 'fruit', 'cube', 'window',
      'moon', 'zoo', 'elbow', 'bow',
      'suit', 'boat', 'blue', 'goat'
    ];

    for (let i = 0; i < 16; i++) {
      const boatNode = cc.find(`Canvas/bg/boats/boat_${i}`);
      boatNode.removeComponent(cc.Sprite);
      boatNode.removeAllChildren();

      if (this._status.boats[i].show) {
        const boat = cc.instantiate(cc.find('boat'));
        boat.parent = boatNode;
        boat.name = 'boat';
        boat.setPosition(2500 + boat.parent.x, 0);
        const label = boat.getChildByName('label');
        label.getComponent(cc.Label).string = words[i];
        const image = cc.find(`images/img_${i}`);
        const icon = boat.getChildByName('icon');
        icon.getComponent(cc.Sprite).spriteFrame = image.getComponent(cc.Sprite).spriteFrame;

        const btn = boat.getChildByName('btn');
        btn.on('click', () => {
          this.startQuestion(i);
        });

        const phone = boat.getChildByName('phone');
        cc.tween(boat)
          .delay(2 + Math.random() * 2)
          .to(2, { x: 0 }, { easing: 'quadOut' })
          .call(() => {
            label.active = false;
            phone.active = true;
          })
          .start();
      }
    }
  }

  initOtherBoats() {
    const words = [
      'toad', 'new', 'coat', 'boot',
      'yellow', 'fruit', 'cube', 'window',
      'moon', 'zoo', 'elbow', 'bow',
      'suit', 'boat', 'blue', 'goat'
    ];

    for (let i = 0; i < 16; i++) {
      const boatNode = cc.find(`Canvas/bg/boats/boat_${i}`);
      if (!this._status.boats[i].show) {
        const boat = cc.instantiate(cc.find('boat'));
        boat.parent = boatNode;
        boat.name = 'boat';
        boat.setPosition(2500 + boat.parent.x, 0);
        const label = boat.getChildByName('label');
        label.getComponent(cc.Label).string = words[i];
        const image = cc.find(`images/img_${i}`);
        const icon = boat.getChildByName('icon');
        icon.getComponent(cc.Sprite).spriteFrame = image.getComponent(cc.Sprite).spriteFrame;

        const btn = boat.getChildByName('btn');
        btn.on('click', () => {
          this.startQuestion(i);
        });

        const phone = boat.getChildByName('phone');
        cc.tween(boat)
          .delay(2 + Math.random() * 2)
          .to(2, { x: 0 }, { easing: 'quadOut' })
          .call(() => {
            label.active = false;
            phone.active = true;
          })
          .start();
      }
    }
  }

  updateBoats() {
    for (let i = 0; i < 16; i++) {
      const boat = cc.find(`Canvas/bg/boats/boat_${i}/boat`);
      const boatData = this._status.boats[i];
      if (!boat) {
        continue;
      }
      if (boatData[this.playerId] == 'wrong') {
        boat.active = true;
        const lock = boat.getChildByName('lock');
        lock.active = true;
        const btn = boat.getChildByName('btn');
        btn.active = false;
        const phone = boat.getChildByName('phone');
        phone.active = false;
      } else if (boatData[this.playerId] == 'right') {
        const btn = boat.getChildByName('btn');
        btn.active = false;
        const phone = boat.getChildByName('phone');
        phone.active = false;
        if (boat.active == true && !boat['tween']) {
          boat['tween'] = cc.tween(boat)
            .to(0.5, { scale: 2, opacity: 0 })
            .call(() => {
              boat.active = false;
              boat['tween'] = null;
            })
            .start();
        }
      }
    }
  }

  updateProgress() {
    let leftRightNumber = 0;
    let leftWrongNumber = 0;
    let rightRightNumber = 0;
    let rightWrongNumber = 0;
    for (let i = 0; i < 16; i++) {
      const boatData = this._status.boats[i];
      if (boatData[this.playerId] == 'right') {
        if (this.isRoomOwner) {
          leftRightNumber++;
        } else {
          rightRightNumber++;
        }
      } else if (boatData[this.playerId] == 'wrong') {
        if (this.isRoomOwner) {
          leftWrongNumber++;
        } else {
          rightWrongNumber++;
        }
      }
      Object.keys(boatData)
        .filter((key) => key != this.playerId)
        .filter((key) => key != 'show')
        .forEach((key) => {
          if (boatData[key] == 'right') {
            if (this.isRoomOwner) {
              rightRightNumber++;
            } else {
              leftRightNumber++;
            }
          } else if (boatData[key] == 'wrong') {
            if (this.isRoomOwner) {
              rightWrongNumber++;
            } else {
              leftWrongNumber++;
            }
          }
        });
    }
    let shakeLeft = false;
    let shakeRight = false;
    for (let i = 0; i < 16; i++) {
      const box = cc.find(`Canvas/bg/progress/layout/box_${i}`);
      const lock = box.getChildByName('lock');
      if (15 - i < leftWrongNumber) {
        if (lock.active == false) {
          lock.active = true;
          shakeLeft = true;
        }
      }
      if (i < leftRightNumber) {
        if (!box['changeColor']) {
          this.changeNodeColor(box);
          box['changeColor'] = true;
          shakeLeft = true;
        }
      } else {
        this.changeNodeGray(box);
      }
    }
    for (let i = 0; i < 16; i++) {
      const box = cc.find(`Canvas/bg/progress2/layout/box_${i}`);
      box.setPosition(2.5, 0);
      const lock = box.getChildByName('lock');
      if (15 - i < rightWrongNumber) {
        if (lock.active == false) {
          lock.active = true;
          shakeRight = true;
        }
      }
      if (i < rightRightNumber) {
        if (!box['changeColor']) {
          this.changeNodeColor(box);
          box['changeColor'] = true;
          shakeRight = true;
        }
      } else {
        this.changeNodeGray(box);
      }
    }
    if (shakeLeft) {
      this.shakeProgress('Left');
    }
    if (shakeRight) {
      this.shakeProgress('Right');
    }

    if (this.isRoomOwner) {
      if (leftRightNumber + leftWrongNumber == 8) {
        if (shakeLeft) {
          this.initOtherBoats();
        }
      }
    } else {
      if (rightRightNumber + rightWrongNumber == 8) {
        if (shakeRight) {
          this.initOtherBoats();
        }
      }
    }
  }

  shakeProgress(side: string) {
    let node;
    let sideNumber = 1;
    if (side == 'Left') {
      node = cc.find('Canvas/bg/progress');
    } else {
      node = cc.find('Canvas/bg/progress2');
      sideNumber = -1;
    }
    if (node['tween']) {
      return;
    }
    node['tween'] = cc.tween(node)
      .to(0.1, { scaleY: 1.05, scaleX: 1.05 * sideNumber })
      .to(0.1, { scaleY: 1.00, scaleX: 1.00 * sideNumber })
      .call(() => {
        node['tween'] = null;
      })
      .start();
  }

  initCountDown(callback: Function) {
    const progressTopBar = cc.find('Canvas/bg/progressTop/progressTopBar');
    const spriteComponent = progressTopBar.getComponent(cc.Sprite);
    cc.tween(spriteComponent)
      .to(this.data.roundTime, { fillRange: 0 })
      .call(callback)
      .start();
  }
  initProgress() {
    const layoutLeft = cc.find('Canvas/bg/progress/layout');
    for (let i = 0; i < 16; i++) {
      const box = cc.instantiate(cc.find('box_green'));
      box.setPosition(2.5, 0);
      box.parent = layoutLeft;
      box.name = `box_${i}`;
      this.changeNodeGray(box);
    }
    const layoutRight = cc.find('Canvas/bg/progress2/layout');
    for (let i = 0; i < 16; i++) {
      const box = cc.instantiate(cc.find('box_orange'));
      box.setPosition(2.5, 0);
      box.parent = layoutRight;
      box.name = `box_${i}`;
      this.changeNodeGray(box);
    }
  }

  initBubbles() {
    const mask1 = cc.find('Canvas/bg/progress/mask');
    this.createBubbles(mask1);
    const mask2 = cc.find('Canvas/bg/progress2/mask');
    this.createBubbles(mask2);
  }

  createBubbles(node: cc.Node) {
    cc.tween(this.node)
      .call(() => {
        this.createOneBubble(node);
      })
      .delay(0.7)
      .union()
      .repeatForever()
      .start();
  }
  createOneBubble(node: cc.Node) {
    const sprite = cc.find('bubble');
    const bubble = cc.instantiate(sprite);
    bubble.parent = node;
    cc.tween(bubble)
      .set({
        x: RandomInt(node.width / -2, node.width / 2),
        y: RandomInt(0, -20) - node.height / 2,
        scale: Math.random() * 0.3 + 0.1,
      })
      .to(RandomInt(15, 18), {
        y: node.height,
        x: RandomInt(node.width / -2, node.width / 2),
        scale: Math.random() * 0.1 + 0.2,
      })
      .removeSelf()
      .start();
  }
  changeNodeGray(node) {
    if (!node) {
      return;
    }
    let material = cc.Material.createWithBuiltin('2d-gray-sprite', 0);
    const sprite = node.getComponent(cc.Sprite);
    let variant1 = cc.MaterialVariant.create(material, sprite);
    sprite.setMaterial(0, variant1);
  }
  changeNodeColor(node) {
    if (!node) {
      return;
    }
    let material = cc.Material.createWithBuiltin('2d-sprite', 0);
    const sprite = node.getComponent(cc.Sprite);
    let variant1 = cc.MaterialVariant.create(material, sprite);
    sprite.setMaterial(0, variant1);
  }

  playEffect(effectName) {
    const effectNode = cc.find(`res/${effectName}`);
    const clip = effectNode.getComponent(cc.AudioSource).clip;
    cc.audioEngine.play(clip, false, 0.8);
  }
}
