<template>
    <div
        id="jobListView"
        ref="jobListArea"
        :class="theme"
        @scroll="updateButtonAccess()"
    >
        <div
            :key="key"
            :class="{'single-page': isSinglePage, 'default-page': !isSinglePage}"
        >
            <!-- wait for crane message -->
            <CardItem
                v-if="isWaitForCraneVisible && waitForCrane !== ''"
                ref="cardItem"
                :content="waitForCraneMessage"
                icon="warning"
                leftStripColor="var(--color-warning)"
                :class="waitForCraneHorizontalMargin"
                :fontSize="contentFontSize"
            />
            <!-- Jobs -->
            <div
                v-for="(job, index) in jobList"
                :key="index"
                ref="jobItemRow"
                class="jobItemRow"
            >
                <div class="item">
                    <div
                        v-if="!isSinglePage && areJobsVisible"
                        :style="{backgroundColor:
                            job.getSequenceNumberColor(index, selectedIndex, tileIndicatorColorFormWidget ?
                                tileIndicatorColorFormWidget.metafieldId : undefined, tileColorsMap)}"
                        class="sequence-number"
                        :class="theme"
                    >
                        {{ formatSequenceNumber(index + 1) }}
                    </div>
                    <JobItemVariform
                        v-if="areJobsVisible"
                        :key="index"
                        style="flex-grow:1;"
                        :class="[addSelectJobAnimation(index), rehandleShadowClass(getNumberOfRehandleShadows(job)),
                                 {promoted: job.isPromoted}]"
                        :backgroundColor="job.isPromoted ? 'var(--color-tertiary)':'var(--color-container-background)'"
                        :containerName="job.getContainerName(labels)"
                        :formWidgets="jobListFormWidgets"
                        :jobData="job.getJobDataObject(tileColorsMap)"
                        :jobDataLocalizedLabels="labels"
                        :isDeliveryEmpty="job.isSwappableDelivery"
                        :isGateOutJob="job.isGateOutJob()"
                        :isPriorityFetch="job.isPriorityFetch"
                        :isTruckWithinProximity="job.isTruckWithinProximity()"
                        :isTwinJob="job.isTwinJob()"
                        :isTbdJob="job.isTbdJob()"
                        :leftStripColor="job.getLeftStripColor(index, selectedIndex, tileIndicatorColorFormWidget ?
                            tileIndicatorColorFormWidget.metafieldId : undefined, tileColorsMap)"
                        :showTruckProximity="true"
                        @click.native="isClickable() ? jobListRender(index) : ''"
                    />
                    <div
                        v-if="isRehandleJobMoreThan5(getNumberOfRehandleShadows(job)) && areJobsVisible"
                        class="extra-rehandles-number"
                    >
                        +{{ getNumberOfRehandleShadows(job) - 5 }}
                    </div>
                </div>
            </div>
            <!-- Lane change item -->
            <div
                v-if="laneChangeVisible && !doesLaneChangePlanHaveRTGmoves"
                ref="lcpItem"
                class="item"
            >
                <div
                    v-if="state === 'full' && !isSinglePage"
                    :style="lcpSequenceNbrBgColor"
                    class="sequence-number"
                    :class="theme"
                >
                    <div class="lcp-icon">
                        <img
                            src="/resources/static/images/Collapse.svg"
                            alt="collapse"
                        >
                        <img
                            src="/resources/static/images/Expand.svg"
                            alt="expand"
                        >
                    </div>
                </div>
                <LaneChangeItem
                    :presenter="middleSectionPresenter"
                    :laneChangePlan="laneChangePlan"
                    :cheLastKnownPos="cheLastKnownPos"
                    :state="'full'"
                    :theme="theme"
                    @clicked="jobList.length === 0 || !areJobsVisible ? laneChangeClick() : ''"
                />
            </div>
        </div>
    </div>
</template>

<script>
/* eslint-disable no-unused-vars */
import {FormWidget} from '@navis-npm/tos-models-js/src/aggregates/variform/FormWidget';
/* eslint-enable no-unused-vars */
import JobItemVariform from '@navis-npm/tos-storybook/src/components/jobitem/JobItemVariform';
import LaneChangeItem from '../LaneChangeItem';
import {MiddleSectionPresenter} from '../../../presenters/MiddleSectionPresenter';
import GlobalState from '../../../state/GlobalState';
import CardItem from '@navis-npm/tos-storybook/src/components/cards/CardItem';
import Localizations from '../../../utils/Localizations';
import {calcNewJobListScrollPosition} from '../../../utils/Utils';
import FallbackJobListTileVariformModel from '../../../utils/FallbackJobListTileVariformModel';

export default {
    name: 'JobList',
    components: {
        JobItemVariform,
        LaneChangeItem,
        CardItem
    },
    props: {
        middleSectionPresenter: {
            type: MiddleSectionPresenter,
            required: true
        },
        jobClick: {
            type: Function,
            required: false,
            default: () => {}
        },
        updateScrollButtonAccess: {
            type: Function,
            required: false,
            default: () => {}
        },
        laneChangePlanClick: {
            type: Function,
            required: false,
            default: () => {}
        }
    },
    /**
     * Data section of this UI component
     * @return {Object}
     */
    data: function() {
        return {
            state: 'full',
            jobList: [],
            selectedIndex: -1,
            selectedJob: null,
            key: 0,
            theme: 'theme-light',
            laneChangeVisible: false,
            laneChangePlan: {},
            cheLastKnownPos: '',
            areJobsVisible: true,
            isWaitForCraneVisible: false,
            isSinglePage: GlobalState.isOnePage(),
            doesLaneChangePlanHaveRTGmoves: false, // default, will be evaluated when fetching the job list
            waitForCrane: '',
            labels: Localizations.getTranslatedLabels('jobItem'),
            jobListFormWidgets: FallbackJobListTileVariformModel.formWidgets
        };
    },
    computed: {
        /**
         * getter determining margins for waitForCrane message box
         * @return {Object}
         */
        waitForCraneHorizontalMargin: function() {
            return {
                waitForCraneLeftMargin: !this.isSinglePage,
                waitForCraneRightMargin: true
            };
        },
        /**
         * wait for crane message with localization
         * @return {string}
         */
        waitForCraneMessage: function () {
            return `${this.labels.waitForCraneTitle.part1} ${this.waitForCrane} ${this.labels.waitForCraneTitle.part2}`;
        },
        /**
         * content font size as per page selection
         * @return {string}
         */
        contentFontSize: function() {
            return GlobalState.isOnePage() ? '28px' : '32px';
        },
        /**
         * Returns the form widget definition for the field which has the isTileIndicatorColorField item attribute
         * as 'true'.
         * @returns {FormWidget|undefined}
         */
        tileIndicatorColorFormWidget: function() {
            /** @type {FormWidget|undefined} */
            return [].concat(...this.jobListFormWidgets).find(formWidget =>
                String(formWidget.itemAttributesPayload.isTileIndicatorColorField).toLowerCase() === 'true');
        },
        /**
         * Return the colors map to be used if the variform has been configured to enable tile indicator coloring
         * based on field value
         * @returns {Object.<string, string>|undefined}
         */
        tileColorsMap: function() {
            if (this.tileIndicatorColorFormWidget) {
                // eslint-disable-next-line no-unused-vars
                const {isTileIndicatorColorField, ...colorsMap} =
                    this.tileIndicatorColorFormWidget.itemAttributesPayload;
                if (colorsMap && Object.keys(colorsMap).length > 0) {
                    return colorsMap;
                }
            }
            return undefined;
        },
        /**
         * Returns the bg color style to be used for lane change plan tile based on whether the tile coloring
         * config is present in the variform.
         * @returns {Object|undefined}
         */
        lcpSequenceNbrBgColor: function() {
            return {backgroundColor: this.tileIndicatorColorFormWidget && this.tileColorsMap ?
                'var(--color-info)' : 'var(--color-container-background)'};
        }
    },
    /**
     * Updated section of this UI component
     */
    updated() {
        // initial scroll enable/disable after all job items render
        this.$nextTick(this.updateButtonAccess);
    },
    methods: {
        /**
         * Format seq number
         * @param {number} inNumber
         * @returns {number}
         */
        formatSequenceNumber(inNumber) {
            return inNumber < 10 ? '0' + inNumber : inNumber;
        },
        /**
         * Function to set the selected job view to be visible
         * @param {boolean} inVisible
         */
        setVisible(inVisible) {
            this.visible = inVisible;
        },
        /**
         * Function to set the Wait for Crane component to be visible
         * @param {boolean} inVisible
         */
        setWaitForCraneVisible(inVisible) {
            this.isWaitForCraneVisible = inVisible;
        },
        /**
         * Function to set the jobs to be visible
         * @param {boolean} inVisible
         * @param {boolean} inDoesLaneChangePlanHaveRTGmoves
         */
        notifySetJobsVisible(inVisible, inDoesLaneChangePlanHaveRTGmoves) {
            this.areJobsVisible = inVisible;
            this.doesLaneChangePlanHaveRTGmoves = inDoesLaneChangePlanHaveRTGmoves;
        },
        /**
         * Function to set the joblist
         * @param {Job[]} inJobList
         */
        setJobList(inJobList) {
            this.jobList = inJobList;
            this.selectedIndex = -1;
            this.key++;
            this.key %= 2;
        },
        /**
         * Set selected job and update key to force rerender
         * @param {Job | null} inSelectedJob
         */
        notifySelectedJob(inSelectedJob) {
            if (inSelectedJob == null || this.jobList == null) return;
            for (let i=0; i < this.jobList.length; i++) {
                if (this.jobList[i].equipmentId === inSelectedJob.equipmentId) {
                    this.selectedIndex = i;
                    break;
                }
            }
            this.key++;
            this.key %= 2;
        },
        /**
         * Callback function called when Job is clicked
         * @param {number} inIndex
         */
        jobListRender(inIndex) {
            this.selectedIndex = inIndex;
            if (this.jobClick != null) {
                this.jobClick(this.middleSectionPresenter, this.jobList[inIndex]);
            }
        },
        /**
         * Set the lane change plan
         * @param {LaneChangePayload} inLaneChangePayload
         */
        setLaneChangePlan(inLaneChangePayload) {
            if(inLaneChangePayload.laneChangePlan &&
                Object.keys(inLaneChangePayload.laneChangePlan).length !== 0) {
                this.laneChangeVisible = true;
                this.laneChangePlan = inLaneChangePayload.laneChangePlan;
                this.cheLastKnownPos = inLaneChangePayload.cheLastKnownPos;
            }
            else {
                this.laneChangeVisible = false;
            }
        },
        /**
         * Callback function called when lane change job is clicked
         */
        laneChangeClick() {
            /**
             * @type {LaneChangePayload}
             */
            const laneChangePayload = {
                laneChangePlan: this.laneChangePlan,
                cheLastKnownPos: this.cheLastKnownPos
            };
            if (this.laneChangePlanClick != null) {
                this.laneChangePlanClick(this.middleSectionPresenter, laneChangePayload);
            }
        },
        /**
         * Function to scroll to the new job list position using the job list area measurements and the scroll
         * direction.
         * @param {string} inDirection --> {'up', 'down'}
         */
        scrollToNewJobListPos(inDirection) {
            const jobListArea = this.$refs.jobListArea;
            const jobListAreaScrollTop = jobListArea.scrollTop;
            const jobListAreaScrollHeight = jobListArea.scrollHeight;
            const jobListAreaClientHeight = jobListArea.clientHeight;
            const jobListAreaPaddingTop = 14;
            const jobItemMargin = 8;
            const jobItemHeights = [];
            // Need to maintain order of job item heights for future calculations
            const jobListCardItemHeight = this.$refs.cardItem !== undefined
                ? this.$refs.cardItem.$el.clientHeight + jobItemMargin : 0;
            jobItemHeights.push(jobListCardItemHeight);
            this.$refs.jobItemRow.forEach(jobItem => jobItemHeights.push(jobItem.clientHeight));
            const jobListLcpItemHeight = this.$refs.lcpItem !== undefined
                ? this.$refs.lcpItem.clientHeight : 0;
            jobItemHeights.push(jobListLcpItemHeight);

            /**
             * @type {JobListScrollCalcData}
             */
            const measurements = {
                jobListAreaScrollTop,
                jobListAreaScrollHeight,
                jobListAreaClientHeight,
                jobListAreaPaddingTop,
                jobItemMargin,
                jobItemHeights
            };
            const newScrollPosition = calcNewJobListScrollPosition(inDirection, measurements);
            this.$refs.jobListArea.scrollTo(0, newScrollPosition);
        },
        /**
         * Function to update button access
         */
        updateButtonAccess() {
            if (this.updateScrollButtonAccess != null) {
                const jobListArea = this.$refs.jobListArea;
                const jobListAreaScrollTop = jobListArea.scrollTop;
                const jobListAreaScrollHeight = jobListArea.scrollHeight;
                const jobListAreaClientHeight = jobListArea.clientHeight;

                /**
                 * @type {JobListScrollToggleData}
                 */
                const measurements = {
                    jobListAreaScrollTop,
                    jobListAreaScrollHeight,
                    jobListAreaClientHeight
                };

                this.updateScrollButtonAccess.apply(
                    this.middleSectionPresenter, [this.middleSectionPresenter, measurements]
                );
            }
        },
        /**
         * Function to update Last Known Pos
         * @param {string} inLastKnownPosition
         */
        updateLastKnownPos(inLastKnownPosition) {
            this.cheLastKnownPos = inLastKnownPosition;
        },
        /**
         * Function to toggle theme between light/dark mode
         */
        toggleTheme() {
            this.theme = this.theme === 'theme-light' ? 'theme-dark' : 'theme-light';
        },
        /**
         * Function to set waitForCrane, interfering crane warning to the operator
         * @param {string} inWaitForCrane possibly interfering crane(s) with the top job
         */
        setWaitForCrane(inWaitForCrane) {
            this.waitForCrane = inWaitForCrane == null ? '' : inWaitForCrane;
        },
        /**
         * When a job is selected we want to expand it and fade other jobs.
         * This is a quick animation since we switch to the dispatch page once a job is selected.
         * @param {number} inIndex
         * @return {string} empty string active-job or .inactive-job
         */
        addSelectJobAnimation: function (inIndex) {
            if (this.selectedIndex === -1 || !this.isClickable()){
                return '';
            }
            if (inIndex === this.selectedIndex) {
                return 'active-job';
            } else {
                return 'inactive-job';
            }
        },
        /**
         * Get number of rehandle shadows
         * @param {Job} inJob
         * @return {number} number of rehandles
         */
        getNumberOfRehandleShadows(inJob) {
            // We only show rehandle shadows in Job list default page
            if (this.isSinglePage) {
                return 0;
            }
            return inJob.rehandleCount;
        },
        /**
         * Whether the current page mode is clickable
         * @return {boolean} true or false
         */
        isClickable() {
            return !this.isSinglePage;
        },
        /**
         * Function for checking whether the number of rehandle jobs is more than 5
         * @param {number} inNumberOfRehandles
         * @return {boolean}
         */
        isRehandleJobMoreThan5(inNumberOfRehandles) {
            return inNumberOfRehandles > 5;
        },
        /**
         * getter for rehandle shadow classes
         * @param {number} inNumberOfRehandles
         * @return {string}
         */
        rehandleShadowClass(inNumberOfRehandles){
            const rehandleClasses = [
                '',
                'oneRehandle',
                'twoRehandles',
                'threeRehandles',
                'fourRehandles',
                'fiveRehandles'
            ];
            if (inNumberOfRehandles < 0){
                console.warn(`Number of rehandles is not supported ${inNumberOfRehandles}`);
                return '';
            }
            if(inNumberOfRehandles > 5){
                return 'fiveRehandles';
            }
            return rehandleClasses[inNumberOfRehandles];
        },
        /**
         * Set form widgets for the job list tile variform.
         * @param {Array.<Array.<FormWidget>>} inFormWidgets
         */
        setJobListTileFormWidgets(inFormWidgets) {
            // to ensure the job list tile is reset before setting the new form widgets
            this.jobListFormWidgets = null;
            this.jobListFormWidgets = inFormWidgets;
        }
    }
};
</script>

<style scoped>
    .default-page {
        padding-top: 14px;
        margin-left: 5%;
        width: 85%;
    }
    .single-page {
        width: 100%;
        padding-top: 6px;
    }
    .jobItemRow {
        position: relative;
    }
    .waitForCraneLeftMargin {
        margin-left: 60px;
    }
    .sequence-number {
        --color-container-text: var(--color-dark);
        --color-container-background: var(--color-white);

        box-sizing: border-box;
        text-align: center;
        vertical-align: middle;
        line-height: 50px;
        width: 50px;
        height: 50px;
        border: 1px solid var(--color-dark);
        border-radius: 5px;
        font-size: 24px;
        font-weight: normal;
        color: var(--color-container-text);
    }
    .sequence-number.theme-dark {
        --color-container-text: var(--color-white);
        --color-container-background: var(--color-gray-dark);
    }
    .extra-rehandles-number {
        position: absolute;
        /* to vertically center: start at 50% job item row height, subtract extra 4px from job item margin
           (4px instead of 8px due to the 50%), subtract half of circle element height */
        top: calc(50% - 4px - 15px);
        /* width of 5 rehandles (40px), circle element width (30px), 10px space */
        right: calc(-40px - 30px - 10px);
        display: inline-block;
        box-sizing: border-box;
        text-align: center;
        line-height: 30px;
        vertical-align: middle;
        font-size: 14px;
        font-weight: bold;
        color: var(--color-container-text);
        width: 30px;
        height: 30px;
        border: 1px solid var(--color-dark);
        border-radius: 50%;
        background-color: #0096FF;
    }
    .item {
        display: flex;
        column-gap: 10px;
    }
    .item .promoted {
        /* also color rehandles */
        --color-container-background-90: var(--color-tertiary-90);
        --color-container-background-70: var(--color-tertiary-70);
        --color-container-background-50: var(--color-tertiary-50);
        --color-container-background-30: var(--color-tertiary-30);
        --color-container-background-10: var(--color-tertiary-10);
    }
    .lcp-icon {
        display: flex;
        flex-direction: column;
        justify-content: center;
        width: 49px;
        align-items: center;
        height: 100%;
    }
    .theme-dark .lcp-icon {
        filter: var(--filter-white);
    }
    img {
        width: 20px;
        height: 20px;
    }
    .active-job {
        transform: scale(1.1);
    }
    .inactive-job {
        opacity: 0.5;
    }
    .oneRehandle {
      box-shadow: 0 0 1px var(--color-dark),
        /*first shadow*/
      8px 0 0 var(--color-container-background-90),
      8px 0 4px rgba(0,28,56,.4);
      padding-right: 8px;
    }
    .twoRehandles {
      box-shadow: 0 0 1px var(--color-dark),
        /*first shadow*/
      8px 0 0 var(--color-container-background-90),
      8px 0 4px rgba(0,28,56,.4),
        /*second shadow*/
      16px 0 0 var(--color-container-background-70),
      16px 0 4px rgba(0,28,56,.4);
    }
    .threeRehandles {
      box-shadow: 0 0 1px var(--color-dark),
        /*first shadow*/
      8px 0 0 var(--color-container-background-90),
      8px 0 4px rgba(0,28,56,.4),
        /*second shadow*/
      16px 0 0 var(--color-container-background-70),
      16px 0 4px rgba(0,28,56,.4),
        /*third shadow*/
      24px 0 0 var(--color-container-background-50),
      24px 0 4px rgba(0,28,56,.4);
    }
    .fourRehandles {
      box-shadow: 0 0 1px var(--color-dark),
        /*first shadow*/
      8px 0 0 var(--color-container-background-90),
      8px 0 4px rgba(0,28,56,.4),
        /*second shadow*/
      16px 0 0 var(--color-container-background-70),
      16px 0 4px rgba(0,28,56,.4),
        /*third shadow*/
      24px 0 0 var(--color-container-background-50),
      24px 0 4px rgba(0,28,56,.4),
        /*fourth shadow*/
      32px 0 0 var(--color-container-background-30),
      32px 0 4px rgba(0,28,56,.4);
      width: calc(100% - 32px);
    }
    .fiveRehandles {
      box-shadow: 0 0 1px var(--color-dark),
        /*first shadow*/
      8px 0 0 var(--color-container-background-90),
      8px 0 4px rgba(0,28,56,.4),
        /*second shadow*/
      16px 0 0 var(--color-container-background-70),
      16px 0 4px rgba(0,28,56,.4),
        /*third shadow*/
      24px 0 0 var(--color-container-background-50),
      24px 0 4px rgba(0,28,56,.4),
        /*fourth shadow*/
      32px 0 0 var(--color-container-background-30),
      32px 0 4px rgba(0,28,56,.4),
        /*fifth shadow*/
      40px 0 0 var(--color-container-background-10),
      40px 0 4px rgba(0,28,56,.4);
    }
</style>
