<script lang="ts">
  import { invoke } from "@tauri-apps/api/tauri";
  import AutoComplete from "simple-svelte-autocomplete";
  import attack_types from "../const/attack_types.json";
  import type { PokemonStatus } from "../model/PokemonStatus";

  export let player1Data: PokemonStatus | undefined;
  export let player2Data: PokemonStatus | undefined;
  export let attackId: number | undefined;
  let attackData;
  $: {
    if (moveValue > 0) {
      invoke("search_move", { index: moveValue }).then((r) => {
        attackData = r;
        moveValue = undefined;
      });
    }
    if (!!attackId) {
      console.log(attackId);
      invoke("search_move", { index: attackId }).then((r) => {
        attackData = r;
        attackId = undefined;
      });
    }
  }
  let attack_direction = "p1p2";
  let total_max_damage = 0;
  let total_min_damage = 0;
  let total_max_damage_percentage = 0;
  let total_min_damage_percentage = 0;
  let item_magnification = 1.0;
  function calculate_damage() {
    let attacker: PokemonStatus;
    let defender: PokemonStatus;
    let atk_value;
    let def_value;
    if (
      !player1Data ||
      !player2Data ||
      !attackData ||
      (!!attackData && ![1, 2].includes(attackData.category))
    ) {
      // display some message somewhere
      total_max_damage = 0;
      total_min_damage = 0;
      total_max_damage_percentage = 0;
      total_min_damage_percentage = 0;
      console.log("skip");
      return;
    }
    if (attack_direction === "p1p2") {
      attacker = player1Data;
      defender = player2Data;
    } else {
      attacker = player2Data;
      defender = player1Data;
    }
    // 物理or特殊の判定
    if (attackData.category === 1) {
      atk_value = attacker.attack;
      def_value = defender.defense;
    } else {
      atk_value = attacker.special_attack;
      def_value = defender.special_defense;
    }

    // debug
    // ステータスアップアイテム
    const A = '{"attack":1.5}';
    const B = '{"defense":1.5}';
    const C = '{"special_attack":1.5}';
    const D = '{"special_defense":1.5}';
    const S = '{"speed":1.5}';
    const BE = '{"attack":1.3,"defense":1.3,"special_attack":1.3,"special_defense":1.3,"speed":1.5,"condition":"highest_status"}';
    // 技威力up
    const E = '{"attack_power":1.3,"condition":"type","type":0}';


    // 計算で使用する値をひとまとめにしてみる
    let data = {
      level: 50, // 対戦時は基本的にレベル50なのでconstでも良さそう
      attack_power: attackData.power, // 技威力
      atk_value: atk_value,
      def_value: def_value,

      // 最大ダメージと最小ダメージ
      maxDamage: 0,
      minDamage: 0,
      // 攻撃側タイプ
      // TODO: try catchでエラーハンドリング
      atk_type: attacker.types,
      atk_terastype: attacker.terastype,
      // 防御側タイプ
      def_type:
        defender.terastype[0] > 0
          ? defender.terastype
          : defender.types,

      // 持ち物情報
      // 来週直したらコメントアウト外す
      // atk_item_effect: JSON.parse(A), // データの形決まったら調整
      // def_item_effect: JSON.parse(A), // データの形決まったら調整

      // 攻撃側の技が物理(1)or特殊の判定
      attack_data_category: attackData.category,

      // 技タイプ
      move_type: attackData.types,
      // 範囲の計算で使用する
    };
    // 計算
    /*
      memo:計算用テストデータ
        - level: 50,
        - power: 100(じしん)
        - attac: 182（ガブリアスA252）
        - defence: 189（キョジオーンBがVで努力値176）
        - 結果
          - タイプ一致なし:37-44
          - タイプ一致あり:55-66
          - タイプ一致あり2倍:110~132
          - タイプ一致あり4倍:220~264(セキタンザン)
          - タイプ一致あり無効: 0~0
          - タイプ一致あり等倍(半減と抜群): 76~91(これはモロバレルで計算)
          - タイプ一致こだわりハチマキ: 164~194
          - タイプ一致テラスタルこだわりハチマキ: 220~260
        - ハチマキが特殊技に乗らないことを確認
          - ガブリアスC100(実数値)火炎放射(ハチマキ)=>: キョジオーンD110 => 16~19
        - メガネが特殊技に乗ることを確認
          - ガブリアスC100(実数値)火炎放射(メガネ)=>: キョジオーンD110 => 23~28
        - チョッキの効果が特殊技に乗ることを確認
          - ガブリアスC100(実数値)火炎放射=>: キョジオーンD110 => 23~28
        - チョッキの効果が物理に乗らないことを確認
          - ガブリアスA182(実数値)地震=>: キョジオーンB189 => 110~132
    */
    let d = damage(data);
    total_max_damage = d.maxDamage;
    total_min_damage = d.minDamage;
    total_max_damage_percentage = (d.maxDamage * 100) / defender.hp;
    total_min_damage_percentage = (d.minDamage * 100) / defender.hp;
  }
  function damage(data) {
    // TODO: 来週直す
    // 命の珠の計算がズレる
    // アイテムの情報を受け取れるようにする
    /*
    // 攻撃側のアイテム補正
    if (data.atk_item_effect.attack_power !== undefined){
      // アイテムで技の威力が上昇する場合の判定
      data.attack_power *= aktPowerIncreasingItems(data)
    }else {
      // アイテムでステータスが上昇する場合の判定
      data.atk_value *=atkStatusIncreasingItems(data)
    }

    // 防御側のアイテム補正
    data.def_value *=defStatusIncreasingItems(data)
    */
    // 基本の計算
    var step1 = Math.trunc((data.level * 2) / 5 + 2);
    var step2 = Math.trunc(
      (step1 * data.attack_power * data.atk_value) / data.def_value
    );
    data.maxDamage = Math.trunc(step2 / 50 + 2); // この時点の計算結果は最大ダメージ

    // 各種補正かけた結果を返す
    return modify(data);
  }
  // アイテムで技の威力補正を計算する
  function aktPowerIncreasingItems(data){
    if (data.atk_item_effect.attack_power !== undefined){
      // const E = '{"attack_power":1.3,"condition":"type","type":}';
      // タイプ一致アイテム
      if (data.atk_item_effect.type !== undefined){
        console.log("タイプ一致", data.atk_item_effect.type, data.atk_type)
        if (data.atk_item_effect.type === data.atk_type){
          return data.atk_item_effect.attack_power
        }
      }
      // 対応予定
      // 物知りメガネとか => 物理特殊一致させる必要がある
      // 達人の帯 => 抜群のときだけ1.2倍

      // 命の珠は無条件で1.3倍
      console.log("いのちのたま: ", data.atk_item_effect.attack_power)
      return data.atk_item_effect.attack_power
    }
    return 1
  }
  // アイテムでのステータス補正を計算する
  function atkStatusIncreasingItems(data){
    // アイテムで技の威力が上がる場合は、上の関数で計算する
    // Note: ハチマキの効果が特攻に乗らないようにする必要がある
    // TODO: ブーストエナジーの計算は、挙動がおかしかったら直す(たぶん考慮たりない)
    if (data.atk_item_effect.condition !== undefined){
      // ブーストエナジー
      // 技が物理or特殊で分岐
      if (data.attack_data_category === 1){
        return data.atk_item_effect.attack
      }else {
        return data.atk_item_effect.special_attack
      }
    } else if (data.atk_item_effect.attack !== undefined && data.attack_data_category === 1){
      // 物理
      return data.atk_item_effect.attack
    } else if (data.atk_item_effect.special_attack !== undefined){
      // 特殊
      return data.atk_item_effect.special_attack
    }
    return 1
  }
  function defStatusIncreasingItems(data){
    if (data.def_item_effect.condition !== undefined){
      // ブーストエナジー
      // 技が物理or特殊で分岐
      if (data.attack_data_category === 1){
        return data.def_item_effect.defense
      }else {
        return data.def_item_effect.special_defense
      }
    }else if (data.def_item_effect.defense !== undefined && data.def_item_effect.special_defense !== undefined){
      // 進化の輝石
      // 防御と特防の両方が上がるのでどちらを返してもOK
      return data.def_item_effect.defense
    } else if (data.def_item_effect.defense === undefined && data.def_item_effect.special_defense !== undefined && data.attack_data_category !==1){
      // チョッキ
      // 物理の計算でチョッキの効果を出さないようにするため、防御の計算値がないことと、攻撃が物理ではないことを比較する
      return data.def_item_effect.special_defense
    }
    return 1
  }
  // 全部一気にやると大変なので、一旦基本計算から
  function modify(data) {
    // 範囲補正
    // 親子愛補正
    // ※svでおやこあいの特性はないのでスキップする
    // 天気補正
    // 急所補正
    // 乱数補正
    data.minDamage = getMinDamage(data);
    // タイプ一致などの倍率
    let magnification = 1; // タイプ相性倍率
    let magnification_terastype = 1; // タイプ一致補正 テラスタル含む

    // ポケモンのタイプとテラスタルのタイプ一致の判定
    let is_terastype = data.atk_terastype !== 0;
    let terastype_match = false;
    if (is_terastype) {
      terastype_match = data.atk_type.includes(data.atk_terastype[0]);
    }

    // ポケモンのタイプと技のタイプの判定
    // 1. テラスタルしてるなら、テラスタルのタイプで判定
    // 2. テラスタルしていないなら、元のタイプで判定
    let atk_type;
    if (is_terastype) {
      atk_type = data.atk_terastype;
    } else {
      atk_type = data.atk_type;
    }
    let type_match =
      data.atk_type.findIndex((type) => type === data.move_type[0]) > -1;
    // Note:
    // フライングプレスだけ格闘と飛行の2属性があるが、タイプ一致補正は格闘にだけ補正が掛かる
    // data.move_type[0] = 7で格闘が取れるのでOK

    // 技のタイプとテラスタルのタイプの一致判定
    let movetype_match = false;
    if (is_terastype) {
      movetype_match = data.move_type.includes(data.atk_terastype);
    }

    // 技のタイプ一致倍率(技のタイプが一致するかどうか)
    // 本来のタイプ	テラスタルタイプ	倍率
    if (type_match && is_terastype && terastype_match) {
      // 1. 一致する	一致する	=> x2
      magnification_terastype = 2;
    } else if (
      (type_match && is_terastype && !terastype_match) ||
      (type_match && !is_terastype) ||
      (!type_match && is_terastype && movetype_match)
    ) {
      // 2. 一致する	一致しない	=> x1.5
      // 3. 一致する	テラスタルなし	=> x1.5
      // 4. 一致しない	一致する	=> x1.5 NG
      magnification_terastype = 1.5;
    }
    // 5. 一致しない	一致しない	1
    // 6. 一致しない	テラスタルなし	1
    // そのままなので特に処理なし

    // 相性補正
    // 相性補正の倍率を計算
    magnification = getCompatibility(data);

    // やけど補正
    // 壁補正
    // ブレインフォース補正
    // スナイパー補正
    // いろめがね補正
    // もふもふ(ほのお)補正
    // ダメージ半減特性の補正(M half)
    // 効果抜群を軽減する特性の補正(M filter)
    // フレンドガード補正
    // 達人の帯補正
    // メトロノーム補正
    // 命の珠補正
    // 半減木の実補正
    // その他ダメージ補正(M twice)
    // ダイマックス技などの軽減(M protect)

    // 最後に倍率かける
    data.maxDamage = Math.trunc(data.maxDamage * magnification_terastype);
    data.minDamage = Math.trunc(data.minDamage * magnification_terastype);
    data.maxDamage = Math.trunc(data.maxDamage * magnification);
    data.minDamage = Math.trunc(data.minDamage * magnification);
    data.maxDamage = Math.trunc(data.maxDamage * item_magnification);
    data.minDamage = Math.trunc(data.minDamage * item_magnification);

    return data;
  }
  // 乱数補正
  // ダメージは、乱数で0.85~1.00をかけた値の範囲で変動する
  // 1.00はそのままの値なので、
  // 0.85をかけた最小ダメージだけを計算する
  // 少数切り捨て
  function getMinDamage(data) {
    return Math.trunc((data.maxDamage * 85) / 100);
  }
  // 相性計算
  // 倍率だけ返す
  function getCompatibility(data) {
    let type_magnification = 1;

    // 技のタイプの相性倍率とりだす
    // attack_types の json の index と move_type の数字が1つずれてる
    let compatibility = attack_types[data.move_type - 1];

    // 相手のタイプ毎に倍率計算
    for (let i = 0; i < data.def_type.length; i++) {
      // 倍率
      type_magnification *= compatibility[data.def_type[i] - 1]; // ここもindexずれる
    }
    return type_magnification;
  }
  // 技名検索
  async function getItems(keyword) {
    try {
      let result = await invoke("autosearch_move", { keyword });
      // console.log(result);
      return result;
    } catch (e) {
      console.log(e);
      return [];
    }
  }
  let moveValue;
</script>

<div class="calculator">
  <table>
    <tr>
      <td>
        <input
          type="radio"
          name="direction"
          value="p1p2"
          bind:group={attack_direction}
        /> P1 ->> P2
      </td>
      <td>
        <input
          type="radio"
          name="direction"
          value="p2p1"
          bind:group={attack_direction}
        /> P2 ->> P1
      </td>
      <td>
        <input
          type="text"
          bind:value={item_magnification}
        />
      </td>
    </tr>
    <tr>
      <td>
        <AutoComplete
          showClear={true}
          searchFunction={getItems}
          delay="200"
          localFiltering={false}
          labelFieldName="name"
          valueFieldName="id"
          bind:value={moveValue}
        />
      </td>
      <td>{ attackData ? attackData.name : "" }</td>
      <td>
        <input type="button" value="Calculate" on:click={calculate_damage} />
      </td>
      <td>Total Damage: {total_min_damage} ~ {total_max_damage}</td>
      <td>
        Percentage: {Math.round(total_min_damage_percentage * 100)/100}% ~ {Math.round(total_max_damage_percentage * 100)/100}%
      </td>
    </tr>
  </table>
  <div class="debug-data">
    {JSON.stringify(player1Data)} -
    {JSON.stringify(player2Data)}
  </div>
</div>

<style>
  .debug-data {
    font-size: 10px;
  }
  .calculator {
    height: 100px;
    width: 100%;
    border-top: 2px solid white;
  }
</style>
