var _live2d_tyrano = {
  "tm": {},
  "layer": "0",
  "lip_se_obj": undefined,
  "lip_interval_id": {}
};

/*
 #[live2d_new]
 :group
 Live2D
 :title
 Live2Dモデルの定義
 :exp
 Live2Dモデルを読み込みます。
 [live2d_new]を実行した時点ではまだモデルは画面に表示されません。
 その後、[live2d_show]を実行することで画面に表示することができます。
 :sample
 [live2d_new name="haru" model_id="Haru" ]
 [live2d_show name="haru" y=-0.8 x=-0.3 scale=2.5 ]
:param
 name=モデル名を指定します。省略した場合はmodel_idの値がnameに適応されます。,
 model_id=モデルIDを指定します。modelフォルダ以下に配置されたフォルダ名がモデルIDです。また、○○.model3.jsonなど関連するファイルもすべて同じ名前にします。,
 idle=アイドルモーション名を指定できます。デフォルトは 「Idle」。複数が設定されている場合はランダムに再生されます。 ,
 scale=モデルを表示する時のスケールを指定できます。例えば、1.5を指定すると1.5倍の大きさに拡大されます。デフォルトは１,
 x=モデルのヨコ、立ち位置を指定します。注意点として画面中央が 0 となり、ティラノの他の指定とは異なりますのでご注意ください。,
 y=モデルのタテ、立ち位置を指定します。注意点として画面中央が 0 となり、ティラノの他の指定とは異なりますのでご注意ください。,
 lip=キャラクターが話しているシーンで口を動かすかどうかを指定できます。「text」を指定するとテキストに合わせて口が動きます。「sound」を指定するとボイスにあわせてリップシンクします。ただし、すべての再生中のサウンドに反応します。例えばBGMを反応させたくない場合 [playbgm html5=true] のようにhtml5 パラメータでtrueを指定して再生させてください。（リップシンク設定が有効なモデルのみ動作）,
 lip_time=口を動かすスピードを指定できます。デフォルトは100。数値を小さくするとすばやく口が動きます,
 lip_se=lipでsoundを指定した場合に、反応させるseのbufを指定します。デフォルトは0,
 lip_sound_level=リップシンクにサウンドを使用している場合の音量レベルを指定します。デフォルトは4000。つまり波形レベルの4000がリップシンクのマックス値になる設定となります。数値を調整することで最適な範囲を調整することができます。,
 breath=trueかfalseを指定します。デフォルトはtrue。アイドル状態に呼吸しているようなモーションが適応されます。適応したくない場合はfalseを指定してください,
 jname=キャラクターの表示名を指定します。口を動かす場合は必ず指定します。ここで指定した名前を#○○で指定している場合のみ口が動きます。
 #[end]
*/


TYRANO.kag.ftag.master_tag.live2d_new = {

  kag: TYRANO.kag,
  vital: ["model_id"],

  pm: {

    name: "",
    model_id: "",
    page: "fore",

    width: "",
    height: "",

    idle: "Idle",

    x: "0",
    y: "0",
    scale: "1",

    visible: "false",

    lip: "false",
    lip_time: "100",
    lip_se: "0",
    lip_sound_level: "4000",

    lip_value: "0",
    jname: "none", //キャラクターの名前を指定できます。

    breath: "true", //自然な動作になるように、首振りがはいります。

    next: "true"

  },

  start: function (pm) {

    var that = this;

    var canvas_id = "live2d_canvas_tyrano";

    //nameを省略した場合はmodel_idがnameになる。
    if (pm.name == "") {
      pm.name = pm.model_id;
    }

    //ステータスにモデル情報を配置する。
    if (!TYRANO.kag.stat.live2d_models) {
      TYRANO.kag.stat.live2d_models = {};
    }

    pm.lip_sound_level = parseInt(pm.lip_sound_level);

    //Live2D一番最初の実行
    var layer = _live2d_tyrano.layer;

    if ($("#live2d_canvas_tyrano").length == 0) {

      var j_canvas = $('<canvas id="' + canvas_id + '" class="' + pm.name + ' live2d_model live2d_canvas_tyrano"></canvas>');
      j_canvas.css("position", "absolute");

      if (pm.width == "") {
        pm.width = TYRANO.kag.config.scWidth;
      }

      if (pm.height == "") {
        pm.height = TYRANO.kag.config.scHeight;
      }

      j_canvas.attr("width", pm.width);
      j_canvas.attr("height", pm.height);

      var target_layer = TYRANO.kag.layer.getLayer(layer, pm.page);
      target_layer.show();
      target_layer.append(j_canvas);

    }

    _live2d_tyrano.tm = tyranolive2dplugin.getTyranoManager();

    //lipの設定
    if (pm.lip != "false") {

      //現在intervalがある場合は、一旦クリアにする
      let current_lip = _live2d_tyrano.lip_interval_id;
      if (current_lip[pm.name]) {
        clearInterval(current_lip[pm.name]);
      }

      (function (pm) {

        if (pm.lip == "sound") {

          var audio_obj = TYRANO.kag.tmp.map_se[pm.lip_se];

          if (typeof _live2d_tyrano.lip_se_obj == "undefined") {

            var tmp_obj = new Howl({
              src: "./tyrano/audio/silent.mp3",
              volume: 0.1
            });

            tmp_obj.play();

            let lo = {};

            //音源を視覚化するために波形データの配列を取得する
            lo.analyserNode = Howler.ctx.createAnalyser();
            lo.freqs = new Uint8Array(lo.analyserNode.frequencyBinCount);
            //ボリュームコントローラ
            Howler.ctx.createGain = Howler.ctx.createGain || Howler.ctx.createGainNode;
            lo.gainNode = Howler.ctx.createGain();
            lo.gainNode.gain.setValueAtTime(1, Howler.ctx.currentTime);
            //各ノードをつなぐ
            Howler.masterGain.connect(lo.analyserNode);
            lo.analyserNode.connect(lo.gainNode);
            lo.gainNode.connect(Howler.ctx.destination);

            _live2d_tyrano.lip_se_obj = lo;

          }


        }


        var lip_id = setInterval(function () {

          //console.log()
          //lipを更新

          var jname = $("." + TYRANO.kag.stat.chara_ptext).html();
          if (jname == pm.jname) {
            //該当キャラの場合だけ口を動かせ

            if (pm.lip == "sound") {

              //TYRANO.kag.stat.
              if (TYRANO.kag.tmp.map_se[pm.lip_se]) {

                var audio_obj = TYRANO.kag.tmp.map_se[pm.lip_se];
                if (!audio_obj.playing) {
                  _live2d_tyrano.tm.setLipValue(pm.name, 0);
                  return;
                }

              } else {
                _live2d_tyrano.tm.setLipValue(pm.name, 0);
                return;
              }

              let lo = _live2d_tyrano.lip_se_obj;

              lo.analyserNode.smoothingTimeConstant = 0.1;
              // FFTサイズ
              lo.analyserNode.fftSize = 64;
              // 周波数領域の波形データを引数の配列に格納する
              lo.analyserNode.getByteFrequencyData(lo.freqs);

              var len = lo.freqs.length,
                cnt = 0;
              for (var i = 0; i < len; i++) {
                cnt += lo.freqs[i];
              }

              var a = cnt / pm.lip_sound_level;
              _live2d_tyrano.tm.setLipValue(pm.name, a);

            } else {

              //該当キャラが喋っているときだけ、口を動かす。
              if (TYRANO.kag.stat.is_adding_text) {
                var a = (Math.floor(Math.random() * 101)) / 100;
                _live2d_tyrano.tm.setLipValue(pm.name, a);
              } else {
                _live2d_tyrano.tm.setLipValue(pm.name, 0);
              }

            }
          }

        }, parseInt(pm.lip_time));

        pm.lip_id = lip_id;
        _live2d_tyrano.lip_interval_id[pm.name] = lip_id;

      })(pm);

    }

    TYRANO.kag.stat.live2d_models[pm.name] = pm;

    //読み込み完了しないと次へはない。
    pm.onFinishLoad = function () {
      if (pm.next == "true") {
        TYRANO.kag.ftag.nextOrder();
      }
    };

    //モデルを追加
    _live2d_tyrano.tm.addModel(pm);

    return;



  }

};


/*
 #[live2d_show]
 :group
 Live2D
 :title
 Live2Dモデルの表示
 :exp
 Live2Dモデルをゲーム画面に表示します。
 表示するためには予め[live2d_new]タグでモデルを読み込んでおく必要があります。
 :sample
 [live2d_new name="haru" model_id="Haru"  ]
 [live2d_show name="haru" y=-0.8 x=-0.3 scale=2.5 ]
:param
 name=モデル名を指定します,
 idle=アイドルモーションを変更できます。複数のモーションが指定されている場合はランダムに繰り返し再生します。,
 scale=モデルを表示する時のスケールを指定できます。例えば、1.5を指定すると1.5倍の大きさに拡大されます。デフォルトは１,
 x=モデルのヨコ、立ち位置を指定します。注意点として画面中央が 0 となり、ティラノの他の指定とは異なりますのでご注意ください。,
 y=モデルのタテ、立ち位置を指定します。注意点として画面中央が 0 となり、ティラノの他の指定とは異なりますのでご注意ください。
 #[end]
*/


TYRANO.kag.ftag.master_tag.live2d_show = {

  kag: TYRANO.kag,
  vital: ["name"],
  pm: {

    name: "",
    /*
    //これは宣言しないけど、つかえるぞい。
    x:"1",
    y:"1",
    scale:"2"
    */

    next: "true"

  },

  start: function (pm) {

    var that = this;

    var name = pm.name;
    _live2d_tyrano.tm = tyranolive2dplugin.getTyranoManager();
    pm.visible = "true";

    var new_pm = _live2d_tyrano.tm.updateModel(pm);

    TYRANO.kag.stat.live2d_models[pm.name] = new_pm;


    if (pm.next == "true") {
      TYRANO.kag.ftag.nextOrder();
    }

  }

};


/*
 #[live2d_mod]
 :group
 Live2D
 :title
 Live2Dモデルの変更
 :exp
 Live2Dモデルのパラメータを変更します。
 :sample
 [live2d_new name="haru" model_id="Haru"  ]
 [live2d_show name="haru" y=-0.8 x=-0.3 scale=2.5 ]

 [live2d_mod name="haru" scale=1 ]

 :param
 name=モデル名を指定します,
 idle=アイドルモーションを変更できます,
 scale=モデルを表示する時のスケールを指定できます。例えば、1.5を指定すると1.5倍の大きさに拡大されます。デフォルトは１,
 x=モデルのヨコ、立ち位置を指定します。注意点として画面中央が 0 となり、ティラノの他の指定とは異なりますのでご注意ください。,
 y=モデルのタテ、立ち位置を指定します。注意点として画面中央が 0 となり、ティラノの他の指定とは異なりますのでご注意ください。
 #[end]

*/


TYRANO.kag.ftag.master_tag.live2d_mod = {

  kag: TYRANO.kag,
  vital: ["name"],
  pm: {

    name: "",
    /*
    //これは宣言しないけど、つかえるぞい。
    x:"1",
    y:"1",
    scale:"2"
    */

    next: "true"

  },

  start: function (pm) {

    var that = this;

    _live2d_tyrano.tm = tyranolive2dplugin.getTyranoManager();

    var new_pm = _live2d_tyrano.tm.updateModel(pm);

    TYRANO.kag.stat.live2d_models[pm.name] = new_pm;

    if (pm.next == "true") {
      TYRANO.kag.ftag.nextOrder();
    }

  }

};


/*
 #[live2d_delete]
 :group
 Live2D
 :title
 Live2Dモデルの削除
 :exp
 すべてのLive2Dモデルを完全に削除します。
 もう一度モデルを使用するには[live2d_new]で改めて定義する必要があります
 用途としては、Live2Dモデルはメモリを多く使用するため
 一度に何体も[live2d_new]を定義しておくのは推奨できません。
 そこで、区切りの良いタイミングで[live2d_delete]でメモリを綺麗にしておくことで
 安定した動作を期待できます。
 多くのLive2Dモデルを使用するゲームではうまく活用してください。
 :sample
 [live2d_delete name="haru"]
 :param
 #[end]
*/

TYRANO.kag.ftag.master_tag.live2d_delete = {

  kag: TYRANO.kag,
  vital: ["name"],
  pm: {

    name: "",
    next: "true"

  },

  start: function (pm) {

    _live2d_tyrano.tm = tyranolive2dplugin.getTyranoManager();

    _live2d_tyrano.tm.deleteModel(pm.name);

    let model_pm = TYRANO.kag.stat.live2d_models[pm.name];

    if (model_pm.lip_id) {
      clearInterval(model_pm.lip_id);
    }

    delete TYRANO.kag.stat.live2d_models[pm.name];

    if (pm.next == "true") {
      TYRANO.kag.ftag.nextOrder();
    }

  }

};




/*
 #[live2d_delete_all]
 :group
 Live2D
 :title
 Live2Dモデルの削除
 :exp
 すべてのLive2Dモデルを完全に削除します。
 もう一度モデルを使用するには[live2d_new]で改めて定義する必要があります
 用途としては、Live2Dモデルはメモリを多く使用するため
 一度に何体も[live2d_new]を定義しておくのは推奨できません。
 そこで、区切りの良いタイミングで[live2d_delete_all]でメモリを綺麗にしておくことで
 安定した動作を期待できます。
 多くのLive2Dモデルを使用するゲームではうまく活用してください。
 :sample
 [live2d_delete_all]
 :param
 #[end]
*/

TYRANO.kag.ftag.master_tag.live2d_delete_all = {

  kag: TYRANO.kag,
  vital: [],
  pm: {

    next: "true"

  },

  start: function (pm) {

    var that = this;

    _live2d_tyrano.tm = tyranolive2dplugin.getTyranoManager();

    //リップシンクのクリア
    var models = TYRANO.kag.stat.live2d_models;
    for (key in models) {
      var _pm = models[key];
      clearInterval(_pm.lip_id);
    }

    let current_lip = _live2d_tyrano.lip_interval_id;
    for (key in current_lip) {
      clearInterval(current_lip[key]);
    }

    //モデルを削除
    _live2d_tyrano.tm.deleteAllModel();
    TYRANO.kag.stat.live2d_models = {};

    if (pm.next == "true") {
      TYRANO.kag.ftag.nextOrder();
    }

  }

};


/*
 #[live2d_hide]
 :group
 Live2D
 :title
 Live2Dモデルの退場
 :exp
 Live2Dモデルを退場させます。
 再度 表示する場合は[live2d_show]でOKです。
 :sample
 [live2d_new name="haru" model_id="Haru"  ]
 [live2d_show name="haru" y=-0.8 x=-0.3 scale=2.5 ]

 [live2d_hide name="haru" ]

 [live2d_show name="haru" ]

 :param
 name=退場させるモデル名を指定します
 #[end]

*/

TYRANO.kag.ftag.master_tag.live2d_hide = {

  kag: TYRANO.kag,
  vital: ["name"],
  pm: {
    name: "",
  },

  start: function (pm) {

    var that = this;

    var name = pm.name;
    _live2d_tyrano.tm = tyranolive2dplugin.getTyranoManager();

    pm.visible = "false";
    _live2d_tyrano.tm.updateModel(pm);

    TYRANO.kag.ftag.nextOrder();

  }

};

/*
 #[live2d_motion]
 :group
 Live2D
 :title
 Live2Dモデルのモーションを再生します
 :exp
 Live2Dモデルのモーションを再生します。
 :sample
 [live2d_new name="haru" model_id="Haru"  ]
 [live2d_show name="haru" y=-0.8 x=-0.3 scale=2.5 ]
 
 [live2d_motion name="haru" mtn="Test" no=0 ]

 :param
 name=モデル名を指定します,
 mtn=指定したモーションを再生します,
 no=グループを指定できます。例えばひとつのモーション名に複数のモーションファイルが割り当てられている場合に添字を指定してください。デフォルトは0です,
 force=trueかfalseを指定します。trueを指定すると他のモーションが再生中の場合、強制的にモーションを上書きします。デフォルトはtrue
 #[end]

*/

//live2d_motion
TYRANO.kag.ftag.master_tag.live2d_motion = {

  kag: TYRANO.kag,
  vital: ["name", "mtn"],
  pm: {
    name: "",
    mtn: "",
    no: "0",
    force: "true"
  },

  start: function (pm) {

    var that = this;
    var name = pm.name;

    _live2d_tyrano.tm = tyranolive2dplugin.getTyranoManager();

    //モデルを追加
    _live2d_tyrano.tm.setMotion(pm.name, pm.mtn, parseInt(pm.no), pm.force); //noを最後に渡す。

    TYRANO.kag.ftag.nextOrder();


  }

};

/*
 #[live2d_expression]
 :group
 Live2D
 :title
 Live2Dモデルの表情を変更します。
 :exp
 Live2Dモデルの表情（Expression）を変更できます。
 :sample
 [live2d_new name="haru" model_id="Haru"  ]
 [live2d_show name="haru" y=-0.8 x=-0.3 scale=2.5 ]
 
 [live2d_expression name="haru" expression="f03" ]

 :param
 name=モデル名を指定します,
 expression=Expressionを指定します。
 
 #[end]

*/

TYRANO.kag.ftag.master_tag.live2d_expression = {

  kag: TYRANO.kag,
  vital: ["name", "expression"],
  pm: {
    name: "",
    expression: "",
    next: "true"

  },

  start: function (pm) {

    var that = this;
    var name = pm.name;

    _live2d_tyrano.tm = tyranolive2dplugin.getTyranoManager();

    //表情
    TYRANO.kag.stat.live2d_models[pm.name]["expression"] = pm.expression;

    //モデルを追加
    _live2d_tyrano.tm.setExpression(pm.name, pm.expression); //noを最後に渡す。

    if (pm.next == "true") {
      TYRANO.kag.ftag.nextOrder();
    }
    //canvas.style.opacity = 1;  


  }

};


/*
 #[live2d_restore]
 :group
 Live2D
 :title
 Live2Dモデルの復元。
 :exp
 セーブデータからロード時にLive2Dモデルを復元するためのタグです。
 必ずmake.ksに記述してください。この記述をしないとロード時に復元できません。
 :sample
 [live2d_restore ]

 :param
 
 #[end]

*/

TYRANO.kag.ftag.master_tag.live2d_restore = {

  kag: TYRANO.kag,
  vital: [],
  pm: {

  },

  start: function (pm) {

    tyranolive2dplugin.releaseTyranoManager();

    //モデル
    var models = $.extend({}, TYRANO.kag.stat.live2d_models);

    if (Object.keys(models).length <= 0) {
      TYRANO.kag.ftag.nextOrder();
      return;
    }


    let tags = [
      { "tag": "live2d_delete_all", "pm": {} }
    ];

    //TYRANO.kag.stat.live2d_models = {};
    //TYRANO.kag.ftag.startTag("live2d_delete_all", { next: "false" });

    //tags.push({ "tag": "wait", "pm": { "time": 2000 } });


    for (key in models) {

      var pm = models[key];
      pm.next = "true";
      tags.push({ "tag": "live2d_new", "pm": pm });

    }


    for (let key in models) {

      var pm = models[key];
      pm.next = "true";
      tags.push({ "tag": "wait", "pm": { time: "50" } });
      tags.push({ "tag": "live2d_expression", "pm": pm });

    }

    //fadein fadeout の復元
    if (typeof TYRANO.kag.stat.live2d_canvas_visible == "undefined" || TYRANO.kag.stat.live2d_canvas_visible == "on") {
      $("#live2d_canvas_tyrano").css("opacity", 1);
    } else {
      $("#live2d_canvas_tyrano").css("opacity", 0);
    }


    TYRANO.kag.ftag.startTags(tags, async () => {

      TYRANO.kag.ftag.nextOrder();

    });


  }

};

/*
 #[live2d_fadein]
 :group
 Live2D
 :title
 Live2D表示レイヤのフェードイン
 :exp
 Live2D表示レイヤをフェードイン表示できます。
 最初に[live2d_fadeout]で非表示にしておいて
 [live2d_new]でLive2Dの表示を完成させてから
 フェードインさせるとスムーズにLive2D画面をつくることができます。
 :sample
 [live2d_fadein time=2000 ]

 :param
 time=フェードインにかける時間を指定してください。ミリ秒で指定します。デフォルトは1000ミリ秒,
 wait=フェードインの完了を待つか否かを指定します。デフォルトはtrue。
 
 #[end]

*/
TYRANO.kag.ftag.master_tag.live2d_fadein = {

  kag: TYRANO.kag,
  vital: [],
  pm: {
    time: "1000",
    wait: "true"
  },

  start: function (pm) {

    if (pm.time == "0") {
      pm.time = "10";
    }

    var j_canvas = $("#live2d_canvas_tyrano");
    if (j_canvas.length == 0) {
      TYRANO.kag.ftag.nextOrder();
      return;
    }

    j_canvas.animate(
      {
        "opacity": 1
      },
      parseInt(pm.time),
      function () {

        if (pm.wait == "true") {
          TYRANO.kag.ftag.nextOrder();
        }
      }
    );

    if (pm.wait == "false") {
      TYRANO.kag.ftag.nextOrder();
    }

    TYRANO.kag.stat.live2d_canvas_visible = "on";

  }

};

/*
 #[live2d_fadeout]
 :group
 Live2D
 :title
 Live2D表示レイヤのフェードアウト
 :exp
 Live2D表示レイヤをフェードアウトできます。
 明示的に[live2d_fadein]するまでモデルが表示されませんのでご注意ください。
 :sample
 [live2d_fadeout time=2000 ]

 :param
 time=フェードアウトにかける時間を指定してください。ミリ秒で指定します。デフォルトは1000ミリ秒,
 wait=フェードアウトの完了を待つか否かを指定します。デフォルトはtrue。
 
 #[end]

*/

TYRANO.kag.ftag.master_tag.live2d_fadeout = {

  kag: TYRANO.kag,
  vital: [],
  pm: {
    time: "1000",
    wait: "true"
  },

  start: function (pm) {

    if (pm.time == "0") {
      pm.time = "10";
    }

    var j_canvas = $("#live2d_canvas_tyrano");
    if (j_canvas.length == 0) {
      TYRANO.kag.ftag.nextOrder();
      return;
    }

    j_canvas.animate(
      {
        "opacity": 0
      },
      parseInt(pm.time),
      function () {

        if (pm.wait == "true") {
          TYRANO.kag.ftag.nextOrder();
        }
      }
    );

    if (pm.wait == "false") {
      TYRANO.kag.ftag.nextOrder();
    }

    TYRANO.kag.stat.live2d_canvas_visible = "off";

  }

};





