<template>
    <div class="container my-3">
        <v-btn-toggle variant="tonal" density="compact" class="mx-auto" v-model="mode" color="blue">
            <v-btn size="small" class="text-none" value="default"
                prepend-icon="mdi-cursor-default-click">Pointer</v-btn>
            <v-btn size="small" class="text-none" value="select" prepend-icon="mdi-select-multiple">Select</v-btn>
            <v-btn size="small" class="text-none" value="move" prepend-icon="mdi-cursor-move">Move</v-btn>
            <v-btn size="small" class="text-none" value="pan" prepend-icon="mdi-hand-back-right-outline">Pan</v-btn>

            <v-btn size="small" class="text-none" value="Scene" prepend-icon="mdi-panorama-outline">{{ l.addScene }}</v-btn>
            <v-btn size="small" class="text-none" value="GameObject"
                v-if="selectedItem.length == 1 && selectedItem[0].__type == 'Scene'" prepend-icon="mdi-bird">{{ l.createGameObject }}</v-btn>
            <!-- <v-btn size="small" class="text-none" value="Task"
                v-if="selectedItem.length == 1 && selectedItem[0].__type == 'GameObject'"
                prepend-icon="mdi-checkbox-marked-circle-plus-outline">{{ l.addTask }}</v-btn> -->
        </v-btn-toggle>
        <div @wheel="onWheel" @mousedown="onMouseDown" @mouseup="onMouseUp" @mousemove="onDrag"
            :class="`svg-container ${mode}`" ref="svgContainer">
            <svg class="scene-designer" @resize="resize" :width="viewBox.w" :height="viewBox.h"
                :viewBox="`${vb.x} ${vb.y} ${vb.w} ${vb.h}`" x="0" y="0" xmlns="http://www.w3.org/2000/svg"
                xmlns:xlink="http://www.w3.org/1999/xlink">
                <defs>
                    <marker id="arrow" viewBox="0 0 10 10" refX="5" refY="5"
                        markerWidth="6" markerHeight="6" orient="auto-start-reverse">
                        <path d="M 0 0 L 10 5 L 0 10 z" />
                    </marker>
                </defs>
                <SvgRectangle v-model="selector" class="selector"></SvgRectangle>
                <g class="lines"></g>
                <g class="tasks"></g>
                <g class="game-objects"></g>
                <g class="scenes"></g>
            </svg>
        </div>
        <v-navigation-drawer location="right" :width="expandDrawer ? 800 : 400">
            <v-btn :icon="expandDrawer ? 'mdi-arrow-expand-right' : 'mdi-arrow-expand-left'" variant="plain" size="x-small" density="compact"
                @click="expandDrawer = !expandDrawer" v-if="selectedItem.length" class="position-absolute z-100 ma-1"></v-btn>
            <template v-for="(item, i) in flatItems" :key="i">
                <component :is="components[item.__type]" :ref="`sc-${item.__path}`" 
                    :vd="item.vd" v-model="item.data"
                    @target="setTarget($event, item)" @preview="preview"
                    :visible="item.visible" :cid="item.id" :parent="item.__parent"
                    :selected="selectedItem.includes(item)">
                </component>
            </template>
        </v-navigation-drawer>
        <v-navigation-drawer>
            <v-list density="compact" nav v-model:selected="selectedItem"
            :select-strategy="mode == 'select' ? 'leaf' : 'single-leaf'">
                <v-list-item v-for="(item, i) in flatItems.toSorted((a,b)=>a.__path.localeCompare(b.__path))" 
                    :key="i" :title="item.data.title" :value="item" :style="`padding-left:${item.__level}rem`" 
                    v-show="!item.__parent || item.__parent.vd.expanded" :base-color="item.vd.__showInView ? '' : 'grey'"
                    :color="[0,'secondary', 'primary', 'success'][item.__level]">
                    <template v-slot:prepend>
                    <v-btn variant="plain" density="comfortable" size="small"
                            :icon="`mdi-eye${item.visible ? '' : '-off'}`"
                            @click.stop="item.visible = !item.visible"></v-btn>
                        <!-- <v-icon :icon="components[item.name].icon" size="small"></v-icon> -->
                    </template>
                    <template v-slot:append>
                        <v-btn variant="plain" density="compact" size="small" color="red-darken-4" icon="mdi-delete"
                            @click="remove(item)"></v-btn>
                        <v-btn v-if="item.__type != 'Task'" :icon="item.vd.expanded ? 'mdi-chevron-up' : 'mdi-chevron-down'" 
                            variant="plain" size="small" @click="item.vd.expanded = !item.vd.expanded"></v-btn>
                    </template>
                </v-list-item>
            </v-list>
            
            <template v-if="selectedItem.length == 1 && selectedItem[0].__type == 'Scene'">
                <v-btn size="small" class="text-none" @click="copyScene" prepend-icon="mdi-content-copy">Copy</v-btn>
                <v-btn size="small" class="text-none" @click="pasteScene" prepend-icon="mdi-content-paste">Paste</v-btn>
            </template>
        </v-navigation-drawer>
    </div>
    <v-dialog v-model="previewDialog" width="auto" max-width="1200">
        <AssetPreview :objectId="previewObject" autoplay></AssetPreview>
    </v-dialog>
</template>

<script>
import GameObject from './GameObject.vue';
import Scene from './Scene.vue';
import SvgRectangle from './SvgRectangle.vue';
import Utils from '#/app/Utils';
import AssetSelector from '../AssetsManagement/AssetSelector.vue';
import Task from './Task.vue';

const components = {
    Scene, GameObject, Task
}

export default {
  components: { AssetSelector },
    props:{
        modelValue: Object
    },
    data(){
        return {
            context: this,
            mode: 'default',
            selectedItem: [],
            viewBox: {
                x: 0,
                y: 0,
                w: 1000,
                h: 800
            },
            scale: 1,
            offset:{
                x: 0,
                y: 0
            },
            selector:{
                x1:0, x2:0, y1:0, y2:0
            },
            modeStep: 0,
            mousedown: false,
            target: null,
            assetSelector: {
                active: false,
                type: ['Scene']
            },
            dialog: false,
            expandDrawer: false,
            previewObject: null,
            previewDialog: false
        }
    },
    mounted(){
        window.addEventListener('resize', this.resize);
        this.resize();
        this.object.scenes = this.object.scenes || [];
    },
    unmounted(){
        window.removeEventListener('resize', this.resize);
    },
    computed:{
        vb(){
            return {
                x: (1-this.scale)*100 + this.offset.x, 
                y: (1-this.scale)*100 + this.offset.y, 
                w: this.viewBox.w * this.scale,
                h: this.viewBox.h * this.scale
            }
        },
        object(){
            return this.modelValue;
        },
        items(){
            return this.object.scenes;
        },
        zoom:{
            get(){
                return 1 / this.scale;
            },
            set(v){
                this.rescale(1 / v);
            }
        },
        components(){
            return components;
        },
        flatItems(){
            let fi = [];
            this.items?.forEach((i, ii)=>{
                i.__type = 'Scene';
                i.__path = `scene-${ii.toString().padStart(4, '0')}`;
                i.__level = 1;
                i.data?.items?.forEach((go, igo)=>{
                    go.__parent = i;
                    go.__type = 'GameObject';
                    go.__path = `${i.__path}.go-${igo.toString().padStart(4, '0')}`
                    go.__level = 2;
                    go.data.__root = this.object; 
                    go.data.__this = go;
                    go.data.items?.forEach((t, it)=>{
                        fi.push(t);
                        t.__parent = go;
                        t.__type = 'Task';
                        t.__path = `${go.__path}.task-${it.toString().padStart(4, '0')}`
                        t.__level = 3;
                    })
                    fi.push(go);
                })
                fi.push(i);
            })
            return fi;
        }
    },
    methods:{
        rescale(scale, e){
            let oldScale = this.scale;
            if (!e){
                this.scale = scale;
                this.offset.x += (oldScale - this.scale) * (this.$refs.svgContainer.offsetWidth/2);
                this.offset.y += (oldScale - this.scale) * (this.$refs.svgContainer.offsetHeight/2);
            }else{
                this.scale *= (1 + Math.sign(e.deltaY) / 10);
                let oo = {
                    x: (oldScale - this.scale) * e.offsetX,
                    y: (oldScale - this.scale) * e.offsetY
                };
                if (this.target){
                    this.retarget(e)
                }
                this.offset.x += oo.x;
                this.offset.y += oo.y
            }
            document.documentElement.style.setProperty('--svg-scale', this.scale);
        },
        retarget(e){
            if (this.target.delta){
                this.retargetDelta(e, this.target)
            }else{
                this.retargetAbsolute(e, this.target)
            }
        },
        retargetDelta(e, target){
            let p = {
                x: Utils.round(e.movementX*this.scale, 0),
                y: Utils.round(e.movementY*this.scale, 0)
            }
            target.attrs.forEach(a=>{
                if (Array.isArray(a)){
                    a[0] = p.x;
                    a[1] = p.y;
                }else if (a.startsWith('x')){
                    this.target.target[a] += p.x;
                }else if (a.startsWith('y')){
                    this.target.target[a] += p.y;
                }
            })
        },
        retargetAbsolute(e, target){
            let p = {
                x: Utils.round(this.vb.x + e.offsetX * this.scale), 
                y: Utils.round(this.vb.y + e.offsetY * this.scale)
            }
            target.attrs.forEach(a=>{
                if (Array.isArray(a)){
                    a[0] = p.x;
                    a[1] = p.y;
                }else if (a.startsWith('x')){
                    this.target.target[a] = p.x;
                }else if (a.startsWith('y')){
                    this.target.target[a] = p.y;
                }
            })
        },
        onWheel(e){
            e.preventDefault();
            this.rescale(null, e);
        },
        onDrag(e){
            if (this.mousedown?.button == 0 || this.modeStep > 0) {
                if (this.mode == 'move'){
                    let p = {
                        x: Utils.round(e.movementX*this.scale, 0),
                        y: Utils.round(e.movementY*this.scale, 0)
                    }
                    this.selectedItem.forEach(i=>{
                        let mf = components[i.__type].modifiers;
                        mf.filter(m=>m.match(/^x[0-9]+$/)).forEach(x=>i.vd[x]+= p.x);
                        mf.filter(m=>m.match(/^y[0-9]+$/)).forEach(y=>i.vd[y]+= p.y);
                    })
                }else if (this.target && this.mode != 'pan') {
                    this.retarget(e)
                }
            }
            if (e.shiftKey || 
                this.mousedown?.button == 1 || 
                (this.mode == 'pan' && this.mousedown?.button == 0) ||
                (this.mode == 'default' && this.mousedown?.button == 0 && !this.target)
            ){
                this.offset.x -= e.movementX*this.scale;
                this.offset.y -= e.movementY*this.scale;
            }
            //console.log(e);
        },
        onMouseDown(e){
            this.mousedown = { button: e.button };
            //console.log(e, this.mode, this.modeStep)
            if (e.button == 0 && !['default', 'move', 'pan'].includes( this.mode )){
                let cs;
                if (this.mode == 'select'){
                    //console.log('selecting')
                    cs = [['x1', 'y1'], ['x2', 'y2']];
                    if (this.modeStep == 0){
                        this.target = {
                            target: this.selector,
                            attrs: []
                        }
                    }
                }else{
                    cs = this.components[this.mode].steps;
                    //console.log(cs);
                    if (this.modeStep == 0){
                        this.target = {
                            target: {},
                            attrs: []
                        }
                        let id, nid = 1;
                        do {
                            id = `${this.components[this.mode].name}-${nid++}`
                        }while (this.flatItems.find(i=>i.data.id == id));

                        let targetContainer = this.items;
                        if (this.mode != 'Scene'){
                            targetContainer = this.selectedItem[0]?.data; //this.items;
                            targetContainer.items = targetContainer.items || [];
                            targetContainer = targetContainer.items;
                        }
                        targetContainer.push({
                            //__type: this.mode,
                            vd: this.target.target,
                            data: { title: id, id },
                            visible: true
                        })
                    }
                }

                let p = {
                    x: Utils.round(this.vb.x + e.offsetX * this.scale), 
                    y: Utils.round(this.vb.y + e.offsetY * this.scale)
                }
                
                for (let i = this.modeStep + 1; i <= cs.length; i++){
                    this.target.target[cs[i-1][0]] = p.x;
                    this.target.target[cs[i-1][1]] = p.y;
                }

                this.modeStep++;
                if (this.modeStep >= cs.length){
                    this.modeStep = 0;
                    if (this.mode == 'select'){
                        this.select();
                        this.selector.x1 = this.selector.y1 = this.selector.x2 = this.selector.y2 = 0;
                    }
                }
                if (this.modeStep ){
                    this.target.attrs[0] = cs[this.modeStep][0];
                    this.target.attrs[1] = cs[this.modeStep][1];
                }
            }
        },
        onMouseUp(){
            this.mousedown = false;
            if (this.mode == 'default' && !this.target){
                this.selectedItem = [];
            }
            if (this.modeStep == 0){
                this.target = null;
            }
        },
        setTarget(t, item){
            this.target = t;
            this.selectedItem = [item]
            let i = item;
            while (i){
                i.vd.expanded = true;
                i = i.__parent;
            }
        },
        select(){
            let r = Utils.adjustMinMax(this.selector);
            this.selectedItem = this.flatItems.filter(i=>this.$refs['sc-'+i.__path][0].intersect(r));
        },
        resize(){
            let r = this.$refs.svgContainer;
            this.viewBox.w = r.clientWidth;
            this.viewBox.h = r.clientHeight;
            //this.zoom = Math.min(r.clientWidth / this.viewBox.w, r.clientHeight / this.viewBox.h);
        },
        remove(item){
            //console.log(item);
            let p = item.__parent?.data?.items || this.items;
            p.splice(p.indexOf(item), 1);
        },
        preview(v){
            this.previewObject = v;
            this.previewDialog = true;
        },
        copyScene(){
            navigator.clipboard.writeText(Utils.toSafeJSON(this.selectedItem[0]));
        },
        async pasteScene(){
            let src = JSON.parse(await navigator.clipboard.readText());
            let dx = this.selectedItem[0].vd.x1 - src.vd.x1, dy = this.selectedItem[0].vd.y1 - src.vd.y1;
            src.data.items.forEach(i=>{
                i.vd.x1 += dx;
                i.vd.y1 += dy;
            })
            this.selectedItem[0].data = src.data;
        }
    }
}
</script>