<template>
    <div id="fly">
        <div id="tools">
            <span class="button-text" @click="exit"
                ><left-outlined /> 返回游戏</span
            >
        </div>
        <div id="fly-settings">
            <div id="fly-border">
                <span>无边框模式</span>
                <Switch
                    class="fly-settings"
                    v-model:checked="noBorder"
                    checked-children="ON"
                    un-checked-children="OFF"
                ></Switch>
            </div>
            <div v-if="!isMobile" id="fly-tradition">
                <span>传统按键模式</span>
                <Switch
                    class="fly-settings"
                    v-model:checked="tradition"
                    checked-children="ON"
                    un-checked-children="OFF"
                ></Switch>
            </div>
            <span
                v-if="!isMobile"
                class="button-text"
                id="fly-download"
                @click="download"
                >下载地图图片</span
            >
        </div>
        <div id="fly-main">
            <div id="fly-left">
                <Scroll id="fly-area"
                    ><div id="area-list">
                        <span
                            v-for="(v, k) in area"
                            :selected="nowArea === k"
                            class="selectable"
                            @click="nowArea = k"
                            >{{ k }}</span
                        >
                    </div></Scroll
                >
                <Divider type="vertical" dashed id="divider-left"></Divider>
                <div id="fly-map-div">
                    <canvas id="fly-map" @click="click"></canvas>
                </div>
            </div>
            <Divider
                id="divider-right"
                dashed
                :type="isMobile ? 'horizontal' : 'vertical'"
            ></Divider>
            <div id="fly-right">
                <canvas id="fly-thumbnail" @click="fly" @wheel="wheel"></canvas>
                <div id="fly-tools">
                    <double-left-outlined
                        @click="changeFloorByDelta(-10)"
                        class="button-text fly-button"
                    />
                    <left-outlined
                        @click="changeFloorByDelta(-1)"
                        class="button-text fly-button"
                    />
                    <span
                        class="changable"
                        id="fly-now"
                        :change="titleChange"
                        >{{ title }}</span
                    >
                    <right-outlined
                        @click="changeFloorByDelta(1)"
                        class="button-text fly-button"
                    />
                    <double-right-outlined
                        @click="changeFloorByDelta(10)"
                        class="button-text fly-button"
                    />
                </div>
            </div>
        </div>
    </div>
</template>

<script lang="ts" setup>
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
import Scroll from '../components/scroll.vue';
import { getArea, getMapData, MinimapDrawer } from '../tools/fly';
import { cancelGlobalDrag, isMobile, useDrag, useWheel } from '../use';
import {
    LeftOutlined,
    DoubleLeftOutlined,
    RightOutlined,
    DoubleRightOutlined
} from '@ant-design/icons-vue';
import { debounce } from 'lodash-es';
import { tip } from '../use';
import { gameKey } from '@motajs/system-action';
import { createChangable } from '../tools/common';
import { mainSetting } from '../preset/settingIns';
import { GameStorage } from '@motajs/legacy-system';
import { IMountedVBind } from '../interface';
import { Divider, Switch } from 'ant-design-vue';

const props = defineProps<IMountedVBind>();

type Loc2 = [number, number, number, number];

const area = getArea();
const nowArea = ref(
    Object.keys(area).find(v => area[v].includes(core.status.floorId)) ?? ''
);
const nowFloor = ref(core.status.floorId);
const noBorder = ref(true);
const tradition = ref(false);
const settingScale = mainSetting.getValue('ui.mapScale', 100) / 100;
let scale = (isMobile ? 1.5 : 3) * settingScale;

const storage = GameStorage.for(GameStorage.fromAuthor('AncTe', 'flyConfig'));

noBorder.value = storage.getValue('noBorder', true);
tradition.value = storage.getValue('flyTradition', false);

const floor = computed(() => {
    return core.status.maps[nowFloor.value];
});

watch(nowFloor, n => {
    drawer.nowFloor = n;
    draw();
});
watch(nowArea, n => {
    scale = 3 * settingScale;
    lastScale = 3 * settingScale;
    drawer.nowArea = n;
    drawer.scale = 3 * settingScale;
    drawer.ox = 0;
    drawer.oy = 0;
    if (area[n] && !area[n].includes(nowFloor.value))
        nowFloor.value =
            area[n].find(v => v === core.status.floorId) ?? area[n][0];
});
watch(noBorder, n => {
    storage.setValue('noBorder', n);
    drawer.noBorder = n;
    drawer.drawedThumbnail = {};
    drawer.drawMap();
});
watch(tradition, n => {
    storage.setValue('flyTradition', n);
});

let map: HTMLCanvasElement;
let thumb: HTMLCanvasElement;
let thumbCtx: CanvasRenderingContext2D;

function exit() {
    props.controller.close(props.num);
}

const title = computed(() => {
    return core.status.maps[nowFloor.value].title;
});
const titleChange = createChangable(title).change;

let drawer: MinimapDrawer;

function drawRight() {
    let w = thumb.width;
    let h = thumb.height;
    let x = 0;
    let y = 0;
    const ratio = floor.value.width / floor.value.height;
    if (ratio > 1) {
        h = w / ratio;
        y = thumb.height / 2 - h / 2;
    }
    if (ratio < 1) {
        w = h * ratio;
        x = thumb.width / 2 - w / 2;
    }
    thumbCtx.fillStyle = '#000';
    thumbCtx.fillRect(0, 0, thumb.width, thumb.height);
    core.drawThumbnail(nowFloor.value, void 0, {
        ctx: thumbCtx,
        all: true,
        damage: true,
        inFlyMap: true,
        x,
        y,
        w,
        h
    });
}

/**
 * 绘制所有内容
 */
function draw() {
    drawer.clearCache();
    drawer.drawMap();
    drawRight();
}

function download() {
    drawer.download();
    draw();
}

function fly() {
    if (core.flyTo(nowFloor.value)) exit();
    else tip('error', `无法飞往${floor.value.title}`);
}

let lastScale = scale;
const changeScale = debounce((s: number) => {
    map.style.transform = '';
    drawer.drawedThumbnail = {};
    drawer.scale = s;
    drawer.drawMap();
    lastScale = s;
}, 200);

function resize(delta: number) {
    drawer.ox *= delta;
    drawer.oy *= delta;
    scale = delta * scale;
    changeScale(scale);
    map.style.transform = `scale(${scale / lastScale})`;
    drawer.thumbnailLoc = {};
}

let lastX = 0;
let lastY = 0;

let moved = false;
let startX = 0;
let startY = 0;

// -------------------- 点击事件

function drag(x: number, y: number) {
    if (touchScale) return;
    const dx = x - lastX;
    const dy = y - lastY;
    drawer.ox += dx;
    drawer.oy += dy;
    lastX = x;
    lastY = y;
    drawer.checkMoveThumbnail();
    drawer.drawToTarget();
    if (Math.abs(x - startX) > 10 || Math.abs(y - startY) > 10) moved = true;
}

function click(e: MouseEvent) {
    if (moved) return;

    const x = e.offsetX * devicePixelRatio;
    const y = e.offsetY * devicePixelRatio;
    for (const [id, [left, top, right, bottom]] of Object.entries(
        drawer.thumbnailLoc
    ) as [FloorIds, Loc2][]) {
        if (x >= left && x <= right && y >= top && y <= bottom) {
            if (id === nowFloor.value) {
                fly();
            } else {
                nowFloor.value = id;
            }
        }
    }
}

function wheel(ev: WheelEvent) {
    changeFloorByDelta(-Math.sign(ev.deltaY));
}

function changeAreaByFloor(id: FloorIds) {
    nowArea.value = Object.keys(area).find(v => area[v].includes(id))!;
}

function changeFloorByDelta(delta: number) {
    const now = core.floorIds.indexOf(nowFloor.value);
    let to = now + delta;
    if (to < 0) to = 0;
    if (to >= core.floorIds.length) to = core.floorIds.length - 1;
    const floor = core.status.maps[core.floorIds[to]];
    if (floor.deleted || floor.forceDelete) {
        while (to !== now) {
            to += Math.sign(delta);
            const floor = core.status.maps[core.floorIds[to]];
            if (floor.cannotViewMap) continue;
            if (!floor.deleted && !floor.forceDelete) break;
            if (to < 0 || to >= core.floorIds.length) break;
        }
    }
    nowFloor.value = core.floorIds[to];
    changeAreaByFloor(nowFloor.value);
    drawer.locateMap(nowFloor.value);
}

function changeFloorByDir(dir: Dir) {
    const data = getMapData(nowFloor.value);

    for (const [from, to] of Object.entries(data.link)) {
        if (!from.startsWith(nowFloor.value)) continue;
        const d = from.split(',')[3] as Dir;

        if (d === dir) {
            const target = to.split(',')[0] as FloorIds;
            drawer.locateMap(target);
            nowFloor.value = target;
            return;
        }
    }
}

// -------------------- 键盘事件

gameKey.use(props.ui.symbol);
gameKey
    .realize('@fly_left', () => {
        if (!tradition.value) changeFloorByDir('left');
    })
    .realize('@fly_right', () => {
        if (!tradition.value) changeFloorByDir('right');
    })
    .realize('@fly_up', () => {
        if (!tradition.value) changeFloorByDir('up');
    })
    .realize('@fly_down', () => {
        if (!tradition.value) changeFloorByDir('down');
    })
    .realize('@fly_last', () => {
        if (!tradition.value) changeFloorByDelta(-1);
    })
    .realize('@fly_next', () => {
        if (!tradition.value) changeFloorByDelta(1);
    })
    .realize('@fly_down_t', () => {
        if (tradition.value) changeFloorByDelta(-1);
    })
    .realize('@fly_up_t', () => {
        if (tradition.value) changeFloorByDelta(1);
    })
    .realize('@fly_left_t', () => {
        if (tradition.value) changeFloorByDelta(-10);
    })
    .realize('@fly_right_t', () => {
        if (tradition.value) changeFloorByDelta(10);
    })
    .realize('exit', () => {
        exit();
    })
    .realize('confirm', () => {
        fly();
    })
    .realize('fly', () => {
        exit();
    });

// -------------------- 触摸事件

let touchScale = false;
let lastDis = 0;
function touchdown(e: TouchEvent) {
    if (e.touches.length >= 2) {
        touchScale = true;
        lastDis = Math.sqrt(
            (e.touches[0].clientX - e.touches[1].clientX) ** 2 +
                (e.touches[0].clientY - e.touches[1].clientY) ** 2
        );
    }
}

function touchup(e: TouchEvent) {
    if (e.touches.length < 2) touchScale = false;
}

function touchmove(e: TouchEvent) {
    if (!touchScale) return;
    const dis = Math.sqrt(
        (e.touches[0].clientX - e.touches[1].clientX) ** 2 +
            (e.touches[0].clientY - e.touches[1].clientY) ** 2
    );
    resize(dis / lastDis);
    lastDis = dis;
}

onMounted(async () => {
    map = document.getElementById('fly-map') as HTMLCanvasElement;
    thumb = document.getElementById('fly-thumbnail') as HTMLCanvasElement;
    thumbCtx = thumb.getContext('2d')!;

    drawer = new MinimapDrawer(map);
    drawer.scale = scale;
    drawer.noBorder = noBorder.value;
    drawer.showInfo = true;
    drawer.nowArea = nowArea.value;

    const mapStyle = getComputedStyle(map);
    const thumbStyle = getComputedStyle(thumb);
    map.width = parseFloat(mapStyle.width) * devicePixelRatio;
    map.height = parseFloat(mapStyle.height) * devicePixelRatio;
    thumb.width = parseFloat(thumbStyle.width) * devicePixelRatio;
    thumb.height = parseFloat(thumbStyle.width) * devicePixelRatio;

    Array.from(document.getElementsByClassName('fly-settings')).forEach(v => {
        v.addEventListener('click', e => (v as HTMLElement).blur());
    });

    drawer.locateMap(nowFloor.value);
    draw();

    useDrag(
        map,
        drag,
        (x, y) => {
            lastX = x;
            lastY = y;
            startX = x;
            startY = y;
        },
        () => {
            setTimeout(() => {
                moved = false;
            }, 50);
        },
        true
    );

    useWheel(map, (x, y) => {
        const delta = -Math.sign(y) * 0.1 + 1;
        resize(delta);
    });

    map.addEventListener('touchstart', touchdown);
    map.addEventListener('touchend', touchup);
    map.addEventListener('touchmove', touchmove);
});

onUnmounted(() => {
    cancelGlobalDrag(drag);
    gameKey.dispose(props.ui.symbol);
});
</script>

<style lang="less" scoped>
#fly {
    width: 100%;
    height: 100%;
    font-size: 150%;
    font-family: 'normal';
    display: flex;
    align-items: center;
    user-select: none;
}

#tools {
    width: 100%;
    font-family: 'normal';
    font-size: 3.2vh;
    height: 5vh;
    position: fixed;
    left: 5vw;
    top: 5vh;
}

#fly-main {
    display: flex;
    height: 80%;
    width: 100%;
    flex-direction: row;
}

#fly-left {
    width: 50vw;
    display: flex;
    flex-direction: row;
    align-items: center;
}

#fly-area {
    height: 100%;
    width: 15vw;
}

#area-list {
    height: 100%;
    display: flex;
    flex-direction: column;
}

#divider-left {
    margin: 0;
    height: 100%;
    border-color: #ddd4;
}

#fly-map-div,
#fly-map {
    width: 35vw;
    height: 72vh;
    overflow: hidden;
}

#divider-right {
    height: 100%;
    border-color: #ddd4;
    margin: 0;
}

#fly-right {
    width: 40vw;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: space-around;
    position: relative;
}

#fly-tools {
    margin: 0;
    width: 80%;
    display: flex;
    flex-direction: row;
    justify-content: space-around;
    align-items: center;
    position: absolute;
    bottom: 0;
    width: 100%;
    background-color: #0004;
}

#fly-thumbnail {
    width: 35vw;
    height: 35vw;
    max-height: 75vh;
    max-width: 75vh;
    border: 0.1vw solid #ddd4;
}

#fly-settings {
    position: fixed;
    bottom: 5vh;
    left: 10vw;
    width: 80vw;
    display: flex;
    flex-direction: row;
    justify-content: space-around;
    align-items: center;

    div {
        display: flex;
        align-items: center;

        span {
            margin-right: 5vw;
        }
    }
}

.fly-settings[aria-checked='false'] {
    background-color: #ddd4;
}

.fly-button {
    padding: 3%;
    border-radius: 8px;
    background-color: rgba(0, 0, 0, 0.301);
    border: 1px dashed #ddda;
    filter: drop-shadow(0px 0px 16px black);
}

#fly-now {
    text-wrap: nowrap;
    white-space: nowrap;
    max-width: 50%;
    text-overflow: ellipsis;
    overflow: hidden;
    text-shadow:
        1px 1px 1px black,
        1px -1px 1px black,
        -1px 1px 1px black,
        -1px -1px 1px black;
}

@media screen and (max-width: 600px) {
    #fly {
        font-size: 225%;
    }

    #fly-main {
        flex-direction: column;
        height: 90%;
    }

    #fly-map-div,
    #fly-map {
        width: 60vw;
        height: 30vh;
    }

    #fly-area {
        width: 30vw;
        height: 30vh;
    }

    #fly-left {
        width: 90vw;
    }

    #divider-right {
        height: 0;
    }

    #fly-right {
        width: 90vw;
        height: 60vh;
    }

    #fly-thumbnail {
        width: 80vw;
        height: 80vw;
    }

    #tools {
        top: 2vh;
    }

    #fly-settings {
        bottom: 2%;
    }
}
</style>
