import DHFactory from "./DHFactory.js";
import config from "./config.js";

function callhook(eventName, data) {
  if (this.eventObj[eventName]) {
    this.eventObj[eventName].forEach((item) => {
      Promise.resolve().then(() => {
        item(data);
      });
    });
  }
}

class DH {
  constructor(spec) {
    this.eventObj = {};
    this.queue = [];
    this.renderQueue = [];
    this.interruptQueues = [];
    this.renderedQueue = [];
    this.renderingData = null;
    this.interruptQueue = [];
    this.runSpeech = null;
    this.sp = (spec && spec.sp) || "tencent";
    this.isInterrupted = false;
    this.isRunning = false;
    this.data = {};
    this.interruptData = {};
    this.isDriver = false;
  }

  async init(params) {
    if (this.sp === "baidu") {
      this.DHObject = DHFactory.getInstance(
        this.sp,
        Object.assign({}, config[this.sp], params)
      );
    } else if (this.sp === "tencent") {
      this.DHObject = DHFactory.getInstance(
        this.sp,
        Object.assign({}, config[this.sp], params)
      );
    }
    this.DHObject.addEventListener("renderInterrupted", (data) => {
      // if (this.isRunning) {
      this.isInterrupted = true;
      callhook.call(this, "renderInterrupted", data);
      // }
    });
    this.DHObject.addEventListener("next", () => {
      if (this.isDriver) callhook.call(this, "renderNext");
      console.warn(
        "nextIn",
        JSON.stringify(this.renderQueue),
        JSON.stringify(this.interruptQueue),
        JSON.stringify(this.queue)
      );

      if (this.isRunning && this.isDriver) this.next();
      else if (this.immediate) {
        callhook.call(this, "immediateCompleted");
        this.immediate = false;
      }
    });
    this.DHObject.addEventListener("initError", (data) => {
      callhook.call(this, "initError", data);
    });
    this.DHObject.addEventListener("renderError", (data) => {
      callhook.call(this, "renderError", data);
    });
    this.DHObject.addEventListener("renderBack", (data) => {
      // if (this.isRunning) {
      if (this.isDriver) callhook.call(this, "renderBack", data);
      // }
    });

    this.DHObject.addEventListener("textRenderError", (data) => {
      if (this.isDriver) callhook.call(this, "textRenderError", data);
    });

    this.DHObject.addEventListener("playError", (data) => {
      if (this.isDriver) callhook.call(this, "playError", data);
    });

    this.DHObject.addEventListener("webSocketError", (data) => {
      callhook.call(this, "webSocketError", data);
    });

    this.DHObject.addEventListener("videoError", (data) => {
      if (this.isDriver) callhook.call(this, "videoError", data);
    });

    this.DHObject.addEventListener("card", (data) => {
      // data.resources = this.isInterrupted ? this.interruptData : this.data;
      if (this.isDriver) callhook.call(this, "card", data);
    });

    this.DHObject.addEventListener("resources", (data) => {
      callhook.call(this, "resources", data);
    });

    this.DHObject.addEventListener("playing", (data) => {
      callhook.call(this, "playing", data);
    });

    this.DHObject.addEventListener("message", (data) => {
      callhook.call(this, "message", data);
    });

    this.DHObject.addEventListener("textRender", (data)=>{
      callhook.call(this, "textRender", data);
    })

    this.DHObject.initPlayer();

    this.DHObject.initMessage();

    return await new Promise((resolve, reject) => {
      this.DHObject.addEventListener("loaded", (data) => {
        console.log('loaded :>> ', data);
        resolve(data);
      });
    });
  }

  play(url) {
    this.DHObject.play(url);
  }

  mute(mute) {
    this.DHObject.mute(mute);
  }

  interrupt() {
    this.isInterrupted = true;
    this.DHObject.interrupt();
    this.render();
  }

  drive(data) {
    if (data.role === "master") {
      this.addInterruptData(data.nodes, true);
    } else {
      this.addRenderData(data.nodes);
    }
  }

  immediateDrive(data) {
    this.immediate = true
    this.DHObject.textRender(data)
  }

  addRenderData(data) {
    console.warn("addRenderData", JSON.stringify(data));
    this.renderQueue.push(...data);
    if (!this.isRunning) this.render();
  }

  addInterruptData(data, interrupt) {
    console.warn(
      "addInterruptData",
      JSON.stringify(data),
      JSON.stringify(interrupt)
    );
    if (interrupt) {
      this.interruptQueues.splice(0);
      this.interruptQueues.push(...data);
      this.DHObject.interrupt();
      this.render();
    } else {
      this.interruptQueues.push(...data);
      if (!this.isRunning) this.render();
    }
  }

  render() {
    // this.renderingData = null;
    console.warn(
      "render",
      JSON.stringify(this.renderQueue),
      JSON.stringify(this.interruptQueue),
      JSON.stringify(this.queue)
    );
    if (this.interruptQueues.length > 0) {
      this.isInterrupted = true;
      const data = this.interruptQueues[0];
      this.execute(data.nodeTitle, data.speech, data.data, data.display);
    } else if (this.renderQueue.length > 0) {
      this.isInterrupted = false;
      const data = this.renderQueue[0];
      // this.renderingData = { ...data };
      this.execute(data.nodeTitle, data.speech, data.data, data.display);
    } else {
      this.isRunning = false;
    }
  }

  execute(nodeName, speech, data, display) {
    this.isRunning = true;
    this.isDriver = true;
    if (this.isInterrupted) {
      this.interruptData = data;
      this.interruptQueue = [...speech];
    } else {
      this.data = data;
      this.queue = [...speech];
    }
    if (display) {
      this.DHObject.textRender(display);
    }
    callhook.call(this, "renderStart", {
      nodeName: nodeName,
      speech: speech,
      data: data,
      display: display,
    });
    this.exec();
  }

  exec(moreMessage) {
    if (this.isInterrupted) {
      if (this.interruptQueue.length > 0) {
        console.warn("interruptExec :>> ", JSON.stringify(this.interruptQueue));
        const data = this.interruptQueue.shift();
        if (data) {
          this.DHObject.exec(data, this.interruptData, moreMessage);
        }
      } else {
        const data = this.interruptQueues.shift();
        callhook.call(this, "renderCompleted", { nodeName: data.nodeTitle });
        this.redo();
      }
    } else {
      if (this.queue.length > 0) {
        console.warn("execQueue :>> ", JSON.stringify(this.queue));
        this.runSpeech = this.queue.shift();
        if (this.runSpeech) {
          this.DHObject.exec(this.runSpeech, this.data, moreMessage);
        }
      } else {
        this.isRunning = false;
        this.isInterrupted = false;
        this.runSpeech = null;
        this.data = {};
        // if (this.renderingData)
        //   this.renderedQueue.push({ ...this.renderingData });
        const data = this.renderQueue.shift();
        callhook.call(this, "renderCompleted", { nodeName: data.nodeTitle });
        if (this.renderQueue.length === 0)
          callhook.call(this, "branchCompleted");
        this.renderedQueue.push(data);
        this.render();
      }
    }
  }

  textRender(text) {
    this.DHObject.textRender(text);
  }

  command(command) {
    this.DHObject.command(command);
  }

  redo(moreMessage) {
    console.warn("redo ", JSON.stringify(this.runSpeech), this.renderQueue);
    this.isInterrupted = false;
    this.interruptData = {};
    if (this.runSpeech && this.isDriver)
      this.DHObject.exec(this.runSpeech, this.data, moreMessage);
    else this.render();
  }

  prevNode() {
    this.jump(-1);
  }

  reStart(moreMessage) {
    this.jump(0);
  }

  jump(number) {
    if (number < 0) {
      this.interruptQueues.splice(0);
      if (this.renderedQueue.length > 0) {
        const data = this.renderedQueue.splice(number);
        if (data.length > 0) {
          this.renderQueue.unshift(...data);
        }
      }
    } else if (number > 0) {
      const data = this.interruptQueues.splice(0, number);
      if (data.length < number) {
        const jumpLength =
          number - data.length >= this.renderQueue.length
            ? this.renderQueue.length - 1
            : number - data.length;
        const jumpData = this.renderQueue.splice(0, jumpLength);
        this.renderedQueue.push(...jumpData);
      }
    }
    callhook.call(this, "jump", { number: number });
    this.render();
  }

  nextNode() {
    this.jump(1);
  }

  prev() {
    this.jumpSentence(-1);
  }

  //
  jumpSentence(number) {
    if (number < 0) {
      let index = number;
      if (this.isInterrupted) {
        index += this.interruptQueues[0].speech.length - this.queue.length - 1;
        if (index >= 0) {
          this.interruptQueue = [...this.interruptQueues[0].speech].splice(
            index
          );
          this.exec();
          return;
        } else {
          this.interruptQueues.splice(0);
          index++;
        }
      }
      index += this.renderQueue[0].speech.length - this.queue.length - 1;
      while (index < 0) {
        if (this.renderedQueue.length > 0) {
          this.renderQueue.unshift(
            ...this.renderedQueue.splice(this.renderedQueue.length - 2, 1)
          );
          index += this.renderQueue[0].speech.length;
        } else {
          break;
        }
      }
      this.queue = [...this.renderQueue[0].speech].splice(index);
      this.exec();
    }
  }

  next(moreMessage) {
    this.exec(moreMessage);
  }

  // message, renderStart, renderCompleted, renderError, renderInterrupted, renderBack
  on(eventName, callback) {
    this.eventObj[eventName]
      ? this.eventObj[eventName].push(callback)
      : (this.eventObj[eventName] = [callback]);
  }

  off(eventName, callback) {
    if (this.eventObj[eventName]) {
      this.eventObj[eventName] = this.eventObj[eventName].filter(
        (item) => item !== callback
      );
    }
  }
}

export default DH;
