<template>
  <div
    :id="uuid"
    :class="[
      'world_map',
      `map_theme_${mapTheme}`,
      drawableMap ? '' : 'draw_required',
      props.isDetailMap ? 'map_detail' : 'map_basic',
      hideMapCityName ? 'hide_cityname' : '',
      getMapSeasonClassName(),
    ]"
  >
    <div
      v-my-tooltip.hover.top="{
        class: 'map_title_tooltiptext',
      }"
      class="map_title"
      :title="getTitleTooltip()"
    >
      <span class="map_title_text" :style="{ color: getTitleColor() }"
        >{{ mapResult?.year }}年 {{ mapResult?.month }}月</span
      >
      <span class="tooltiptext" />
    </div>
    <div ref="map_area" class="map_body">
      <div class="map_bglayer1" />
      <div class="map_bglayer2" />
      <div class="map_bgroad" />
      <div class="map_button_stack">
        <button
          type="button"
          :class="['btn btn-primary map_toggle_cityname btn-sm btn-minimum', hideMapCityName ? 'active' : '']"
          data-bs-toggle="button"
          :aria-pressed="hideMapCityName"
          autocomplete="off"
          @click="hideMapCityName = !hideMapCityName"
        >
          도시명 표기</button
        ><br />
        <button
          :style="{
            display: deviceType != 'mouseOnly' ? 'block' : 'none',
          }"
          type="button"
          :class="['btn btn-secondary map_toggle_double_tap btn-sm btn-minimum', toggleSingleTap ? 'active' : '']"
          data-bs-toggle="button"
          :aria-pressed="toggleSingleTap"
          autocomplete="off"
          @click="toggleSingleTap = !toggleSingleTap"
        >
          두번 탭 해 도시 이동
        </button>
      </div>
      <template v-if="drawableMap === undefined"><!--로딩중?--></template>
      <template v-else-if="props.isDetailMap">
        <MapCityDetail
          v-for="city of drawableMap.cityList"
          :key="city.id"
          :city="city"
          :image-path="imagePath"
          :is-my-city="city.id === drawableMap.myCity"
          @click="emit('city-click', city, $event)"
          @mouseenter="activatedCity = city"
          @mouseleave="activatedCity = undefined"
        />
      </template>
      <template v-else
        ><MapCityBasic
          v-for="city of drawableMap.cityList"
          :key="city.id"
          :city="city"
          :is-my-city="city.id === drawableMap.myCity"
          @click="emit('city-click', city, $event)"
          @mouseenter="activatedCity = city"
          @mouseleave="activatedCity = undefined"
      /></template>
    </div>
    <div
      ref="tooltipDom"
      class="city_tooltip"
      :style="{
        display: isOutside || !activatedCity ? 'none' : 'block',
        position: 'absolute',
        left: `${(() => {
          if (cursorX + tooltipWidth + 10 > 700) {
            return cursorX - tooltipWidth - 5;
          }
          return cursorX + 10;
        })()}px`,
        top: `${cursorY + 30}px`,
      }"
    >
      <div class="city_name">{{ activatedCity?.text }}</div>
      <div class="nation_name">{{ activatedCity?.nation }}</div>
    </div>
  </div>
</template>

<script lang="ts">
export type MapCityParsedRaw = {
  id: number;
  level: number;
  state: number;
  nationID?: number;
  region: number;
  supply: boolean;
};

type MapCityParsedName = MapCityParsedRaw & {
  name: string;
  x: number;
  y: number;
};

type MapCityParsedNation = MapCityParsedName & {
  nationID?: number;
  nation?: string;
  color?: string;
  isCapital: boolean;
};

type MapCityParsedClickable = MapCityParsedNation & {
  clickable: number;
};

type MapCityParsedRegionLevelText = MapCityParsedClickable & {
  region_str: string;
  level_str: string;
  text: string;
};

export type MapCityParsed = MapCityParsedRegionLevelText;

type MapCityDrawable = {
  cityList: MapCityParsed[];
  myCity?: number;
};

type MapNationParsed = {
  id: number;
  name: string;
  color: string;
  capital: number;
};

export type CityPositionMap = {
  [cityID: number]: [string, number, number];
};
</script>
<script lang="ts" setup>
import "@/../css/map.css";
import type { loadMapOption } from "@/map";
import { type PropType, toRef, inject, type Ref, ref, watch, type ComponentPublicInstance } from "vue";
import { v4 as uuidv4 } from "uuid";
import type { MapResult } from "@/defs";
import { joinYearMonth } from "@/util/joinYearMonth";
import { parseYearMonth } from "@/util/parseYearMonth";
import vMyTooltip from "@/directives/vMyTooltip";
import type { GameConstStore } from "@/GameConstStore";
import { unwrap_err } from "@/util/unwrap_err";
import { getMaxRelativeTechLevel, TECH_LEVEL_YEAR_GAP } from "@/utilGame/techLevel";
import { deviceType } from "detect-it";
import { unwrap } from "@/util/unwrap";
import MapCityBasic from "./MapCityBasic.vue";
import MapCityDetail from "./MapCityDetail.vue";
import { convertDictById } from "@/common_legacy";
import { useElementSize, useMouseInElement } from "@vueuse/core";
import { hideMapCityName, toggleSingleTap } from "@/state/mapViewer";
const uuid = uuidv4();
const gameConstStore = unwrap_err(
  inject<Ref<GameConstStore>>("gameConstStore"),
  Error,
  "gameConstStore가 주입되지 않았습니다."
);

const tooltipDom = ref<ComponentPublicInstance<HTMLDivElement>>();
const map_area = ref<ComponentPublicInstance<HTMLDivElement>>();
const { elementX: cursorX, elementY: cursorY, isOutside } = useMouseInElement(map_area);
const { width: tooltipWidth } = useElementSize(tooltipDom);

const emit = defineEmits<{
  (event: "city-click", city: MapCityParsed, e: MouseEvent): void;
  (event: "parsed", drawable: MapCityDrawable): void;
}>();

const props = defineProps({
  serverNick: {
    type: String,
    required: true,
  },
  serverID: {
    type: String,
    required: true,
  },
  imagePath: {
    type: String,
    required: true,
  },
  mapName: {
    type: String,
    required: true,
  },
  isDetailMap: { type: Boolean, default: undefined, required: false },
  clickableAll: { type: Boolean, default: undefined, required: false },
  hrefTemplate: { type: String, default: "#", required: false },
  /// clickable, 소속 국가, 첩보 여부 등을 반환여부를 설정
  neutralView: { type: Boolean, default: false, required: false },
  ///반환 값에 본인이 위치한 도시 값을 반환하도록 설정. neutralView와 별개
  showMe: { type: Boolean, default: true, required: false },

  callback: {
    type: Function as PropType<loadMapOption["callback"]>,
    default: undefined,
  },
  startYear: { type: Number, default: undefined, required: false },

  cityPosition: {
    type: Object as PropType<CityPositionMap>,
    required: true,
  },
  formatCityInfo: {
    type: Function as PropType<(city: MapCityParsed) => MapCityParsed>,
    required: true,
  },

  modelValue: {
    type: Object as PropType<MapResult>,
    required: true,
  },
});

const mapResult = toRef(props, "modelValue");

const mapTheme = toRef(props, "mapName");
function getTitleColor(): string | undefined {
  const { startYear, year } = mapResult.value;

  if (year < startYear + 1) {
    return "magenta";
  }
  if (year < startYear + 2) {
    return "orange";
  }
  if (year < startYear + 3) {
    return "yellow";
  }
}

const drawableMap = ref<MapCityDrawable>(convertCityObjs(props.modelValue));
const activatedCity = ref<MapCityParsed>();

function getBeginGameLimitTooltip(): string | undefined {
  const { startYear, year, month } = mapResult.value;
  if (year > startYear + 3) return undefined;

  const [remainYear, remainMonth] = parseYearMonth(joinYearMonth(startYear + 3, 0) - joinYearMonth(year, month));

  return `초반제한 기간 : ${remainYear}년${remainMonth > 0 ? ` ${remainMonth}개월` : ""} (${startYear + 3}년)`;
}

function getTitleTooltip(): string {
  const result: string[] = [];
  const beginLimit = getBeginGameLimitTooltip();
  if (beginLimit) {
    result.push(beginLimit);
  }

  const { startYear, year } = mapResult.value;

  const maxTechLevel = gameConstStore.value.gameConst.maxTechLevel;
  const currentTechLimit = getMaxRelativeTechLevel(startYear, year, maxTechLevel);

  if (currentTechLimit == maxTechLevel) {
    result.push(`기술등급 제한 : ${currentTechLimit}등급 (최종)`);
  } else {
    const nextTechLimitYear = currentTechLimit * TECH_LEVEL_YEAR_GAP + startYear;
    result.push(`기술등급 제한 : ${currentTechLimit}등급 (${nextTechLimitYear}년 해제)`);
  }

  return result.join("<br>");
}

function getMapSeasonClassName(): string {
  const { month } = mapResult.value;

  if (month <= 3) {
    return "map_spring";
  }
  if (month <= 6) {
    return "map_summer";
  }
  if (month <= 9) {
    return "map_fall";
  }
  return "map_winter";
}

function convertCityObjs(obj: MapResult): MapCityDrawable {
  //원본 Obj는 굉장히 간소하게 온다, Object 형태로 변환해서 사용한다.

  function toCityObj([id, level, state, nationID, region, supply]: MapResult["cityList"][0]): MapCityParsedRaw {
    return {
      id: id,
      level: level,
      state: state,
      nationID: nationID > 0 ? nationID : undefined,
      region: region,
      supply: supply != 0,
    };
  }

  function toNationObj([id, name, color, capital]: MapResult["nationList"][0]): MapNationParsed {
    return {
      id,
      name,
      color,
      capital,
    };
  }

  const nationList = convertDictById(obj.nationList.map(toNationObj)); //array of object -> dict

  const spyList = obj.spyList;
  const shownByGeneralList = new Set(obj.shownByGeneralList);

  const myCity = obj.myCity;
  const myNation = obj.myNation;

  function mergePositionInfo(city: MapCityParsedRaw): MapCityParsedName {
    const id = city.id;
    if (!(id in props.cityPosition)) {
      throw TypeError(`알수 없는 cityID: ${id}`);
    }
    const [name, x, y] = props.cityPosition[id];

    return {
      ...city,
      name,
      x,
      y,
    };
  }

  function mergeNationInfo(city: MapCityParsedName): MapCityParsedNation {
    //nationID 값으로 isCapital, color, nation을 통합

    const nationID = city.nationID;
    if (nationID === undefined || !(nationID in nationList)) {
      return {
        ...city,
        isCapital: false,
      };
    }

    const nationObj = nationList[nationID];
    return {
      ...city,
      nation: nationObj.name,
      color: nationObj.color,
      isCapital: nationObj.capital == city.id,
    };
  }

  function mergeClickable(city: MapCityParsedNation): MapCityParsedClickable {
    //clickable = (defaultCity << 4 ) | (remainSpy << 3) | (ourCity << 2) | (shownByGeneral << 1) | clickableAll
    const id = city.id;
    const nationID = city.nationID;
    let clickable = 16;
    if (id in spyList) {
      clickable |= spyList[id] << 3;
    }
    if (myNation !== null && nationID == myNation) {
      clickable |= 4;
    }
    if (shownByGeneralList.has(id)) {
      clickable |= 2;
    }
    if (myCity !== null && id == myCity) {
      clickable |= 2;
    }
    if (props.clickableAll) {
      clickable |= 1;
    }

    return {
      ...city,
      clickable,
    };
  }

  const cityList = obj.cityList
    .map(toCityObj)
    .map(mergePositionInfo)
    .map(mergeNationInfo)
    .map(mergeClickable)
    .map(window.formatCityInfo);

  const result = {
    cityList: cityList,
    myCity: myCity,
  };
  emit("parsed", result);

  return result;
}
watch(
  () => props.modelValue,
  (mapInfo) => {
    activatedCity.value = undefined;
    drawableMap.value = convertCityObjs(mapInfo);
  }
);
/*
function setMouseWork(obj: MapCityDrawable) {
  initTooltip($(drawTarget));

  const $tooltip = $(drawTarget + " .city_tooltip");
  const $tooltip_city = $tooltip.find(".city_name");
  const $tooltip_nation = $tooltip.find(".nation_name");

  const $objs = $(drawTarget + " .city_link");

  const $map_body = $(drawTarget + " .map_body");

  //터치스크린 탭

  if (!option.neutralView && is_touch_device()) {
    $objs.on("touchstart", function () {
      if (window.sam_toggleSingleTap) {
        return true;
      }
      const $this = $(this);

      const touchMode = $this.data("touchMode") as number;
      if ($tooltip_city.data("target") != $this.data("id")) {
        $this.data("touchMode", 1);
      } else if (touchMode === undefined) {
        $this.data("touchMode", 1);
      } else {
        $this.data("touchMode", touchMode + 1);
      }
      $map_body.data("touchMode", 1);

      $tooltip_city.data("target", $this.data("id"));
    });

    $objs.on("touchend", function () {
      if (window.sam_toggleSingleTap) {
        return true;
      }
      const $this = $(this);
      const position = $this.parent().position();
      $tooltip_city.html($this.data("text") as string);

      const nation_text = $this.data("nation") as string;
      if (nation_text) {
        $tooltip_nation.html(nation_text).show();
      } else {
        $tooltip_nation.html("").hide();
      }

      const left = position.left;
      const top = position.top;

      const tooltipWidth = unwrap($tooltip.width());

      if (left + tooltipWidth + 35 > window.innerWidth) {
        $tooltip.css({ top: top + 45, left: left - tooltipWidth - 10 }).show();
      } else {
        $tooltip.css({ top: top + 45, left: left + 35 }).show();
      }

      const touchMode = $this.data("touchMode") as number;
      if (touchMode <= 1) {
        return false;
      }

      //xxx: touchend 다음 click 이벤트가 갈 수도 있고, 안 갈 수도 있다.
      $this.data("touchMode", 0);
    });

    $map_body.on("touchend", function () {
      if (window.sam_toggleSingleTap) {
        return true;
      }
      //위의 touchend bind에 해당하지 않는 경우 -> 빈 지도 터치
      $tooltip.hide();
    });
  }

  //Mouse over 모드 작동

  $map_body.on("mousemove", function (e) {
    if ($(this).data("touchMode")) {
      return true;
    }

    const rect = this.getBoundingClientRect();
    const left = e.clientX - rect.left - this.clientLeft + this.scrollLeft;
    const top = e.clientY - rect.top - this.clientTop + this.scrollTop;

    const tooltipWidth = unwrap($tooltip.width());

    if (e.clientX + rect.left + tooltipWidth + 10 > window.innerWidth) {
      $tooltip.css({ top: top + 30, left: left - tooltipWidth - 10 });
    } else {
      $tooltip.css({ top: top + 30, left: left + 10 });
    }
  });

  $objs.on("mouseenter", function () {
    if ($map_body.data("touchMode")) {
      return true;
    }

    const $this = $(this);

    $tooltip_city.data("target", $this.data("id"));
    $tooltip_city.html($this.data("text"));
    const nation_text = $this.data("nation");
    if (nation_text) {
      $tooltip_nation.html(nation_text).show();
    } else {
      $tooltip_nation.html("").hide();
    }

    $tooltip.show();
  });

  $objs.on("mouseleave", function () {
    $tooltip.hide();
  });

  $objs.on("click", function () {
    //xxx: touchend 다음 click 이벤트가 갈 수도 있고, 안 갈 수도 있다.
    const touchMode = $(this).data("touchMode") as number | undefined;
    if (touchMode === undefined) {
      return;
    }

    if (touchMode === 1) {
      return false;
    }
  });

  return obj;
}

function setCityClickable(obj: MapCityDrawable) {
  obj.cityList.forEach(function (city) {
    const $cityLink = $world_map.find(`.city_base_${city.id} .city_link`);

    if ("clickable" in city && city.clickable > 0) {
      $cityLink.attr("href", stringFormat(hrefTemplate, city.id));
    }

    if (selectCallback) {
      $cityLink.on("click", function (e) {
        e.preventDefault();
        return selectCallback(city);
      });
    }
  });

  return obj;
}

async function reloadWorldMap(option: loadMapOption, drawTarget = ".world_map"): Promise<void> {
  const $world_map = $(drawTarget);

  const defaultOption: loadMapOption = {
    isDetailMap: true, //복잡 지도, 단순 지도
    clickableAll: false, //어떤 경우든 클릭을 가능하게 함. 해당 동작의 동작 가능성 여부와는 별도.
    selectCallback: undefined, //callback을 지정시 clickable과 관계 없이 해당 함수를 실행.
    hrefTemplate: "#", //도시가 클릭가능할 경우 지정할 href값. {0}은 도시 id로 변환됨

    //아래부터는 post query에 들어갈 녀석
    year: undefined, //year값, 연감등에 사용
    month: undefined, //month값, 연감등에 사용
    neutralView: false, //clickable, 소속 국가, 첩보 여부 등을 반환여부를 설정
    showMe: true, //반환 값에 본인이 위치한 도시 값을 반환하도록 설정. neutralView와 별개

    callback: undefined,

    //기타 보조 값
    startYear: undefined,
  };

  const isDetailMap = props.isDetailMap ?? true;
  const clickableAll = props.clickableAll ?? false;
  const selectCallback = props.selectCallback;
  const hrefTemplate = props.hrefTemplate;

  const cityPosition = window.getCityPosition();

  const storedOldMapKey = `sam.${props.serverNick}.map`;
  const storedStartYear = `sam.${props.serverNick}.startYear`;
  //OBJ : startYear, year, month, cityList, nationList, spyList, shownByGeneralList, myCity

  const $hideCityNameBtn = $world_map.find(".map_toggle_cityname");
  if (localStorage.getItem("sam.hideMapCityName") == "yes") {
    $world_map.addClass("hide_cityname");
    $hideCityNameBtn.addClass("active").attr("aria-pressed", "true");
  }

  $hideCityNameBtn.on("click", function () {
    //이전 상태 확인
    const state = localStorage.getItem("sam.hideMapCityName") == "no";
    if (state) {
      $world_map.addClass("hide_cityname");
      localStorage.setItem("sam.hideMapCityName", "yes");
    } else {
      $world_map.removeClass("hide_cityname");
      localStorage.setItem("sam.hideMapCityName", "no");
    }
  });

  const $toggleSingleTapBtn = $world_map.find(".map_toggle_single_tap");
  if (localStorage.getItem("sam.toggleSingleTap") == "yes") {
    window.sam_toggleSingleTap = true;
    $toggleSingleTapBtn.addClass("active").attr("aria-pressed", "true");
  } else {
    window.sam_toggleSingleTap = false;
  }

  const $map_body = $(drawTarget + " .map_body");

  $toggleSingleTapBtn.on("click", function () {
    //이전 상태 확인
    const state = localStorage.getItem("sam.toggleSingleTap") == "no";
    if (state) {
      $map_body.removeData("touchMode");
      localStorage.setItem("sam.toggleSingleTap", "yes");
      window.sam_toggleSingleTap = true;
    } else {
      localStorage.setItem("sam.toggleSingleTap", "no");
      window.sam_toggleSingleTap = false;
    }
  });

  const computedResult = await convertCityObjs(rawObject)
    .then(isDetailMap ? drawDetailWorldMap : drawBasicWorldMap)
    .then(setMouseWork)
    .then(setCityClickable);

  if (option.callback) {
    option.callback(computedResult, rawObject);
  }
}
*/
</script>

<style scoped lang="scss"></style>
