<template lang="pug">

AddEmployeeModal(
        
        :searchData="rows" 
        :searchFields="['first_name', 'last_name']" 
        :searchRow="addEmployeeRow" 
        :modalId="modalId" 
        @emitResult="addEmployee($event)"
    )

<!-- Schedule Table -->
.pt-5(v-for="(month, mIndex) in scheduleData" :key="mIndex")
    table(class='table table-bordered table-sm' v-for="(row, rIndex) in month" :key="rIndex")
        thead
            tr
                td.fw-bold(style="width: 150px") {{ format(startDates[mIndex], "MMMM", {locale: de}) }}
                td(v-for="(day, dIndex) in weekdays" :key="dIndex" :style="dIndex === 6 ? 'border-right : 2px solid' : ''" :class="{'text-body text-opacity-50 bg-secondary bg-opacity-10' : row.dates[dIndex] === null}") {{ day }}
            tr
                td.fw-bold() {{ format(startDates[mIndex], "y") }}
                td(v-for="(date, dIndex) in row.dates" :key="dIndex" :style="dIndex === 6 ? 'border-right : 2px solid' : ''" :class="{'bg-secondary bg-opacity-10' : row.dates[dIndex] === null}") 
                    template(v-if="date !== null") {{ format(date, "dd.MM.")}}
        tbody 
            tr(v-for="(employee, eIndex) in row.employees" :key="eIndex")
                td()
                    .row.justify-content-between.align-items-center.employee-wrapper
                        .col.text-start.ps-3 {{employee.shorthand}}
                        .col.text-end
                            button.btn.btn-sm(
                                @click="removeEmployee(mIndex, rIndex, employee)"
                            )
                                i.bi-x-lg
                td(
                    v-for="(date, dIndex) in row.dates" 
                    :key="dIndex" 
                    :style="dIndex === 6 ? 'border-right : 2px solid' : ''"
                    :class="{'bg-secondary bg-opacity-10' : row.dates[dIndex] === null, 'selected' : isSelected(mIndex, rIndex, eIndex, dIndex)}"
                    tabindex="0"
                    @click.exact="row.dates[dIndex] !== null ? select(mIndex, rIndex, eIndex, dIndex) : null"
                    @click.ctrl.exact="row.dates[dIndex] !== null ? ctrlSelect(mIndex, rIndex, eIndex, dIndex) : null"
                    @click.shift.exact="row.dates[dIndex] !== null ? shiftSelect(mIndex, rIndex, eIndex, dIndex) : null"
                    @keydown.esc="onEscape"
                    @keydown.up.prevent="onUp"
                    @keydown.down.prevent="onDown"
                    @keydown.right="onRight"
                    @keydown.left="onLeft"
                )
            tr()
                td().text-end
                    button(
                            class="btn btn-sm" 
                            @click="addEmployeeRow = rIndex; addEmployeeMonth = mIndex"
                            data-bs-toggle="modal"
                            :data-bs-target="'#'+modalId"
                        )
                        i.bi.bi-plus-lg
                td(
                    v-for="(date, dIndex) in row.dates" 
                    :key="dIndex" 
                    :style="dIndex === 6 ? 'border-right : 2px solid' : ''" 
                    :class="{'bg-secondary bg-opacity-10' : row.dates[dIndex] === null}"
                ) &nbsp;


</template>


<script setup lang="ts">
    import { watch, computed, ref } from 'vue'
    import type { Ref, ComputedRef } from 'vue'
    import { storeToRefs } from 'pinia'
    import { useEmployees } from '@/stores/employees'
    import { addMonths, eachMonthOfInterval, subDays, addDays, format, getISODay, getYear, getMonth, getDaysInMonth } from 'date-fns'
    import { de } from 'date-fns/locale'
    import AddEmployeeModal from '@/components/Schedule/AddEmployeeModal.vue'
    import Coordinates from '@/types/coordinates'

    /*
    *   Props
    */
    const props = defineProps({
        startDate : {
            type: Date,
            required: true,
        },
        numberOfMonths : {
            type: Number,
            default: 3,
            required: false,
            validator(value : number) {
                return value > 0
            }
        },
    })
    /**
     * End Props
     * 
     * Local variables
     */
    const modalId : string = "addEmployeeModal"
    const weekdays : string[] = getDoubleWeekdays()
    const store = useEmployees()
    
    /**
     * End Local Variables
     * 
     * Computed
     */
    const startDates : ComputedRef<Date[]> = computed(() => {
        let numberOfMonths : number;

        if(typeof(props.numberOfMonths) === "string") {
            numberOfMonths = parseInt(props.numberOfMonths)
        }
        else numberOfMonths = props.numberOfMonths
        
        return eachMonthOfInterval({
            start: new Date(getYear(props.startDate), getMonth(props.startDate)),
            end: new Date(getYear(props.startDate), getMonth(props.startDate) + numberOfMonths - 1)
        })
    })

    const offsets : ComputedRef<number[]> = computed(() => {
        let arr : number[] = []
        let offset : number;
        arr.push(getISODay(startDates.value[0]) - 1)

        for ( let i = 1; i < startDates.value.length; i++ ){
            offset = getDaysInMonth(startDates.value[i-1]) + arr[arr.length - 1]

            if( offset >= 35 && offset <= 42) {
                arr.push(getISODay(startDates.value[i])-1+7)
            }
            else arr.push(getISODay(startDates.value[i])-1)
        }

        return arr
    })

    /**
     * End Computed
     * 
     * Refs
     */
    const addEmployeeRow : Ref<number> = ref(-1)
    const addEmployeeMonth : Ref<number> = ref(-1)
    const scheduleData : Ref<ScheduleData> = ref(getScheduleData())
    const { rows } = storeToRefs(store)

    const selected : Ref<string[]> = ref([])
    const shiftAnchor : Ref<Coordinates | undefined> = ref(undefined)
    /**
     * End Refs
     *
     * Functions
     */
    function getScheduleData() : ScheduleData {
        let arr : ScheduleData = []
        let month : ScheduleMonth = []
        let row : ScheduleRow = {dates: [], employees: []}
        let end : Date;

        let i

        startDates.value.forEach((start, mIndex) => {
            end = subDays(addMonths(start, 1), 1)
    
            for (i = 0; i < offsets.value[mIndex] ; i++) {
                row.dates.push(null)
            }

            while (start <= end){
                while (i < 14 && start <= end){
                    row.dates.push(start)
                    start = addDays(start, 1)
                    i++
                }
                while (i < 14) {
                    row.dates.push(null)
                    i++
                }
                month.push(row)
                row = {dates: [], employees: []}
                i = 0
            }

            arr.push(month)
            month = []
        })
        return arr
    }

    function getDoubleWeekdays() : string[] {
        let arr = ['Mo.', 'Di.', 'Mi.', 'Do.', 'Fr.', 'Sa.', 'So.']
        return [...arr , ...arr]
    }

    function addEmployee(param : Employee){
        if (addEmployeeMonth.value > -1 && addEmployeeMonth.value > -1) {
            scheduleData.value[addEmployeeMonth.value][addEmployeeRow.value].employees.push(param)
        }
    }

    function isSelected(m : number, r : number, e : number, d : number) : boolean {
        return selected.value.includes(Coordinates.toString(m,r,e,d))
    }
    
    function removeFromSelected(m : number, r : number, e : number, d : number) : boolean {
        if(selected.value.includes(Coordinates.toString(m,r,e,d))){
            selected.value.splice(selected.value.indexOf(Coordinates.toString(m,r,e,d)),1)
            return true
        }
        return false
    }

    function getRange(x : number, y : number) : number[] {
        let arr : number[] = []

        if(x === y) return [x]
        if ( x < y){
            for(x ; x <= y; x++){
                arr.push(x)
            }
        }
        else if (x > y){
            for(x ; x >= y; x--){
                arr.push(x)
            }
        }
        return arr
    }

    /**
     * Event Listeners
     */
    function removeEmployee(mIndex : number, rIndex: number, emp : Employee) : void {
        let i = scheduleData.value[mIndex][rIndex].employees.indexOf(emp)
        if(i > -1){
            scheduleData.value[mIndex][rIndex].employees.splice(i, 1)
        }
    }

    function select(m : number, r : number, e : number, d : number) : void {
        if(selected.value.length > 1 || !isSelected(m,r,e,d)) {
            selected.value = []
            selected.value.push(Coordinates.toString(m,r,e,d))
            shiftAnchor.value = new Coordinates(m,r,e,d)
        }
        else {
            selected.value = []
            shiftAnchor.value = undefined
        }
    }

    function ctrlSelect(m : number, r : number, e : number, d : number) : void {
        if(isSelected(m,r,e,d)){
            removeFromSelected(m,r,e,d)
            shiftAnchor.value = new Coordinates(m,r,e,d)
        }
        else {
            selected.value.push(Coordinates.toString(m,r,e,d))
            shiftAnchor.value = new Coordinates(m,r,e,d)
        }
    }

    function shiftSelect(m : number, r : number, e : number, d : number) : void {
        if(shiftAnchor.value !== undefined && shiftAnchor.value.mIndex === m && shiftAnchor.value.rIndex === r){
            
            if(shiftAnchor.value.toString() !== selected.value[selected.value.length - 1] && selected.value.includes(shiftAnchor.value.toString())){
                selected.value.splice(selected.value.indexOf(shiftAnchor.value.toString()))
            }

            let rangeE : number[] = getRange(shiftAnchor.value.eIndex, e)
            let rangeD : number[] = getRange(shiftAnchor.value.dIndex, d)

            let anchor = shiftAnchor.value
            removeFromSelected(anchor.mIndex, anchor.rIndex, anchor.eIndex, anchor.dIndex)

            rangeE.forEach(te => {
                rangeD.forEach(td => {
                    selected.value.push(Coordinates.toString(m,r,te,td))
                })
            })
        }
    }

    function onEscape() : void {
        selected.value = []
        shiftAnchor.value = undefined
    }

    function onUp() : void {
        let anchor = shiftAnchor.value
        
        if(anchor !== undefined && anchor.eIndex > 0){
            let newCoord = new Coordinates(anchor.mIndex, anchor.rIndex, anchor.eIndex - 1, anchor.dIndex)
            
            selected.value = [newCoord.toString()]
            shiftAnchor.value = newCoord
        }
    }
    function onRight() : void {
        let anchor = shiftAnchor.value
        
        if(anchor !== undefined && anchor.dIndex < scheduleData.value[anchor.mIndex][anchor.rIndex].dates.length - 1) {
            let newCoord = new Coordinates(anchor.mIndex, anchor.rIndex, anchor.eIndex, anchor.dIndex + 1)
            
            selected.value = [newCoord.toString()]
            shiftAnchor.value = newCoord
        }
    }
    function onDown() : void {
        let anchor = shiftAnchor.value
        
        if(anchor !== undefined && anchor.eIndex < scheduleData.value[anchor.mIndex][anchor.rIndex].employees.length - 1) {
            let newCoord =  new Coordinates(anchor.mIndex, anchor.rIndex, anchor.eIndex + 1, anchor.dIndex)
            
            selected.value = [newCoord.toString()]
            shiftAnchor.value = newCoord   
        } 
    }
    function onLeft() : void {
        let anchor = shiftAnchor.value

        if(anchor !== undefined && anchor.dIndex > 0 && scheduleData.value[anchor.mIndex][anchor.rIndex].dates[anchor.dIndex - 1] !== null ) {
            let newCoord = new Coordinates(anchor.mIndex, anchor.rIndex, anchor.eIndex, anchor.dIndex - 1)

            selected.value = [newCoord.toString()]
            shiftAnchor.value = newCoord
        }
    }

    /**
     * Watchers 
     */
    watch(startDates, () => {
        scheduleData.value = getScheduleData()
    })

</script>

<style lang="scss" scoped>
    table {
        table-layout: fixed;
        -ms-user-select: none;
        -moz-user-select: none;
        -webkit-user-select: none;
        -webkit-touch-callout: none;
        -khtml-user-select: none;
        user-select: none;

        td {
            cursor: pointer;
        }
    }

    .o-acp__item.list-group-item:hover {
        background-color: aliceblue !important;
    }

    div.employee-wrapper .btn {
        transition: 0.3s;
        opacity: 0;
        border: 0px;
    }

    div.employee-wrapper:hover .btn {
        opacity: 1;
    }

    .selected {
        background-color: var(--bs-secondary);
        opacity: 0.1;
    }

</style>