/*eslint no-var: "error"*/
/*eslint no-console: "off"*/
/*eslint no-unused-vars: warn */
/*eslint max-len: ["error", { "code": 160 }] */
/*eslint-disable no-trailing-spaces */
/*eslint-env es6*/
// Put this file in path/to/plugin/amd/src

import {SimpleLine} from "./simpleline";
import {call} from 'core/ajax';
import notification from 'core/notification';
import {debounce} from './debounce';
import {get_strings} from 'core/str';
import {load_stringkeys, load_strings} from './string-helper';
import {objCopy,transportItem} from './studyplan-processor';
import Debugger from './debugger';
import {download,upload} from './downloader';

const STUDYPLAN_EDITOR_FIELDS = ['name','shortname','description','context_id',
                                 'slots','startdate','enddate','aggregation','aggregation_config'];

export default {
    STUDYPLAN_EDITOR_FIELDS: STUDYPLAN_EDITOR_FIELDS, // make copy available in plugin
    install(Vue/*,options*/){

        let debug = new Debugger("treestudyplan-editor");
        debug.enable();
        /************************************
         *                                  *
         * Treestudyplan Editor components  *
         *                                  *
         ************************************/

        /**
         * Check if element is visible
         * @param {Object} elem The element to check
         * @returns {boolean} True if visible
         */
        function isVisible(elem){
            return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
        }


        // Create new eventbus for interaction between item components
        const ItemEventBus = new Vue();

        let string_keys = load_stringkeys({
            conditions: [
                { value: null,  textkey: 'condition_default'},
                { value: 'ALL', textkey: 'condition_all'},
                { value: '67',  textkey: 'condition_67'},
                { value: '50',  textkey: 'condition_50'},
                { value: 'ANY', textkey: 'condition_any'},
            ],
        });

        let strings = load_strings({
            studyplan_text: {
                studyline_editmode: 'studyline_editmode',
                editmode_modules_hidden:'editmode_modules_hidden',
                studyline_add: 'studyline_add',
                add$core: 'add$core',
                edit$core: 'edit$core',
                studyline_name: 'studyline_name',
                studyline_name_ph: 'studyline_name_ph',
                studyline_shortname: 'studyline_shortname',
                studyline_shortname_ph: 'studyline_shortname_ph',
                studyline_color: 'studyline_color',
                associations: 'associations',
                associated_cohorts: 'associated_cohorts',
                associated_users: 'associated_users',
                studyline_edit: 'studyline_edit',
                studyplan_name: 'studyplan_name',
                studyplan_name_ph: 'studyplan_name_ph',
                studyplan_shortname: 'studyplan_shortname',
                studyplan_shortname_ph: 'studyplan_shortname_ph',
                studyplan_description: 'studyplan_description',
                studyplan_description_ph: 'studyplan_description_ph',
                studyplan_slots: 'studyplan_slots',
                studyplan_startdate: 'studyplan_startdate',
                studyplan_enddate: 'studyplan_enddate',
            },
            studyplan_advanced: {
                advanced_tools: 'advanced_tools',
                confirm_cancel: 'confirm_cancel',
                confirm_ok: 'confirm_ok',
                success$core: 'success$core',
                error$core: 'failed$core',
                advanced_converted: 'advanced_converted',
                advanced_skipped: 'advanced_skipped',
                advanced_failed: 'advanced_failed',
                advanced_locked: 'advanced_locked',
                advanced_multiple: 'advanced_multiple',
                advanced_error: 'advanced_error',
                advanced_tools_heading: 'advanced_tools_heading',
                advanced_warning_title: 'advanced_warning_title',
                advanced_warning: 'advanced_warning',
                advanced_pick_scale: 'advanced_pick_scale',
                advanced_course_manipulation_title: 'advanced_course_manipulation_title',
                advanced_force_scale_title: 'advanced_force_scale_title',
                advanced_force_scale_desc: 'advanced_force_scale_desc',
                advanced_force_scale_button: 'advanced_force_scale_button',
                advanced_disable_autoenddate_title: 'advanced_disable_autoenddate_title',
                advanced_disable_autoenddate_desc: 'advanced_disable_autoenddate_desc',
                advanced_disable_autoenddate_button: 'advanced_disable_autoenddate_button',
                advanced_confirm_header: 'advanced_confirm_header',
                advanced_force_scale_confirm: 'advanced_force_scale_confirm',
                advanced_import: 'advanced_import',
                advanced_export: 'advanced_export',
                advanced_export_csv: 'advanced_export_csv',
                advanced_import_from_file: 'advanced_import_from_file',
                advanced_purge: "advanced_purge",
                advanced_purge_expl: "advanced_purge_expl",
                advanced_cascade_cohortsync_title: "advanced_cascade_cohortsync_title",
                advanced_cascade_cohortsync_desc: "advanced_cascade_cohortsync_desc",
                advanced_cascade_cohortsync: "advanced_cascade_cohortsync",
            },
            studyplan_edit: {
                studyplan_edit: 'studyplan_edit',
                studyplan_name: 'studyplan_name',
                studyplan_name_ph: 'studyplan_name_ph',
                studyplan_shortname: 'studyplan_shortname',
                studyplan_shortname_ph: 'studyplan_shortname_ph',
                studyplan_description: 'studyplan_description',
                studyplan_description_ph: 'studyplan_description_ph',
                studyplan_context: 'studyplan_context',
                studyplan_slots: 'studyplan_slots',
                studyplan_startdate: 'studyplan_startdate',
                studyplan_enddate: 'studyplan_enddate',
                choose_aggregation_style: 'choose_aggregation_style',
                setting_bistate_thresh_excellent: 'setting_bistate_thresh_excellent',
                settingdesc_bistate_thresh_excellent: 'settingdesc_bistate_thresh_excellent',
                setting_bistate_thresh_good: 'setting_bistate_thresh_good',
                settingdesc_bistate_thresh_good: 'settingdesc_bistate_thresh_good',
                setting_bistate_thresh_completed: 'setting_bistate_thresh_completed',
                settingdesc_bistate_thresh_completed: 'settingdesc_bistate_thresh_completed',
                setting_bistate_support_failed: 'setting_bistate_support_failed',
                settingdesc_bistate_support_failed: 'settingdesc_bistate_support_failed',
                setting_bistate_thresh_progress: 'setting_bistate_thresh_progress',
                settingdesc_bistate_thresh_progress: 'settingdesc_bistate_thresh_progress',
                setting_bistate_accept_pending_submitted: 'setting_bistate_accept_pending_submitted',
                settingdesc_bistate_accept_pending_submitted: 'settingdesc_bistate_accept_pending_submitted',
            },
            studyplan_associate: {
                associations: 'associations',
                associated_cohorts: 'associated_cohorts',
                associated_users: 'associated_users',
                associate_cohorts: 'associate_cohorts',
                associate_users: 'associate_users',
                add_association: 'add_association',
                delete_association: 'delete_association',
                associations_empty: 'associations_empty',
                associations_search: 'associations_search',
                cohorts: 'cohorts',
                users: 'users',
                selected: 'selected',
                name: 'name',
                context: 'context',
            },
            item_text: {
                select_conditions: "select_conditions",
                item_configuration: "item_configuration",
            },
            item_course_text: {
                select_conditions: "select_conditions",
                select_grades: "select_grades",
                coursetiming_past: "coursetiming_past",
                coursetiming_present: "coursetiming_present",
                coursetiming_future: "coursetiming_future",      
                grade_include: "grade_include",          
                grade_require: "grade_require",          
            },
            invalid: {
                error: 'error',
            },
            completion: {
                completion_completed: "completion_completed",
                completion_incomplete: "completion_incomplete",
                aggregation_all: "aggregation_all",
                aggregation_any: "aggregation_any",
                aggregation_overall_all: "aggregation_overall_all",
                aggregation_overall_any: "aggregation_overall_any",
                completion_not_configured: "completion_not_configured",
                configure_completion: "configure_completion",
            },
            badge: {
                share_badge: "share_badge",
                dateissued: "dateissued",
                dateexpire: "dateexpire",
                badgeinfo: "badgeinfo",
            },

        });


        /*
        * T-STUDYPLAN-ADVANCED
        */
        Vue.component('t-studyplan-advanced', {
            props: {
                value: {
                    type: Object,
                    default(){ return null;},
                },
            },
            data() {
                return {
                    force_scales: {
                        selected_scale: null,
                        result: [],
                    },
                    text: strings.studyplan_advanced,
                };
            },
            created() {
            },
            mounted() {
            },
            updated() {

            },
            computed: {
                scales(){
                    return [{
                        id: null,
                        disabled: true,
                        name: this.text.advanced_pick_scale,
                    }].concat(this.value.advanced.force_scales.scales);
                },
            },
            methods: {
                disable_autoenddate(){
                    const self=this;
                    call([{
                        methodname: 'local_treestudyplan_disable_autoenddate',
                        args: {
                            studyplan_id: this.value.id,
                        }
                    }])[0].done(function(response){
                        self.$bvModal.msgBoxConfirm((response.success?self.text.success$core:self.text.error$core)
                             + "\n" + response.msg);
                    }).fail(notification.exception);
                },
                force_scales_start(){
                    // set confirmation box
                    const self=this;
                    this.$bvModal.msgBoxConfirm(this.text.advanced_force_scale_confirm,{
                        title: this.text.advanced_force_scale_confirm,
                        okVariant: 'danger',
                        okTitle: this.text.confirm_ok,
                        cancelTitle: this.text.confirm_cancel,
                    }).then( value => {
                        if(value == true){
                            call([{
                                methodname: 'local_treestudyplan_force_studyplan_scale',
                                args: {
                                    studyplan_id: this.value.id,
                                    scale_id: this.force_scales.selected_scale,
                                }
                            }])[0].done(function(response){
                                self.force_scales.result = response;
                            }).fail(notification.exception);
                        }
                    });
                },
                export_plan(format){
                    const self = this;
                    if(format == undefined || !["json","csv"].includes(format)){
                        format = "json";
                    }
                    call([{
                            methodname: 'local_treestudyplan_export_plan',
                            args: {
                                studyplan_id: this.value.id,
                                format: format,
                            },
                        }])[0].done(function(response){
                            
                            download(self.value.shortname+"."+format,response.content,response.format);
                        }).fail(notification.exception);    
                },
                import_studylines(){
                    //const self = this;
                    upload((filename,content)=>{
                        call([{
                            methodname: 'local_treestudyplan_import_studylines',
                            args: {
                                studyplan_id:  this.value.id,
                                content: content,
                                format: "application/json",
                            },
                        }])[0].done(function(response){
                            if(response.success){
                                location.reload();
                            } else {
                                debug.error("Import failed: ",response.msg);
                            }
                            
                        }).fail(notification.exception);  
                    }, "application/json");
                },
                purge_studyline(){
                    call([{
                        methodname: 'local_treestudyplan_delete_studyplan',
                        args: {
                            id:  this.value.id,
                            force: true,
                        },
                    }])[0].done(function(response){
                        if(response.success){
                            location.reload();
                        } else {
                            debug.error("Could not delete plan: ",response.msg);
                        }
                        
                    }).fail(notification.exception);  
                },
                cascade_cohortsync(){
                    const self = this;
                    call([{
                        methodname: 'local_treestudyplan_cascade_cohortsync',
                        args: {
                            studyplan_id:  this.value.id,
                        },
                    }])[0].done(function(response){
                        self.$bvModal.msgBoxOk(response.success?self.text.success$core:self.text.error$core,
                            { title: self.text.advanced_cascade_cohortsync});
                    }).fail(notification.exception);  
                },
                modal_close(){
                    this.force_scales.result = [];
                }
            },
            template: 
            `
            <span>
                <a  v-if="value.advanced" 
                    href='#' 
                    @click.prevent='' 
                    class='text-danger' 
                    v-b-modal="'t-studyplan-'+value.id+'-advanced'"
                    ><i class='fa fa-cog'></i>{{text.advanced_tools}}</a>
                <b-modal v-if="value.advanced" 
                    :id="'t-studyplan-'+value.id+'-advanced'"
                    size="lg"
                    :title="text.advanced_tools_heading"
                    ok-only
                    @hide="modal_close"
                    >
                    <b-card no-body>
                    <b-tabs card>
                        <b-tab :title="text.advanced_warning_title" active>
                            {{ text.advanced_warning}}
                        </b-tab>
                        <b-tab :title="text.advanced_course_manipulation_title" >
                            <b-container>
                            <b-row><b-col cols="*">
                                <h3>{{ text.advanced_cascade_cohortsync_title}}</h3>
                                    {{ text.advanced_cascade_cohortsync_desc}}
                            </b-col></b-row>
                            <b-row><b-col cols="*">
                                <b-button 
                                    variant="info"
                                    @click="cascade_cohortsync"
                                >{{ text.advanced_cascade_cohortsync}}</b-button>
                            </b-col></b-row>
                            <b-row class="mt-3"><b-col cols="*">
                                <h3>{{ text.advanced_force_scale_title}}</h3>
                                {{ text.advanced_force_scale_desc}}
                            </b-col></b-row>
                            <b-row>
                                <b-col>
                                    <b-form-select v-model="force_scales.selected_scale" 
                                    :options="scales" text-field="name" value-field="id"
                                    ></b-form-select>
                                </b-col>
                                <b-col cols="4">
                                    <b-button 
                                        variant="danger"
                                        :disabled="force_scales.selected_scale == null"
                                        @click="force_scales_start"
                                    >{{ text.advanced_force_scale_button}}</b-button>
                                </b-col>

                            </b-row>
                            <b-row class="mt-3"><b-col cols="*">
                                <ul class='t-advanced-scrollable' v-if="force_scales.result.length > 0">
                                    <li v-for="c in force_scales.result">
                                        <span class='t-advanced-coursename'>{{c.course.fullname}}</span>
                                        <ul v-if="c.grades.length > 0">
                                            <li v-for='g in c.grades'>
                                                <span class='t-advanced-gradename'>{{g.name}}</span>
                                                <span v-if="g.changed == 'converted'" class='t-advanced-status changed'
                                                    >{{text.advanced_converted}}</span
                                                ><span v-else-if="g.changed == 'skipped'" class='t-advanced-status skipped'
                                                    >{{text.advanced_skipped}}</span
                                                >
                                                <span v-else class='t-advanced-status skipped'
                                                    >{{text.advanced_error}}</span
                                                >
                                            </li>
                                        </ul>
                                    </li>
                                </ul>
                            </b-col></b-row>
                            <b-row><b-col cols="*">
                                <h3>{{ text.advanced_disable_autoenddate_title}}</h3>
                                {{ text.advanced_disable_autoenddate_desc}}
                            </b-col></b-row>
                            <b-row><b-col cols="*">
                                <b-button 
                                    variant="danger"
                                    @click="disable_autoenddate"
                                >{{ text.advanced_disable_autoenddate_button}}</b-button>
                            </b-col></b-row>
                            </b-container>
                        </b-tab>
                        <b-tab :title='text.advanced_export'>
                            <b-button   
                                    variant="primary"
                                    @click="export_plan"
                                >{{ text.advanced_export}}</b-button>
                            <b-button
                                    variant="danger"
                                    @click="import_studylines"
                                >{{ text.advanced_import}}</b-button>
                            <b-button   
                                variant="primary"
                                @click="export_plan('csv')"
                            >{{ text.advanced_export_csv}}</b-button>
                    </b-tab>
                        <b-tab :title='text.advanced_purge'>
                            <p>{{text.advanced_purge_expl}}</p>
                            <p><b-button
                                    variant="danger"
                                    @click="purge_studyline"
                                >{{ text.advanced_purge}}</b-button></p>
                        </b-tab>                    </b-tabs>
                    </b-card>
                </b-modal>     
            </span>           
            `
        });


        /*
        * T-STUDYPLAN-EDIT
        */
        Vue.component('t-studyplan-edit', {
            props: {
                'value' :{
                    type: Object,
                    default(){ return null;},
                },
                'mode' :{
                    type: String,
                    default() { return "edit";},
                },
                'type' :{
                    type: String,
                    default() { return "link";},
                },
                'variant' : {
                    type: String,
                    default() { return "";},
                }
            },
            data() {
                return {
                    show: false,
                    config: {
                        userfields: [ 
                            { key: "selected",},
                            { key: "firstname", "sortable": true,},
                            { key: "lastname", "sortable": true,},
                        ],
                        cohortfields:[ 
                            { key: "selected",},
                            { key: "name", "sortable": true,},
                            { key: "context", "sortable": true,},
                        ]
                    },
                    editdata: {   
                        name: '',
                        shortname: '',
                        description: '',
                        context_id: 1,
                        slots : 4,
                        startdate: (new Date()).getFullYear() + '-08-01',
                        enddate:  ((new Date()).getFullYear()+1) + '-08-01',
                        aggregation: 'bistate',
                        aggregation_config: '',
                    },
                    aggregation_parsed: {

                    },
                    aggregators: [],
                    categories: [ { context_id: 1, category: { path: "System"}}], // overwritten during load...
                    text: strings.studyplan_edit,
                };
            },
            created() {
                // retrieve aggregator info
                const self = this;
                call([{
                    methodname: 'local_treestudyplan_list_aggregators',
                    args: [],
                }])[0].done(function(response){
                    self.aggregators = response;
                    for(const ix  in self.aggregators){
                        const ag = self.aggregators[ix];
                        
                        try{
                            if(ag.defaultconfig && ag.defaultconfig.length > 0){
                                self.aggregation_parsed[ag.id] = JSON.parse(ag.defaultconfig);
                            }
                        }
                        catch(e){
                            debug.warn(e);
                        }
                    }
                }).fail(notification.exception);
                call([{
                    methodname: 'local_treestudyplan_list_accessible_categories',
                    args: {operation: "edit",}
                }])[0].done(function(response){
                    for(const ix in response){
                        const cat = response[ix];
                        cat.category.pathname = cat.category.path.join(" / ");
                    }
                    self.categories = response;
                }).fail(notification.exception);
            },
            mounted() {
            },
            updated() {

            },
            computed: {
            },
            methods: {
                editPlanStart(){
                    if(this.mode != 'create'){
                        objCopy(this.editdata,this.value,STUDYPLAN_EDITOR_FIELDS);
                    }
                    // decode the aggregation config data that is stored
                    if(this.editdata.aggregation_config && this.editdata.aggregation_config.length > 0){
                        try{
                            this.aggregation_parsed[this.editdata.aggregation] = JSON.parse(this.editdata.aggregation_config);
                        }
                        catch(e){
                            debug.warn(e);
                        }
                    }
                    this.show = true;
                },
                editPlanFinish(){
                    const self = this;
                    let args = { };
                    let method =  'local_treestudyplan_edit_studyplan';
                    if(this.mode == 'create'){
                        method = 'local_treestudyplan_add_studyplan';
                    } else {
                        args['id'] = this.value.id;
                    }

                    // store the configuration for this aggregation type if it is relevant
                    if(this.aggregation_parsed[this.editdata.aggregation]){
                        this.editdata.aggregation_config = JSON.stringify(this.aggregation_parsed[this.editdata.aggregation]);
                    }
                    objCopy(args,this.editdata,STUDYPLAN_EDITOR_FIELDS);

                    call([{
                        methodname: method,
                        args: args
                    }])[0].done(function(response){
                        if(self.mode == 'create'){
                            self.$emit("created", response);
                            // And reset the edit fields to default
                            self.editdata = {   
                                name: '',
                                shortname: '',
                                description: '',
                                context_id: 1,
                                slots : 4,
                                startdate: (new Date()).getFullYear() + '-08-01',
                                enddate: ((new Date()).getFullYear()+1) + '-08-01',
                                aggregation: 'bistate',
                                aggregation_config: '',
                            };
                        }
                        else {
                            // determine if the plan moved context...
                            const moved_from = self.value.context_id;
                            const moved_to = response.context_id;
                            const moved = (moved_from != moved_to);

                            objCopy(self.value,response,STUDYPLAN_EDITOR_FIELDS);
                            self.$emit('input',self.value);
                            if(moved){
                                self.$emit('moved',self.value,moved_from, moved_to);
                            }
                        }
                    }).fail(notification.exception);
                },
                numberFilter(value){
                    return value;
                }
            }
            ,
            template: 
            `
            <span class='s-studyplan-edit'>
                <b-button :variant="variant" v-if='type == "button"' @click.prevent='editPlanStart()'
                    ><slot><i class='fa fa-pencil'></i></slot></b-button>
                <a variant="variant" v-else href='#' @click.prevent='editPlanStart()'
                    ><slot><i class='fa fa-pencil'></i></slot></a>
                <b-modal 
                    v-model="show"
                    size="lg"
                    ok-variant="primary"
                    :title="text.studyplan_edit"
                    @ok="editPlanFinish()"
                    :ok-disabled="Math.min(editdata.name.length,editdata.shortname.length) == 0"
                    >
                    <b-container>
                        <b-row>
                            <b-col cols="4">{{ text.studyplan_name}}</b-col>
                            <b-col cols="8">
                                <b-form-input v-model="editdata.name" 
                                :state='editdata.name.length>0'
                                :placeholder="text.studyplan_name_ph"></b-form-input>
                            </b-col>
                        </b-row>
                        <b-row>
                            <b-col cols="4">{{ text.studyplan_shortname}}</b-col>
                            <b-col cols="8">
                                <b-form-input v-model="editdata.shortname" 
                                :state='editdata.shortname.length>0'
                                :placeholder="text.studyplan_shortname_ph"></b-form-input>
                            </b-col>
                        </b-row>
                        <b-row>
                            <b-col cols="4">{{ text.studyplan_description}}</b-col>
                            <b-col cols="8">
                                <b-form-input v-model="editdata.description" 
                                :placeholder="text.studyplan_description_ph"></b-form-input>
                            </b-col>
                        </b-row>
                        <b-row>
                            <b-col cols="4">{{ text.studyplan_context}}</b-col>
                            <b-col cols="8">
                                <b-form-select v-model="editdata.context_id" 
                                :options="categories" text-field="category.pathname" value-field="context_id"
                                ></b-form-select>
                            </b-col>
                        </b-row>
                        <b-row>
                            <b-col cols="4">{{ text.studyplan_slots}}</b-col>
                            <b-col cols="8">
                                <b-form-input type=number v-model="editdata.slots"></b-form-input>
                            </b-col>
                        </b-row>
                        <b-row>
                            <b-col cols="4">{{ text.studyplan_startdate}}</b-col>
                            <b-col cols="8">
                                <b-form-datepicker v-model="editdata.startdate"></b-form-datepicker>
                            </b-col>
                        </b-row>
                        <b-row>
                            <b-col cols="4">{{ text.studyplan_enddate}}</b-col>
                            <b-col cols="8">
                                <b-form-datepicker v-model="editdata.enddate" ></b-form-datepicker>
                            </b-col>
                        </b-row>
                        <b-row>
                            <b-col cols="4">{{ text.choose_aggregation_style}}</b-col>
                            <b-col cols="8">
                                <b-form-select v-model="editdata.aggregation" 
                                :options="aggregators" text-field="name" value-field="id"
                                ></b-form-select>
                            </b-col>
                        </b-row>
                        <template v-if="aggregation_parsed.bistate && editdata.aggregation == 'bistate'">
                            <b-row >
                                <b-col cols="4">{{ text.setting_bistate_thresh_excellent}}</b-col>
                                <b-col cols="8">
                                    <b-form-input v-model="aggregation_parsed.bistate.thresh_excellent" 
                                    type="number" number :formatter="numberFilter"
                                    ></b-form-input>
                                </b-col>
                            </b-row>
                            <b-row >
                                <b-col cols="4">{{ text.setting_bistate_thresh_good}}</b-col>
                                <b-col cols="8">
                                    <b-form-input v-model="aggregation_parsed.bistate.thresh_good" 
                                    type="number" number :formatter="numberFilter"
                                    ></b-form-input>
                                </b-col>
                            </b-row>
                            <b-row >
                                <b-col cols="4">{{ text.setting_bistate_thresh_completed}}</b-col>
                                <b-col cols="8">
                                    <b-form-input v-model="aggregation_parsed.bistate.thresh_completed" 
                                    type="number" number :formatter="numberFilter"
                                    ></b-form-input>
                                </b-col>
                            </b-row>       
                            <b-row>
                                <b-col cols="4">{{ text.setting_bistate_thresh_progress}}</b-col>
                                <b-col cols="8">
                                    <b-form-input v-model="aggregation_parsed.bistate.thresh_progress" 
                                    type="number" number :formatter="numberFilter"
                                    ></b-form-input>
                                </b-col>
                            </b-row>          
                            <b-row><b-col cols="*">&nbsp;</b-col></b-row>
                            <b-row>
                                <b-col cols="7">{{ text.setting_bistate_support_failed}}</b-col>
                                <b-col cols="3">
                                    <b-form-checkbox v-model="aggregation_parsed.bistate.use_failed" 
                                    ></b-form-checkbox>
                                </b-col>
                            </b-row>
                            <b-row><b-col cols="*">&nbsp;</b-col></b-row>
                            <b-row >
                                <b-col cols="7">{{ text.setting_bistate_accept_pending_submitted}}</b-col>
                                <b-col cols="3">
                                    <b-form-checkbox v-model="aggregation_parsed.bistate.accept_pending_as_submitted" 
                                    ></b-form-checkbox>
                                </b-col>
                            </b-row>       
                        </template>                  
                    </b-container>
                </b-modal>     
            </span>           
            `
        });

        /*
        * T-STUDYPLAN-ASSOCIATE
        */
        Vue.component('t-studyplan-associate', {
            props: ['value',],
            data() {
                return {
                    show: false,
                    config: {
                        userfields: [ 
                            { key: "selected",},
                            { key: "firstname", "sortable": true,},
                            { key: "lastname", "sortable": true,},
                        ],
                        cohortfields:[ 
                            { key: "selected",},
                            { key: "name", "sortable": true,},
                            { key: "context", "sortable": true,},
                        ]
                    },
                    association: {
                        cohorts: [],
                        users: [],
                    },
                    loading: {
                        cohorts: false,
                        users: false,
                    },
                    search: {users: [], cohorts:[]},
                    selected: {
                         search: {users: [] , cohorts:[]}, 
                         associated: {users: [] , cohorts:[]}
                    },              
                    text: strings.studyplan_associate,
                };
            },
            created() {
            
            },
            mounted() {
            },
            updated() {

            },
            methods: {
                showModal(){
                    this.show = true;
                    this.loadAssociations();
                },
                cohortOptionModel(c){
                    return {
                        value: c.id,
                        text: c.name + ' (' + c.context.path.join(' / ') + ')',
                    };
                },
                userOptionModel(u){
                    return {
                        value: u.id,
                        text: u.firstname + ' ' + u.lastname,
                    };
                },
                loadAssociations(){
                    const self = this;
                    self.loading.cohorts = true;
                    self.loading.users = true;
                    call([{
                        methodname: 'local_treestudyplan_associated_users',
                        args: { studyplan_id: self.value.id,}
                    }])[0].done(function(response){
                        self.association.users = response.map(self.userOptionModel);
                        self.loading.users = false;
                    }).fail(notification.exception);         

                    call([{
                        methodname: 'local_treestudyplan_associated_cohorts',
                        args: { studyplan_id: self.value.id,}
                    }])[0].done(function(response){
                        self.association.cohorts = response.map(self.cohortOptionModel);
                        self.loading.cohorts = false;
                    }).fail(notification.exception);                             
                },                
                searchCohorts(searchtext){
                    const self = this;

                    if(searchtext.length > 0)
                    {
                        call([{
                            methodname: 'local_treestudyplan_list_cohort',
                            args: { like: searchtext, exclude_id: self.value.id}
                        }])[0].done(function(response){
                            self.search.cohorts = response.map(self.cohortOptionModel);
                        }).fail(notification.exception);                
                    }
                    else {
                        self.search.cohorts = [];
                    }
                },
                cohortAssociate(){
                    const self = this;
                    let requests = [];
                    const associated = self.association.cohorts;
                    const search = self.search.cohorts;
                    const searchselected = self.selected.search.cohorts;
                    for(const i in searchselected){
                        const r = searchselected[i];                    
                        requests.push({
                            methodname: 'local_treestudyplan_connect_cohort',
                            args: {studyplan_id: self.value.id, cohort_id: r},
                            fail: notification.exception,
                            done: function(response){
                                if(response.success){
                                    transportItem(associated,search,r);
                                }
                            }
                        });
                    }
                    call(requests);
                },
                cohortDisassociate(){
                    const self = this;
                    let requests = [];
                    const associatedselected = self.selected.associated.cohorts;
                    const associated = self.association.cohorts;
                    const search =  self.search.cohorts;
                    for(const i in associatedselected){
                        const r = associatedselected[i];                    
                        requests.push({
                            methodname: 'local_treestudyplan_disconnect_cohort',
                            args: {studyplan_id: self.value.id, cohort_id: r},
                            fail: notification.exception,
                            done: function(response){
                                if(response.success){
                                    transportItem(search,associated,r);
                                }
                            }
                        });
                    }
                    call(requests);
                },       
                searchUsers(searchtext){
                    const self = this;
                    if(searchtext.length > 0)
                    {
                        call([{
                            methodname: 'local_treestudyplan_find_user',
                            args: { like: searchtext, exclude_id: self.value.id}
                        }])[0].done(function(response){
                            self.search.users = response.map(self.userOptionModel);
                        }).fail(notification.exception);                
                    }
                    else {
                        self.search.users = [];
                    }
                },
                userAssociate(){
                    const self = this;
                    let requests = [];
                    const associated = self.association.users;
                    const search =  self.search.users;
                    const searchselected =  self.selected.search.users;
                    for(const i in searchselected){
                        const r = searchselected[i];                    
                        
                        requests.push({
                            methodname: 'local_treestudyplan_connect_user',
                            args: {studyplan_id: self.value.id, user_id: r},
                            fail: notification.exception,
                            done: function(response){
                                if(response.success){
                                    transportItem(associated,search,r);
                                }
                            }
                        });
                    }
                    call(requests);
                },
                userDisassociate(){
                    const self = this;
                    let requests = [];
                    const associated = self.association.users;
                    const associatedselected = self.selected.associated.users;
                    const search = self.search.users;
                    for(const i in associatedselected){
                        const r = associatedselected[i];                    
                        
                        requests.push({
                            methodname: 'local_treestudyplan_disconnect_user',
                            args: {studyplan_id: self.value.id, user_id: r},
                            fail: notification.exception,
                            done: function(response){
                                if(response.success){
                                    transportItem(search,associated,r);
                                }
                            }
                        });
                    }
                    call(requests);
                },
            }
            ,
            template: 
`
<span class='s-studyplan-associate'
    ><a href='#' @click.prevent="showModal" ><slot><i class='fa fa-users'></i></slot></a>
    <b-modal
        v-model="show" 
        size="lg" 
        ok-variant="primary"
        :title="text.associations + ' - ' + value.name"
        ok-only>
        <b-tabs class='s-studyplan-associate-window'>
            <b-tab :title="text.cohorts">
                <b-container>
                    <b-row class='mb-2 mt-2'>
                        <b-col>{{text.associated_cohorts}}</b-col>
                        <b-col>{{text.associate_cohorts}}</b-col>
                    </b-row>
                    <b-row class='mb-2'>
                        <b-col>
                        </b-col>
                        <b-col>
                            <b-form-input 
                                type="text" @input="searchCohorts($event)" 
                                :placeholder="text.search"></b-form-input>
                        </b-col>
                    </b-row>
                    <b-row>
                        <b-col>
                            <b-form-select
                                multiple
                                v-model="selected.associated.cohorts"
                                :options="association.cohorts"
                                :select-size="10"
                                ></b-form-select>
                        </b-col>
                        <b-col>
                            <b-form-select
                                multiple
                                v-model="selected.search.cohorts"
                                :options="search.cohorts"
                                :select-size="10"
                                ></b-form-select>
                        </b-col>
                    </b-row>
                    <b-row class='mt-2'>
                        <b-col>
                            <b-button variant='danger' @click="cohortDisassociate()"
                                ><i class='fa fa-chain-broken'></i>&nbsp;{{text.delete_association}}</b-button>
                        </b-col>
                        <b-col>
                            <b-button variant='success' @click="cohortAssociate()"
                                ><i class='fa fa-link'></i>&nbsp;{{text.add_association}}</b-button>                            
                        </b-col>
                    </b-row>
                </b-container>
            </b-tab>
            <b-tab :title="text.users">
                <b-container>
                    <b-row class='mb-2 mt-2'>
                        <b-col>{{text.associated_users}}</b-col>
                        <b-col>{{text.associate_users}}</b-col>
                    </b-row>
                    <b-row class='mb-2'>
                        <b-col>
                        </b-col>
                        <b-col>
                            <b-form-input 
                                type="text" 
                                @input="searchUsers($event)" 
                                placeholder="Search users"></b-form-input>
                        </b-col>
                    </b-row>
                    <b-row>
                        <b-col>
                            <b-form-select
                                multiple
                                v-model="selected.associated.users"
                                :options="association.users"
                                :select-size="10"
                                ></b-form-select>
                        </b-col>
                        <b-col>
                            <b-form-select
                                multiple
                                v-model="selected.search.users"
                                :options="search.users"
                                :select-size="10"
                                ></b-form-select>
                        </b-col>
                    </b-row>
                    <b-row class='mt-2'>
                        <b-col>
                                <b-button variant='danger' @click="userDisassociate()"
                                ><i class='fa fa-chain-broken'></i>&nbsp;{{text.delete_association}}</b-button>
                        </b-col>
                        <b-col>
                            <b-button variant='success' @click="userAssociate()"
                                ><i class='fa fa-link'></i>&nbsp;{{text.add_association}}</b-button>              
                        </b-col>
                    </b-row>                                
                </b-container>                            
            </b-tab>
        </b-tabs>
    </b-modal>
</span>               
`
        });

        /*
        * T-STUDYPLAN
        */
        Vue.component('t-studyplan', {
            props: ['value', 'index'],
            data() {
                return {
                    config: {
                        userfields: [ 
                            { key: "selected",},
                            { key: "firstname", "sortable": true,},
                            { key: "lastname", "sortable": true,},
                        ],
                        cohortfields:[ 
                            { key: "selected",},
                            { key: "name", "sortable": true,},
                            { key: "context", "sortable": true,},
                        ]
                    },
                    create: {
                        studyline: {
                            'name': '',
                            'shortname': '',
                            'color': '#DDDDDD',
                        },
                    },
                    edit: {
                        studyline: {
                            editmode: false,
                            data: {   
                                name: '',
                                shortname: '',
                                color: '#DDDDDD',
                            },
                            original: {},
                        },
                        studyplan: {
                            data: {   
                                name: '',
                                shortname: '',
                                description: '',
                                slots : 4,
                                startdate: '2020-08-01',
                                enddate: '',
                                aggregation: '',
                                aggregation_config: '',
                                aggregation_info: {
                                    useRequiredGrades: true,
                                    useItemCondition: false,
                                },

                            },
                            original: {},
                        }
                    },
                    text: strings.studyplan_text,
                };
            },
            created() {
            
            },
            mounted() {
                if(this.value.studylines.length == 0){
                    // start in editmode if studylines are empty
                    this.edit.studyline.editmode = true;
                }  
                this.$root.$emit('redrawLines');
            },
            updated() {
                console.info("UPDATED Studyplan");
                this.$root.$emit('redrawLines');
                ItemEventBus.$emit('redrawLines');
            },
            computed: {
                columns() {
                    return 1+ (this.value.slots * 2);
                },
                columns_stylerule() {
                    // Uses css variables, so width for slots and filters can be configured in css
                    let s = "grid-template-columns: var(--studyplan-filter-width)"; // use css variable here
                    for(let i=0; i<this.value.slots;i++){
                        s+= " var(--studyplan-course-width) var(--studyplan-filter-width)";
                    }
                    return s+";";
                }
            },
            methods: {
                countLineLayers(line){
                    let maxLayer = -1;
                    for(let i = 0; i <= this.value.slots; i++){
                        const slot = line.slots[i];
                        // Determine the amount of used layers in a studyline slit
                        for(const ix in line.slots[i].competencies){
                            const item = line.slots[i].competencies[ix];
                            if(item.layer > maxLayer){
                                maxLayer = item.layer;
                            }
                        }
                        for(const ix in line.slots[i].filters){
                            const item = line.slots[i].filters[ix];
                            if(item.layer > maxLayer){
                                maxLayer = item.layer;
                            }
                        }
                    }
                    return maxLayer+1;
                },
                slotsempty(slots) {
                    if(Array.isArray(slots)){
                        let count = 0;
                        for(let i = 0; i < slots.length; i++) {
                            if(Array.isArray(slots[i].competencies)){
                                count += slots[i].competencies.length;
                            }
                            if(Array.isArray(slots[i].filters)){
                                count += slots[i].filters.length;
                            }
                        }
                        return (count == 0);
                    } else {
                        return false;
                    }
                },
                movedStudyplan(plan,from,to) {
                    this.$emit('moved',plan,from,to); // Throw the event up....
                },
                addStudyLine(studyplan,newlineinfo) {
                    call([{
                        methodname: 'local_treestudyplan_add_studyline',
                        args: {
                            'studyplan_id': studyplan.id,
                            'name': newlineinfo.name,
                            'shortname': newlineinfo.shortname,
                            'color': newlineinfo.color,
                            'sequence': studyplan.studylines.length,
                        }
                    }])[0].done(function(response){
                        debug.info("New studyline:",response);
                        studyplan.studylines.push(response);
                        newlineinfo.name = '';
                        newlineinfo.shortname = '';
                        newlineinfo.color = "#dddddd";
                    }).fail(notification.exception);
                },
                editLineStart(line) {
                    Object.assign(this.edit.studyline.data,line);
                    this.edit.studyline.original = line;
                    this.$bvModal.show('modal-edit-studyline-'+this.value.id);
                },
                editLineFinish() {
                    let editedline = this.edit.studyline.data;
                    let originalline = this.edit.studyline.original;
                    debug.info('Edit Line',this.edit.studyline);
                    call([{
                        methodname: 'local_treestudyplan_edit_studyline',
                        args: { 'id': editedline.id,
                                'name': editedline.name,
                                'shortname': editedline.shortname,
                                'color': editedline.color,}
                    }])[0].done(function(response){
                        debug.info('Edit response:', response);
                        originalline['name'] = response['name'];
                        originalline['shortname'] = response['shortname'];
                        originalline['color'] = response['color'];
                    }).fail(notification.exception);
                },
                deleteLine(studyplan,line) {
                    debug.info('Delete Line',line);
                    const self=this;
                    get_strings([
                            {key: 'studyline_confirm_remove', param: line.name, component: 'local_treestudyplan' },
                            {key: 'delete', component: 'core' },
                        ]).then(function(s){
                        self.$bvModal.msgBoxConfirm(s[0], {
                            okTitle: s[1],
                            okVariant: 'danger',
                        }).then(function(modalresponse){
                            if(modalresponse){
                                call([{
                                    methodname: 'local_treestudyplan_delete_studyline',
                                    args: { 'id': line.id, }
                                }])[0].done(function(response){
                                    debug.info('Delete response:', response);
                                    if(response.success == true){
                                        let index = studyplan.studylines.indexOf(line);
                                        studyplan.studylines.splice(index, 1);
                                    }
                                }).fail(notification.exception);
                            }
                        });
                    });
                },
                reorderLines(event,lines){
                    debug.info("Reorder lines",event,lines);

                    // apply reordering
                    event.apply(lines);
                    // send the new sequence to the server
                    let sequence = [];
                    for(let idx in lines)
                    {
                        sequence.push({'id': lines[idx].id,'sequence': idx});
                    }
                    call([{
                        methodname: 'local_treestudyplan_reorder_studylines',
                        args: { 'sequence': sequence }
                    }])[0].done(function(response){
                        debug.info('Reorder response:', response);
                    }).fail(notification.exception);
                },
                deletePlan(studyplan){
                    const self=this;
                    debug.info('Delete studyplan:', studyplan);
                    get_strings([
                        {key: 'studyplan_confirm_remove', param: studyplan.name, component: 'local_treestudyplan' },
                        {key: 'delete', component: 'core' },
                    ]).then(function(s){
                        self.$bvModal.msgBoxConfirm(s[0], {
                            okTitle: s[1],
                            okVariant: 'danger',
                        }).then(function(modalresponse){
                            if(modalresponse){
                                call([{
                                    methodname: 'local_treestudyplan_delete_studyplan',
                                    args: { 'id': studyplan.id, }
                                }])[0].done(function(response){
                                    debug.info('Delete response:', response);
                                    if(response.success == true){
                                        self.$root.$emit("studyplanRemoved",studyplan);
                                    }
                                }).fail(notification.exception);
                            }
                        });
                    });
                },
                deleteStudyItem(event){
                    debug.info('Delete studyitem:', event);
                    //const self = this;
                    let item = event.data;

                    call([{
                        methodname: 'local_treestudyplan_delete_studyitem',
                        args: { 'id': item.id, }
                    }])[0].done(function(response){
                        debug.info('Delete response:', response);
                        if(response.success == true){
                            event.source.$emit('cut',event);
                        }
                    }).fail(notification.exception);

                },
            }
            ,
            template: 
            `
            <div>
                <div class='controlbox t-studyplan-controlbox'>
                    <b-form-checkbox v-model="edit.studyline.editmode" class="sw-studyline-editmode" switch
                        >{{ text.studyline_editmode }}</b-form-checkbox>
                    <drop 
                        mode='copy'
                        class='t-item-deletebox text-danger border-danger'
                        @drop='deleteStudyItem'
                        :accepts-type="['gradable-item','filter-item']"
                        ><i class='fa fa-trash'></i>
                    </drop>
                    <span class='control deletable'>
                        <a v-if='value.studylines.length == 0' href='#' @click='deletePlan(value)'
                            ><i class='text-danger fa fa-trash'></i></a>
                    </span>
                    <span class='control editable'>
                        <t-studyplan-edit v-model="value" @moved="movedStudyplan"
                            ><i class='fa fa-pencil'></i> {{text.edit$core}}</t-studyplan-edit>    
                    </span>
                    <span class='control editable'>
                        <t-studyplan-associate 
                            v-model="value"><i class='fa fa-users'></i> {{text.associations}}</t-studyplan-associate>
                    </span>
                    <span class='control editable'>
                        <t-studyplan-advanced v-model="value"></t-studyplan-advanced>
                    </span>
                </div>
                <div class='t-studyplan-content-edit' v-if="edit.studyline.editmode">
                    <drop-list
                        :items="value.studylines"
                        class="t-slot-droplist"
                        :accepts-type="'studyline-'+value.id"
                        xreorder="$event.apply(value.studylines)"
                        @reorder="reorderLines($event,value.studylines)"
                        mode="copy"
                        row
                    >
                        <template v-slot:item="{item}">
                            <drag 
                                :key="item.id" 
                                class='t-studyplan-drag'
                                :data="item" 
                                :type="'studyline-'+value.id" 
                                >
                                <template v-slot:drag-image>
                                    <i class="fa fa-arrows text-primary"></i>
                                </template>
                                <t-studyline-edit 
                                    v-model="item"
                                    :numlines='value.studylines.length'
                                    @edit='editLineStart(item)'
                                    @delete='deleteLine(value,item)'
                                    >
                                    <div v-if="!slotsempty(item.slots)"> {{ text.editmode_modules_hidden}} </div>
                                </t-studyline-edit>
                            </drag>
                        </template>
                    </drop-list>
                </div>
                <div class='t-studyplan-content' v-else>
                    <!-- First paint the headings-->
                    <div class='t-studyplan-headings'>
                        <t-studyline-heading  v-for="(line,lineindex) in value.studylines"
                            :key="line.id"
                            v-model="value.studylines[lineindex]"
                            :numlines='value.studylines.length'
                            :layers='countLineLayers(line)+1'
                        ></t-studyline-heading>
                    </div>
                    <!-- Next, paint all the cells in the scrollable -->
                    <div class="t-studyplan-scrollable" >
                        <div class="t-studyplan-timeline" :style="columns_stylerule">
                        <!-- Line by line add the items -->
                        <!-- The grid layout handles putting it in rows and columns -->
                        <template v-for="(line,lineindex) in value.studylines"
                            ><template v-for="layeridx in countLineLayers(line)+1"
                                ><template v-for="(n,index) in (value.slots+1)"
                                    >
                                    <t-studyline-slot 
                                        v-if="index > 0"
                                        type='gradable'
                                        v-model="line.slots[index].competencies" 
                                        :key="'c-'+lineindex+'-'+index+'-'+layeridx" 
                                        :slotindex="index" 
                                        :line="line"
                                        :plan="value"
                                        :layer="layeridx-1"
                                        :class= "'t-studyline ' + ((line.sequence==1 && layeridx==1)?' first':'') + 
                                                (line.sequence==value.studylines.length?' last':'')"
                                    ></t-studyline-slot
                                    ><t-studyline-slot 
                                        type='filter'
                                        v-model="line.slots[index].filters" 
                                        :key="'f-'+lineindex+'-'+index+'-'+layeridx" 
                                        :slotindex="index" 
                                        :line="line"
                                        :plan="value"
                                        :layer="layeridx-1"
                                        :class="  't-studyline ' 
                                                + ((line.sequence==1 && layeridx==1)?' first':'') 
                                                + (line.sequence==value.studylines.length?' last':'')
                                                + (index==value.slots?' end':'')"
                                        >
                                    </t-studyline-slot
                                ></template
                            ></template
                        ></template
                        ></div><div :id="'studyplan-linewrapper-'+value.id" class='l-leaderline-linewrapper'></div>
                    </div>
                </div>
                <div v-if="edit.studyline.editmode" class='t-studyline-add'>
                    <a href="#" v-b-modal="'modal-add-studyline-'+value.id" @click="false;"
                        ><i class='fa fa-plus'></i>{{ text.studyline_add }}</a>
                </div>
                <b-modal 
                    :id="'modal-add-studyline-'+value.id" 
                    size="lg" 
                    :ok-title="text.add$core"
                    ok-variant="primary"
                    :title="text.studyline_add"
                    @ok="addStudyLine(value,create.studyline)"
                    :ok-disabled="Math.min(create.studyline.name.length,create.studyline.shortname.length) == 0"
                    >
                    <b-container>
                        <b-row>
                            <b-col cols="3">{{text.studyline_name}}</b-col>
                            <b-col>
                                <b-form-input v-model="create.studyline.name" :placeholder="text.studyline_name_ph"></b-form-input>
                            </b-col>
                        </b-row>
                        <b-row>
                            <b-col cols="3">{{text.studyline_shortname}}</b-col>
                            <b-col>
                            <b-form-input 
                                v-model="create.studyline.shortname" 
                                :placeholder="text.studyline_shortname_ph"></b-form-input>
                            </b-col>
                        </b-row>
                        <b-row>
                            <b-col cols="3">{{text.studyline_color}}</b-col>
                            <b-col>
                                <input type='color' v-model="create.studyline.color" />
                                <!-- hsluv-picker v-model="create.studyline.color" horizontal displaysize="175" ></hsluv-picker -->
                            </b-col>
                        </b-row>
                    </b-container>
                </b-modal>
                <b-modal 
                    :id="'modal-edit-studyline-'+value.id" 
                    size="lg" 
                    ok-variant="primary"
                    :title="text.studyline_edit"
                    @ok="editLineFinish()"
                    :ok-disabled="Math.min(edit.studyline.data.name.length,edit.studyline.data.shortname.length) == 0"
                    >
                    <b-container>
                        <b-row>
                            <b-col cols="3">{{ text.studyline_name}}</b-col>
                            <b-col>
                                <b-form-input 
                                    v-model="edit.studyline.data.name" 
                                    :placeholder="text.studyline_name_ph"></b-form-input>
                            </b-col>
                        </b-row>
                        <b-row>
                            <b-col cols="3">{{ text.studyline_shortname}}</b-col>
                            <b-col>
                            <b-form-input 
                                v-model="edit.studyline.data.shortname" 
                                :placeholder="text.studyline_shortname_ph"></b-form-input>
                            </b-col>
                        </b-row>
                        <b-row>
                            <b-col cols="3">{{ text.studyline_color}}</b-col>
                            <b-col>
                                <input type='color' v-model="edit.studyline.data.color" />
                                <!--hsluv-picker 
                                    v-model="edit.studyline.data.color" 
                                    horizontal displaysize="175" ></hsluv-picker -->
                            </b-col>
                        </b-row>
                    </b-container>
                </b-modal>
            </div>
            `
        });


       /*
        * T-STUDYLINE-HEADER
        */
       Vue.component('t-studyline-heading', {
            props: {
                value : {
                    type: Object,
                    default: function(){ return {};},
                },
                numlines: {
                    type: Number,
                    default: 1,
                },
                layers: {
                    type: Number,
                    default: 1,
                },
            },
            data() {
                return {
                    layerHeights: {}
                };
            },
            created() {
                    // Listener for the signal that a new connection was made and needs to be drawn
                    // Sent by the incoming item  - By convention, outgoing items are responsible for drawing the lines
                    ItemEventBus.$on('lineHeightChange', this.onLineHeightChange);
            },
            computed: {
                
            },
            methods: {
                onLineHeightChange(lineid,layerid,newheight){
                    // All layers for this line have the first slot send an update message on layer height change.
                    // When one of those updates is received, record the height and recalculate the total height of the 
                    // header
                    if(lineid == this.value.id){
                        const items = document.querySelectorAll(
                            `.t-studyline-slot-0[data-studyline='${this.value.id}']`);
                        
                        // determine the height of all the lines and add them up.
                        let heightSum = 0;
                        items.forEach((el) => {
                            // getBoundingClientRect() Gets the actual fractional height instead of rounded to integer pixels
                            const r = el.getBoundingClientRect();
                            const height = r.height;
                            heightSum += height;
                        });

                        const heightStyle=`${heightSum}px`;
                        this.$refs.mainEl.style.height = heightStyle;
                    }
                }
            },
            template: `
            <div :class="'t-studyline t-studyline-heading' + ((value.sequence%2)?' odd':' even') +
                (value.sequence==1?' first':'') + (value.sequence==numlines?' last':'')" 
                :data-studyline="value.id" :data-sequence="value.sequence" :data-numlines="numlines"
                ref="mainEl"
                ><div class="t-studyline-handle" :style="'background-color: ' + value.color"></div>
                <div class="t-studyline-title">
                    <abbr v-b-tooltip.hover :title="value.name">{{ value.shortname }}</abbr>
                </div>
            </div>
            `,
        });
        
        /*
        * T-STUDYLINE (Used only for study line edit mode)
        */
        Vue.component('t-studyline-edit', {
            props: {
                value : {
                    type: Object,
                    default: function(){ return {};},
                },
                numlines: {
                    type: Number,
                    default: 1,
                },                

                /*
                    :color='value.color' 
                    :name='value.name' 
                    :code='value.shortname'
                    :sequence='value.sequence'
                */
           },
            data() {
                return {
                };
            },
            computed: {
                deletable() {
                    // Check if all the slots are empty
                    const slots = this.value.slots;
                    if(Array.isArray(slots)){
                        let count = 0;
                        for(let i = 0; i < slots.length; i++) {
                            if(Array.isArray(slots[i].competencies)){
                                count += slots[i].competencies.length;
                            }
                            if(Array.isArray(slots[i].filters)){
                                count += slots[i].filters.length;
                            }
                        }
                        return (count == 0);
                    } else {
                        return false;
                    }
                }
            },
            methods: {
                onEdit() {
                    this.$emit('edit',this.value);
                },
                onDelete() {
                    this.$emit('delete',this.value);
                },

            },
            template: `
            <div :class="'t-studyline ' + (value.sequence==1?' first':'') + (value.sequence==numlines?' last':'')" > 
                <div class="t-studyline-handle" :style="'background-color: ' + value.color"></div>
                <div class="t-studyline-title">
                    <div>
                        <i class='fa fa-arrows text-primary'></i>
                        <abbr v-b-tooltip.hover :title="value.name">{{ value.shortname }}</abbr>
                    </div>
                </div>
                <div class='t-studyline-editmode-content'>
                    <slot></slot>
                </div>
                <div class='controlbox' v-if='editable || deletable'>
                    <span class='control editable'>
                        <a href='#' @click='onEdit'><i class='fa fa-pencil'></i></a>
                    </span>
                    <span class='control deletable'>
                        <a v-if='deletable' href='#' @click='onDelete'><i class='text-danger fa fa-trash'></i></a>
                    </span>
                </div>
            </div>
            `,
        });

        /*
         * During a redisign it was decided to have the studyline still get the entire array as a value,
         * even though it only shows one drop slot for the layer it is in. This is to make repainting easier, 
         * since we modify the array for the layer we handle 
         */
        Vue.component('t-studyline-slot', {
            props: {
                type : {
                    type: String,
                    default: 'gradable',
                },
                slotindex : {
                    type: Number,
                    default: '',
                },
                line : {
                    type: Object,
                    default(){ return null;},
                },
                layer : {
                    type: Number,
                },
                value: {
                    type: Array, // dict with layer as index
                    default(){ return [];},
                },
                plan: {
                    type: Object,
                    default(){ return null;},
                },

            },
            mounted() {
                const self=this;
                if(self.type == "gradable" && self.slotindex == 1){
                    self.resizeListener = new ResizeObserver(() => {
                        if(self.$refs.sizeElement){
                            const height = self.$refs.sizeElement.getBoundingClientRect().height;
                            ItemEventBus.$emit('lineHeightChange', self.line.id, self.layer, height);
                        }
                    }).observe(self.$refs.sizeElement);
                }
            },
            unmounted() {
                if(this.resizeListener) {
                    this.resizeListener.disconnect();
                }
            },
            computed: {
                item(){
                    for(const itm of this.value){
                        if(itm.layer == this.layer){
                            return itm;
                        }
                    }
                    return null;
                },
                listtype() {
                    return this.type;
                },
                dragacceptlist(){
                    if(this.type == "gradable"){
                        return ["course", "gradable-item"];
                    } else {
                        return ["filter", "filter-item"];
                    }
                },
                courseHoverDummy(){
                    return {course: this.hover.component};
                }
            },
            data() {
                return {
                    resizeListener: null,
                    hover: { 
                        component:null,
                        type: null,
                    }
                };
            },
            methods: {
                dragacceptitem(){
                    if(this.type == "gradable"){
                        return ["gradable-item"];
                    } else {
                        return ["filter-item"];
                    }
                },
                dragacceptcomponent(){
                    if(this.type == "gradable"){
                        return ["course",];
                    } else {
                        return ["filter",];
                    }
                },
                onDrop(event) {
                    this.hover.component = null;
                    this.hover.type = null;

                    const self = this;
                    if(self.dragacceptitem().includes(event.type)) {
                        let item = event.data;
                        // Perform layer update - set this slot and layer here
                        self.relocateStudyItem(item).done(()=>{
                            item.layer = this.layer;
                            item.slot = this.slotindex;
                            self.value.push(item);
                            self.$emit("input",self.value);                    
                        });
                    }
                    else if(self.dragacceptcomponent().includes(event.type) ){
                        if(event.type == "course"){
                            call([{
                                methodname: 'local_treestudyplan_add_studyitem',
                                args: { 
                                    "line_id": self.line.id,
                                    "slot" : self.slotindex,
                                    "layer" : self.layer,
                                    "type": 'course',
                                    "details": {
                                        "competency_id": null,
                                        'conditions':'',
                                        'course_id':event.data.id,
                                        'badge_id':null,
                                        'continuation_id':null,
                                    }
                                }
                            }])[0].done((response) => {
                                console.info('Add item response:', response);
                                let item = response;
                                self.relocateStudyItem(item).done(()=>{
                                    self.value.push(item);
                                    self.$emit("input",self.value);                    
                                });
                            }).fail(notification.exception);
                        }                 
                        else  if(event.type == "filter") {
                            call([{
                                methodname: 'local_treestudyplan_add_studyitem',
                                args: { 
                                    "line_id": self.line.id,
                                    "slot" : self.slotindex,
                                    "type": event.data.type,
                                    "details":{
                                        "badge_id": event.data.badge?event.data.badge.id:undefined,
                                    }
                                }
                            }])[0].done((response) => {
                                console.info('Add item response:', response);
                                let item = response;
                                self.relocateStudyItem(item).done(()=>{
                                    self.value.push(item);
                                    self.$emit("input",self.value);                    
                                });
                            }).fail(notification.exception);
                        } 
                    }
                },
                onCut(event) {
                    const self=this;
                    let id = event.data.id;
                    for(let i = 0; i < self.value.length; i++){ 
                        if(self.value[i].id == id){ 
                            self.value.splice(i, 1); i--; 
                            break; // just remove one
                        }
                    }
                    // Do something to signal that this item has been removed
                    this.$emit("input",this.value);
                },
                relocateStudyItem(item){
                    const iteminfo = {'id': item.id, 'layer': this.layer, 'slot': this.slotindex, 'line_id': this.line.id};
                    return call([{
                        methodname: 'local_treestudyplan_reorder_studyitems',
                        args: { 'items': [iteminfo] } // function was designed to relocate multiple items at once, hence the array
                    }])[0].fail(notification.exception);  
                },
                onDragEnter(event){
                    this.hover.component = event.data;
                    this.hover.type = event.type;
                },
                onDragLeave(){
                    this.hover.component = null;
                    this.hover.type = null;
                },

            },
            template: `
            <div :class="'t-studyline-slot '+type + ' t-studyline-slot-'+slotindex + ((slotindex==0)?' t-studyline-firstcolumn':'')
                         + ((line.sequence%2)?' odd':' even') "
                         :data-studyline="line.id" ref="sizeElement"
                ><drag v-if="item"
                    
                    :key="item.id" 
                    class="t-slot-item" 
                    :data="item" 
                    :type="type+'-item'" 
                    @cut="onCut"
                        ><t-item v-model="item" :plan="plan"></t-item
                        ></drag
                ><drop v-else
                    :class="'t-slot-drop '+type + (layer > 0?' secondary':' primary')"
                    :accepts-type="dragacceptlist"
                    @drop="onDrop"
                    mode="cut"
                    @dragenter="onDragEnter"
                    @dragleave="onDragLeave"
                ><template v-if="hover.component">
                        <div v-if="hover.type == listtype+'-item'" 
                            class="t-slot-item feedback" 
                            :key="hover.component.id"
                            ><t-item v-model="hover.component" dummy></t-item
                            ></div
                        ><div v-else-if="hover.type == 'course'" 
                            class="t-slot-item feedback" 
                            :key="'course-'+hover.component.id"
                            ><t-item-course v-model="courseHoverDummy"></t-item-course></div
                        ><div v-else-if="hover.type == 'filter'" 
                            class="t-slot-item feedback" 
                            key="tooldrop"
                            ><t-item-junction    v-if="hover.component.type == 'junction'" ></t-item-junction
                            ><t-item-start       v-else-if="hover.component.type == 'start'"    ></t-item-start
                            ><t-item-finish      v-else-if="hover.component.type == 'finish'"   ></t-item-finish
                            ><t-item-badge       v-else-if="hover.component.type == 'badge'"    ></t-item-badge
                        ></div
                        ><div v-else 
                            class="t-slot-item feedback"
                            :key="hover.type">--{{ hover.type }}--</div
                    ></template
                ></drop
            ></div>
            `,
        });


        Vue.component('t-item', {
            props: {
                'value' :{
                    type: Object,
                    default(){ return null;},
                },
                'dummy' :{
                    type: Boolean,
                    default() { return false;},
                },
                'plan': {
                    type: Object,
                    default() { return null;},
                },
            },
            data() {
                return {
                    dragLine: null,
                    dragEventListener: null,
                    deleteMode: false,
                    condition_options: string_keys.conditions,
                    text: strings.item_text,
                    showContext: false,
                    lines: [],
                };
            },
            methods: {
                dragStart(event){
                    // Add line between start point and drag image
                    this.deleteMode = false;
                    let start = document.getElementById('studyitem-'+this.value.id);
                    let dragelement= document.getElementById('t-item-cdrag-'+this.value.id);
                    dragelement.style.position = 'fixed';
                    dragelement.style.left = event.position.x+'px';
                    dragelement.style.top = event.position.y+'px';
                    this.dragLine = new SimpleLine(start,dragelement,{
                            color: "#777"
                        });
                    // Add separate event listener to reposition mouse move
                    document.addEventListener("mousemove",this.onMouseMove);
                },
                dragEnd(){
                    if(this.dragLine !== null) {
                        this.dragLine.remove();
                    }
                    let dragelement = document.getElementById('t-item-cdrag-'+this.value.id);
                    dragelement.style.removeProperty('left');
                    dragelement.style.removeProperty('top');
                    dragelement.style.removeProperty('position');
                    document.removeEventListener("mousemove",this.onMouseMove);
                },
                onMouseMove: function(event){
                    let dragelement = document.getElementById('t-item-cdrag-'+this.value.id);
                    dragelement.style.position = 'fixed';
                    dragelement.style.left = event.clientX+'px';
                    dragelement.style.top = event.clientY+'px';
                    // line will follow automatically
                },
                onDrop(event){
                    let from_id = event.data.id;
                    let to_id = this.value.id;
                    this.redrawLines();
                    call([{
                        methodname: 'local_treestudyplan_connect_studyitems',
                        args: { 'from_id': from_id, 'to_id': to_id }
                    }])[0].done((result)=>{
                        console.info("Drop result",result);
                        let conn = {'id': result.id, 'from_id': result.from_id, 'to_id': result.to_id};
                        ItemEventBus.$emit("createdConnection",conn);
                        this.value.connections.in.push(conn);
                    }).fail(notification.exception);
                },
                redrawLine(conn){
                    const lineColor = "var(--success)";
                    const start = document.getElementById(`studyitem-${conn.from_id}`);
                    const end = document.getElementById(`studyitem-${conn.to_id}`);

                    // delete old line
                    if(this.lines[conn.to_id]){
                        this.lines[conn.to_id].remove(); 
                        delete this.lines[conn.to_id];
                    } 
                    // create a new line if the start and finish items are visible
                    if(start !== null && end !== null && isVisible(start) && isVisible(end)){
                        this.lines[conn.to_id] = new SimpleLine( start,end,{color: lineColor,}
                        );                       
                    }

                },
                deleteLine(conn){
                    const self = this;
        //            console.info("Delete Line",conn);
                    call([{
                        methodname: 'local_treestudyplan_disconnect_studyitems',
                        args: { 'from_id': conn.from_id, 'to_id': conn.to_id }
                    }])[0].done((result)=>{
                        if(result.success){
                            this.removeLine(conn);
                            // send disconnect event on message bus, so the connection on the other end can delete it too
                            ItemEventBus.$emit("connectionDisconnected",conn);
                            // Remove connection from our outgoing list
                            let index = self.value.connections.out.indexOf(conn);
                            self.value.connections.out.splice(index, 1);
                        }
                    }).fail(notification.exception);
                },
                highlight(conn){
                    if(this.lines[conn.to_id]){
                        this.lines[conn.to_id].setConfig({color:"var(--danger)",});
                    }
                },
                normalize(conn){
                    if(this.lines[conn.to_id]){
                        this.lines[conn.to_id].setConfig({color:"var(--success)",});
                    }
                },
                updateItem() {
                    call([{
                        methodname: 'local_treestudyplan_edit_studyitem',
                        args: { 'id': this.value.id,
                                'conditions': this.value.conditions, 
                                'continuation_id': this.value.continuation_id,}
                    }])[0].fail(notification.exception);
                },
                doShowContext(event) {
                    if(this.hasContext){
                        this.showContext=true;
                        event.preventDefault();
                    }
                },
                redrawLines(){
                    for(let i in this.value.connections.out){
                        let conn = this.value.connections.out[i];
        //                console.info('Connection out', conn);
                        this.redrawLine(conn);
                    }
                },

                // EVENT LISTENERS
                onCreatedConnection(conn){
                    if(conn.from_id == this.value.id){
        //                    console.info("incomingConnection",conn);
                        this.value.connections.out.push(conn);
                        this.redrawLine(conn);
                    }
                },
                // Listener for the signal that a connection was removed by the outgoing item
                onRemovedConnection(conn){
                    for(let i in this.value.connections.in){
                        let c_in = this.value.connections.in[i];
                        if(conn.id == c_in.id){
        //                        console.info("Deleting incoming connection",conn);
                            self.value.connections.out.splice(i, 1);
                        }
                    }
                },
                // Listener for reposition events
                // When an item in the list is repositioned, all lines need to be redrawn
                onRePositioned(){
                    for(let i in this.value.connections.out){
                        let conn = this.value.connections.out[i];
                        this.redrawLine(conn);
                    }
                },
                // When an item is disPositioned - (temporarily) removed from the list,
                // all connections need to be deleted.
                onDisPositioned(re_id){
                    for(let i in this.value.connections.out){
                        let conn = this.value.connections.out[i];
                        if(conn.to_id == re_id){
                            this.removeLine(conn);
                        } 
                    }
                },

                // When an item is deleted 
                // all connections to/from that item need to be cleaned up
                onItemDeleted(item_id){
                    const self = this;
                    for(const i in this.value.connections.out){
                        let conn = this.value.connections.out[i];
                        if(conn.to_id == item_id){
                            self.removeLine(conn);
                            self.value.connections.out.splice(i, 1);                        
                        } 
                    }
                    for(const i in this.value.connections.in){
                        let conn = this.value.connections.in[i];
                        if(conn.from_id == item_id){
                            self.value.connections.out.splice(i, 1);                        
                        } 
                    }                
                },

                onRedrawLines(){
                    this.redrawLines();
                },

                removeLine(conn){
                    if(this.lines[conn.to_id]){
                        this.lines[conn.to_id].remove();
                        delete this.lines[conn.to_id];
                    }            
                },

            },
            computed: {
                hasConnectionsOut() {
                    return !(["finish",].includes(this.value.type));
                },
                hasConnectionsIn() {
                    return !(["start",].includes(this.value.type));
                },
                hasContext() {
                    return ['junction','finish'].includes(this.value.type);
                }
            },
            created(){
                // Add event listeners on the message bus
                // But only if not in "dummy" mode - mode which is used for droplist placeholders
                // Since an item is "fully made" with all references, not specifying dummy mode really messes things up
                if(!this.dummy){

                    // Listener for the signal that a new connection was made and needs to be drawn
                    // Sent by the incoming item  - By convention, outgoing items are responsible for drawing the lines
                    ItemEventBus.$on('createdConnection', this.onCreatedConnection);
                    // Listener for the signal that a connection was removed by the outgoing item
                    ItemEventBus.$on('removedConnection', this.onRemovedConnection);
                    // Listener for reposition events
                    // When an item in the list is repositioned, all lines need to be redrawn
                    ItemEventBus.$on('rePositioned', this.onRePositioned);
                    // When an item is disPositioned - (temporarily) removed from the list,
                    // all connections need to be deleted.
                    ItemEventBus.$on('disPositioned', this.onDisPositioned);
                    // When an item is deleted 
                    // all connections to/from that item need to be cleaned up
                    ItemEventBus.$on('itemDeleted', this.onItemDeleted);
                    ItemEventBus.$on('redrawLines', this.onRedrawLines);


                }
            },
            mounted(){
                // Initialize connection lines when mounting
                // But only if not in "dummy" mode - mode which is used for droplist placeholders
                // Since an item is "fully made" with all references, not specifying dummy mode really messes things up

                if(!this.dummy)
                {
        //            console.info('Mounted', this);
                    this.redrawLines();
                    setTimeout(()=>{
                        ItemEventBus.$emit("rePositioned",this.value.id);
                    },10);
                }
            },
            beforeDestroy(){
                if(!this.dummy) {
                    for(let i in this.value.connections.out){
                        let conn = this.value.connections.out[i];
                        this.removeLine(conn);
                    }
                    ItemEventBus.$emit("disPositioned",this.value.id);

                    // Remove event listeners
                    ItemEventBus.$off('createdConnection', this.onCreatedConnection);
                    ItemEventBus.$off('removedConnection', this.onRemovedConnection);
                    ItemEventBus.$off('rePositioned', this.onRePositioned);
                    ItemEventBus.$off('disPositioned', this.onDisPositioned);
                    ItemEventBus.$off('itemDeleted', this.onItemDeleted);
                    ItemEventBus.$off('redrawLines', this.onRedrawLines);
                }
            },
            beforeUpdate(){
            },
            updated(){
                if(!this.dummy) {
                    this.redrawLines();
                }
            },
            template: `
            <div class="t-item-base" :id="'studyitem-'+value.id">
                <t-item-competency  v-model="value"  v-if="value.type == 'competency'"   ></t-item-competency>
                <t-item-course      v-model="value"  v-if="value.type == 'course'"
                    :plan='plan' ></t-item-course>
                <t-item-junction    v-model="value"  v-if="value.type == 'junction'"     ></t-item-junction>
                <t-item-start       v-model="value"  v-if="value.type == 'start'"        ></t-item-start>
                <t-item-finish      v-model="value"  v-if="value.type == 'finish'"       ></t-item-finish>
                <t-item-badge       v-model="value"  v-if="value.type == 'badge'"        ></t-item-badge>
                <t-item-invalid     v-model="value"  v-if="value.type == 'invalid'"      ></t-item-invalid>
                <drop v-if='!dummy && hasConnectionsIn' accepts-type="linestart"
                    :id="'t-item-cend-'+value.id"
                    class="t-item-connector-end"
                    mode="copy"
                    @drop="onDrop"
                    ><svg width='5px' height='10px'><rect ry="1px" rx="1px" y="0px" x="0px" height="10px" width="5px"/></svg></drop>
                <drag v-if='!dummy && hasConnectionsOut' type="linestart"
                    :id="'t-item-cstart-'+value.id"
                    :class="'t-item-connector-start ' + ((deleteMode&&value.connections.out.length)?'deleteMode':'')"
                    :data="value"
                    @dragstart="dragStart"
                    @dragend="dragEnd"
                    @click="deleteMode = (value.connections.out.length)?(!deleteMode):false"
                    >
                    <svg  width='5px' height='10px'><rect ry="1px" rx="1px" y="0px" x="0px" height="10px" width="5px"/></svg>
                    <template v-slot:drag-image="{data}"> <i :id="'t-item-cdrag-'+value.id" class="fa"></i>
                    </template>
                </drag>
                <div class="deletebox" v-if="deleteMode && value.connections.out.length > 0"
                    >
                    <a v-for="conn in value.connections.out"
                        @click="deleteLine(conn)"
                        @mouseenter="highlight(conn)"
                        @mouseleave="normalize(conn)"
                        class="t-connection-delete text-danger"
                        :title="conn.id">
                        <i class="fa fa-trash"></i>
                    </a>
                </div>
                <a  v-if="hasContext" class="t-item-config"
                    v-b-modal="'t-item-config-'+value.id" href="#" @click.prevent=""><i  class="fa fa-gear"></i></a>
                <b-modal no-body class='t-item-contextview'
                    :id="'t-item-config-'+value.id"
                    :title="text['item_configuration']"
                    scrollable
                    ok-only
                >
                    <b-form-group
                        :label="text.select_conditions"
                    >
                        <b-form-select size="sm" 
                            @input="updateItem" 
                            v-model="value.conditions" 
                            :options="condition_options"
                        ></b-form-select>
                    </b-form-group>
                </b-modal>
            </div>
            `,
        });

        Vue.component('t-item-invalid', {
            props: {
                'value' :{
                    type: Object,
                    default: function(){ return null;},
                },
            },
            data() {
                return {
                    text: strings.invalid,
                };
            },
            methods: {
            },
            template: `
            <b-card no-body class="t-item-invalid">
                <b-row no-gutters>
                    <b-col md="1">
                        <span class="t-timing-indicator timing-invalid"></span>
                    </b-col>
                    <b-col md="11">
                        <b-card-body class="align-items-center">
                            <i class="fa fa-exclamation"></i> {{text.error}}
                        </b-card-body>
                    </b-col>
                </b-row>
            </b-card>
            `,
        });

        Vue.component('t-item-competency', {
            props: {
                'value' :{
                    type: Object,
                    default: function(){ return null;},
                },
            },
            data() {
                return {
                    dragLine: null,
                };
            },
            methods: {
            },
            template: `
            <b-card no-body class="t-item-competency">
                <b-card-body>
                <a :id="'t-item-competency-details-'+value.id" href="#" 
                    ><i class="fa fa-chevron-down">{{ value.competency.shortname }}</i> </a>
                </b-card-body>
                <b-popover
                    placement="bottom" 
                    :target="'t-item-competency-details-'+value.id" 
                    :title="value.competency.shortname+' - ' + value.competency.idnumber" 
                    triggers="click">
                    <ul class="t-item-module-children">
                        <li v-for="g in value.competency.children">{{g.shortname}}</li>
                    </ul>
                </b-popover>        
            </b-card>
            `,
        });

        Vue.component('t-item-course', {
            props: {
                'value' :{
                    type: Object,
                    default(){ return null;},
                },
                'plan' :{
                    type: Object,
                    default(){ return null;},
                },
            },
            data() {
                return {
                    condition_options: string_keys.conditions,
                    text: strings.item_course_text,
                };
            },
            computed: {
                useItemConditions() {
                    if(this.plan && this.plan.aggregation_info && this.plan.aggregation_info.useItemConditions !== undefined){
                        return this.plan.aggregation_info.useItemConditions;
                    }
                    else {
                        return false;
                    }
                },

                configurationState(){
                    if(this.hasGrades() || this.hasCompletions()) {
                        return "t-configured-ok";
                    } else {
                        return "t-configured-alert";
                    }
                },

                configurationIcon(){
                    if(this.hasGrades() || this.hasCompletions()) {
                        return "check";
                    } else {
                        return "exclamation-circle";
                    }
                }
            },
            methods: {
                hasGrades() {
                    if(this.value.course.grades && this.value.course.grades.length > 0){
                        for(const g of this.value.course.grades){
                            if(g.selected){
                                return true;
                            }
                        }
                    } 
                    return false;
                },
                hasCompletions() {
                    if(this.value.course.completion && this.value.course.completion.conditions) {
                        for(const cgroup of this.value.course.completion.conditions){
                            if(cgroup.items && cgroup.items.length > 0){
                                return true;
                            }
                        }
                    }
                    return false;
                },
                includeChanged(newValue,g){
                    call([{
                        methodname: 'local_treestudyplan_include_grade',
                        args: { 'grade_id': g.id,
                                'item_id': this.value.id, 
                                'include': newValue,
                                'required': g.required,
                                }
                    }])[0].fail(notification.exception);
                },
                requiredChanged(newValue,g){
                    call([{
                        methodname: 'local_treestudyplan_include_grade',
                        args: { 'grade_id': g.id,
                                'item_id': this.value.id, 
                                'include': g.selected,
                                'required': newValue,
                                }
                    }])[0].fail(notification.exception);
                },                
                updateConditions() {
                    call([{
                        methodname: 'local_treestudyplan_edit_studyitem',
                        args: { 'id': this.value.id,
                                'conditions': this.value.conditions, 
                                }
                    }])[0].fail(notification.exception);
                },
            },
            created() {

            },
            template: `
            <b-card no-body class="t-item-course">
                <b-row no-gutters>
                    <b-col md="1">
                        <span 
                            :title="text['coursetiming_'+value.course.timing]" 
                            v-b-popover.hover.top="value.course.startdate+' - '+value.course.enddate" 
                            :class="'t-timing-indicator timing-'+value.course.timing"></span>
                    </b-col>
                    <b-col md="11">
                        <b-card-body class="align-items-center">
                            <a  class="t-item-course-config"
                                v-b-modal="'t-item-course-config-'+value.id" 
                                href="#" @click.prevent=""
                                ><i :class="'fa fa-'+configurationIcon+' ' + configurationState"></i></a>
                            <a  v-b-modal="'t-item-course-config-'+value.id"
                                :id="'t-item-course-details-'+value.id" 
                                :href="'/course/view.php?id='+value.course.id"
                                @click.prevent.stop="">{{ value.course.displayname }}</a>
                        </b-card-body>
                    </b-col>
                </b-row>
                <b-modal
                    :id="'t-item-course-config-'+value.id" 
                    :title="value.course.displayname + ' - ' + value.course.fullname" 
                    ok-only
                    size="lg"
                    scrollable
                    >
                    <template #modal-header>
                        <div>
                            <h1><a :href="'/course/view.php?id='+value.course.id" target="_blank"
                                ><i class="fa fa-graduation-cap"></i> {{ value.course.fullname }}</a>
                                <a v-if='!!value.course.completion' 
                                    :href="'/course/completion.php?id='+value.course.id"  target="_blank"
                                :title="text.configure_completion"><i class="fa fa-gear"></i></a>
                                </h1>
                            {{ value.course.context.path.join(" / ")}} / {{value.course.shortname}}
                        </div>
                        <div class="r-course-detail-header-right">
                            <div :class="'r-timing-'+value.course.timing">
                                {{text['coursetiming_'+value.course.timing]}}<br>
                                {{ value.course.startdate }} - {{ value.course.enddate }}
                            </div>
                        </div>
                    </template>
                    
                    <t-item-course-grades 
                        v-if='!!value.course.grades && value.course.grades.length > 0' 
                        v-model='value' :plan="plan"
                        ></t-item-course-grades>
                    <t-item-course-completion
                        v-if='!!value.course.completion' 
                        v-model='value.course.completion'
                        :course='value.course'
                        ></t-item-course-completion>
                </b-modal>
            </b-card>
            `,
        });


        Vue.component('t-item-course-grades', {
            props: {
                'value' :{
                    type: Object,
                    default(){ return null;},
                },
                'plan' :{
                    type: Object,
                    default(){ return null;},
                },
            },
            data() {
                return {
                    condition_options: string_keys.conditions,
                    text: strings.item_course_text,
                };
            },
            computed: {
                useRequiredGrades() {
                    if(this.plan && this.plan.aggregation_info && this.plan.aggregation_info.useRequiredGrades !== undefined){
                        return this.plan.aggregation_info.useRequiredGrades;
                    }
                    else {
                        return false;
                    }
                },
                selectedgrades(){
                    let list = [];
                    for(let ix in this.value.course.grades){
                        let g = this.value.course.grades[ix];
                        if(g.selected){
                            list.push(g);
                        }
                    }
                    return list;
                },                
            },
            methods: {
                includeChanged(newValue,g){
                    call([{
                        methodname: 'local_treestudyplan_include_grade',
                        args: { 'grade_id': g.id,
                                'item_id': this.value.id, 
                                'include': newValue,
                                'required': g.required,
                                }
                    }])[0].fail(notification.exception);
                },
                requiredChanged(newValue,g){
                    call([{
                        methodname: 'local_treestudyplan_include_grade',
                        args: { 'grade_id': g.id,
                                'item_id': this.value.id, 
                                'include': g.selected,
                                'required': newValue,
                                }
                    }])[0].fail(notification.exception);
                },                
            },
            created() {

            },
            template: `
            <div>
                <b-form-group
                    :label="text.select_grades"
                    ><ul class="t-item-module-children">
                        <li class="t-item-course-gradeinfo">
                            <span class='t-item-course-chk-lbl'>{{text.grade_include}}</span
                            ><span v-if="useRequiredGrades"  class='t-item-course-chk-lbl'>{{text.grade_require}}</span>
                        </li>
                        <li class="t-item-course-gradeinfo" v-for="g in value.course.grades">
                            <b-form-checkbox inline
                                @change="includeChanged($event,g)" v-model="g.selected"
                                ></b-form-checkbox>
                            <b-form-checkbox v-if="useRequiredGrades" inline :disabled="!g.selected"
                                @change="requiredChanged($event,g)" v-model="g.required"
                                ></b-form-checkbox>
                            <span :title="g.typename" v-html="g.icon"></span>
                            <s-edit-mod 
                                :title="value.course.fullname"
                                @saved="(fd) => g.name = fd.get('name')"
                                v-if="g.cmid > 0" 
                                :cmid="g.cmid" 
                                :coursectxid="value.course.ctxid"
                                genericonly>{{g.name}}</s-edit-mod>
                        </li>
                    </ul>
                </b-form-group>
            </div>
            `,
        });

        Vue.component('t-item-course-completion',{
            props: {
                value : {
                    type: Object,
                    default: function(){ return {};},
                },
                guestmode: {
                    type: Boolean,
                    default: false,
                },
                course: {
                    type: Object,
                    default: function(){ return {};},
                },
            },
            data() {
                return {
                    text: strings.completion,
                };
            },
            created(){
                const self = this;
                // Get text strings for condition settings
                let stringkeys = [];
                for(const key in  this.text){
                    stringkeys.push({ key: key, component: 'local_treestudyplan'});
                }
                get_strings(stringkeys).then(function(strings){
                    let i = 0;
                    for(const key in  self.text){
                        self.text[key] = strings[i];
                        i++;
                    }
                });
            },
            computed: {
                hasCompletions() {
                    if(this.value.conditions) {
                        for(const cgroup of this.value.conditions){
                            if(cgroup.items && cgroup.items.length > 0){
                                return true;
                            }
                        }
                    }
                    return false;
                },
            },
            methods: {
                completion_icon(completion) {
                    switch(completion){
                        case "progress":
                            return "exclamation-circle";
                        case "complete":
                            return "check-circle";
                        case "complete-pass":
                            return "check-circle";
                        case "complete-fail":
                            return "times-circle";
                        default: // case "incomplete"
                            return "circle-o";
                    }
                },
    
                completion_tag(cgroup){
                    return cgroup.completion?'completed':'incomplete';
                }
            },
            template: `
            <table class="r-item-course-grade-details">
                <tr v-if="hasCompletions">
                    <td colspan='2'><span v-if="value.aggregation == 'all'">{{ text.aggregation_overall_all}}</span
                    ><span v-else>{{ text.aggregation_overall_any}}</span></td>
                </tr>
                <tr v-else>
                    <td colspan='2'>{{text.completion_not_configured}}!
                    <br/><a :href="'/course/completion.php?id='+course.id" target='_blank'>{{text.configure_completion}}</a>
                    </td>
                </tr>
                <template v-for='cgroup in value.conditions'>
                    <tr>
                        <th colspan='2'><span v-if="cgroup.items.length > 1"
                        ><span v-if="cgroup.aggregation == 'all'">{{ text.aggregation_all}}</span
                        ><span v-else>{{ text.aggregation_any}}</span></span>
                        {{cgroup.title}}</th>
                    </tr>
                    <tr v-for='ci in cgroup.items'>
                        <td><span v-html='ci.details.criteria'></span>
                        </td>
                        <td v-if="ci.details.requirement" class="font-italic">
                            {{ci.details.requirement}}
                        </td>
                    </tr>
                </template>
            </table>
            `,
        });


        /************************************
        *                                  *
        * Competency map Vue components    *
        *                                  *
        ************************************/
        Vue.component('t-competency-heading', {
            props: {
                value : {
                    type: Object,
                },
            },
            data() {
                return {
                };
            },
            computed: {
                inuse() {
                    return (this.value.inuse !== undefined && !!this.value.inuse);
                }
            },
            methods: {
                onCut(){
        //            console.info('cutevent-competency',event);
                    this.value.inuse=true;
                    this.$emit('input',this.value);
                },
            },
            template: `
                <span class="t-competency-heading"> 
                    <i :class="'t-'+value.type+' fa fa-puzzle-piece'"    v-if="value.type == 'module'"></i>
                    <i :class="'t-'+value.type+' fa fa-map'"            v-else-if="value.type == 'category'"></i>
                    <i :class="'t-'+value.type+' fa fa-check-square'"   v-else-if="value.type == 'goal'"></i>
                    <i :class="'t-'+value.type+' fa fa-circle'"         v-else ></i>
                    <drag 
                        class="draggable-competency" 
                        v-if="value.type == 'module' && !inuse" 
                        :data="value" 
                        type="competency"
                        @cut="onCut">
                        {{ value.shortname }}
                    </drag>
                    <span v-else-if="value.type == 'module'" class="disabled-competency">{{ value.shortname }}</span>
                    <span v-else class="competency-info">{{ value.shortname }}</span>
                </span>
            `,
        });

        Vue.component('t-competency-display', {
            props: {
                value : {
                    type: Object,
                    default: function(){ return {};},
                },
            },
            data() {
                return {
                    dragLine: null,
                };
            },
            methods: {
            },
            computed: {
                haschildren() {
                    return this.value.children && this.value.children.length > 0;
                },
            },
            template: `
            <li>
                <span v-if="haschildren" v-b-toggle="'cmp-'+value.id">
                    <i class="when-closed fa fa-caret-right"></i>
                    <i class="when-open fa fa-caret-down"></i>
                    <t-competency-heading v-model="value"></t-competency-heading>
                </span>
                <span v-else>
                    <t-competency-heading v-model="value"></t-competency-heading>
                </span>
                <b-collapse v-if="haschildren" :id="'cmp-'+value.id">
                    <t-competency-list v-model="value.children"></t-competency-list>
                </b-collapse>
            </li>                                       
            `,
        });
        Vue.component('t-competency-list', {
            props: {
                value : {
                    type: Array,
                    default: function(){ return [];},
                },
            },
            data() {
                return {
                };
            },
            methods: {
            },
            template: `
                <ul class='t-competency-list'>
                    <t-competency-display 
                        v-for="(child, index) in value" 
                        :key='child.idnumber' 
                        v-model="value[index]"
                    ></t-competency-display>
                </ul>
            `,
        });

        Vue.component('t-item-junction',{
            props: {
                value : {
                    type: Object,
                    default: function(){ return {};},
                },
            },
            data() {
                return {
                    condition_options: string_keys.conditions,
                };
            },
            methods: {

            },
            template: `
            <div class='t-item-junction  t-item-filter'> 
                <i class="fa fa-check-circle"></i>
            </div>
            `,
        });

        Vue.component('t-item-finish',{
            props: {
                value : {
                    type: Object,
                    default: function(){ return {};},
                },
            },
            data() {
                return {
                };
            },
            methods: {
            },
            template: `
            <div class='t-item-finish t-item-filter'> 
                <i class="fa fa-stop-circle"></i>
            </div>    
            `,
        });

        Vue.component('t-item-start',{
            props: {
                value : {
                    type: Object,
                    default: function(){ return {};},
                },
            },
            data() {
                return {
                };
            },
            created(){
        
            },
            methods: {
            },
            template: `
            <div class='t-item-start t-item-filter'> 
                <i class="fa fa-play-circle"></i>
            </div>    
            `,
        });

        Vue.component('t-item-badge',{
            props: {
                value : {
                    type: Object,
                    default: function(){ return { badge: {}};},
                },
            },
            data() {
                return {
                    txt: strings,
                };
            },
            methods: {
            },
            template: `
            <div class='t-item-badge t-item-filter' v-b-tooltip.hover :title="value.badge.name"> 
                <svg class="t-badge-backdrop " width='50px' height='50px' viewBox="0 0 100 100">
                    <title>{{value.badge.name}}</title>
                    <circle cx="50" cy="50" r="46" 
                        style="stroke: currentcolor; stroke-width: 4; fill: currentcolor; fill-opacity: 0.5;"/>
                    <image class="badge-image" clip-path="circle() fill-box"
                        :href="value.badge.imageurl" x="12" y="12" width="76" height="76"/>
                </svg>
                <a class="t-item-config badge"
                v-b-modal="'t-item-badge-details-'+value.id" href="#" @click.prevent=""><i  class="fa fa-gear"></i></a>
                <b-modal
                    :id="'t-item-badge-details-'+value.id" 
                    :title="value.badge.name" 
                    size="lg"
                    ok-only
                    centered
                    scrollable
                    >
                    <template #modal-header>
                        <div>
                            <h1><i class="fa fa-certificate"></i>
                                <a :href="value.badge.infolink" target="_blank"
                                >{{ value.badge.name }}</a
                            ></h1>
                        </div>
                    </template>
                    <b-container fluid>
                        <b-row><b-col cols="3">
                            <img :src="value.badge.imageurl"/>
                        </b-col><b-col cols="9">
                            <p>{{value.badge.description}}</p>
                            <ul class="list-unstyled w-100 border-grey border-top border-bottom pt-1 pb-1 mb-1"
                             v-if="value.badge.criteria"><li v-for="crit in value.badge.criteria"
                             ><span v-html='crit'></span></li></ul>
                            <p><strong><i class="fa fa-link"></i>
                                <a :href="value.badge.infolink">{{ txt.badge.badgeinfo }}</a></strong></p>
                        </b-col></b-row>
                    </b-container>
                </b-modal>

            </div>    
            `,
        });

        Vue.component('t-coursecat-list',{
            props: {
                value : {
                    type: Array,
                    default: function(){ return {};},
                },
            },
            data() {
                return {
                };
            },
            methods: {
            },
            template: `
            <ul class="t-coursecat-list">
                <t-coursecat-list-item 
                    v-for="coursecat,idx in value" 
                    v-model="value[idx]" 
                    :key="coursecat.id"></t-coursecat-list-item>
            </ul>    
            `,
        });

        Vue.component('t-coursecat-list-item',{
            props: {
                value : {
                    type: Object,
                    default: function(){ return {};},
                },
            },
            data() {
                return {
                    loading: false,

                };
            },
            computed: {
                showSpinner() {
                    return this.canLoadMore();
                },
                hasDetails() {
                    return (this.value.haschildren || this.value.hascourses);
                }

            },
            methods: {
                canLoadMore() {
                    return (this.value.haschildren && (!this.value.children || this.value.children.length == 0)) ||
                        (this.value.hascourses && (!this.value.courses || this.value.courses.length == 0));
                },
                onShowDetails(){
                    const self = this;
                    if(this.canLoadMore()) {
                        call([{
                            methodname: 'local_treestudyplan_get_category',
                            args: { "id": this.value.id}
                        }])[0].done(function(response){
                            debug.info("Course info:",response);
                            self.$emit('input', response);
                        }).fail(notification.exception);
                    }
                }
            },
            template: `
            <li class="t-coursecat-list-item">
                <span v-if="hasDetails" v-b-toggle="'coursecat-'+value.id">
                    <i class="when-closed fa fa-caret-right"></i>
                    <i class="when-open fa fa-caret-down"></i>
                    <span class="t-coursecat-heading">
                        <i class="t-coursecat-list-item fa fa-tasks"></i>
                        {{ value.category.name }}
                    </span>
                </span>
                <span v-else>
                    <span class="t-coursecat-heading">
                        <i class="t-coursecat-list-item fa fa-tasks"></i>
                        {{ value.category.name }}
                    </span>
                </span>
                <b-collapse v-if="hasDetails" :id="'coursecat-'+value.id" 
                    @show="onShowDetails" :visible="!!(value.children) || !!(value.courses)">
                    <b-spinner class="ml-4" v-if="showSpinner" small variant="primary"></b-spinner>
                    <t-coursecat-list v-if="value.children" v-model="value.children"></t-coursecat-list>
                    <t-course-list v-if="value.courses" v-model="value.courses"></t-course-list>
                </b-collapse>
            </li>
            `,
        });

        Vue.component('t-course-list',{
            props: {
                value : {
                    type: Array,
                    default: function(){ return {};},
                },
            },
            data() {
                return {
                };
            },
            methods: {
            },
            template: `
            <ul class="t-course-list">
                <li class="t-course-list-item" v-for="course in value" :key="course.id">
                    <drag 
                        class="draggable-course" 
                        :data="course" 
                        type="course"
                        @cut=""
                    >
                    <i class="t-course-list-item fa fa-book"></i> {{ course.shortname }} - {{ course.fullname }}
                    </drag>
                </li>
            </ul>    
            `,
        });
        
    },
};
