const isExist = (ref) => typeof ref != 'undefined' && ref!=null;
const isArray = (ref) => typeof ref == 'object' && ref!=null;
const isObject = (ref) => typeof ref == 'object' && ref!=null;
const objectClone = (obj) => JSON.parse(JSON.stringify(obj));
const arrayFindById = (list, id) => list.find(ref => ref.id === id);

Vue.component('characters-bar-item', {
    props: ['item', 'index'],
    data: function() { return {
        name: this.item.info["Basic informations"].Name
    } },
    template: `
    <a v-bind:href="'#selectCharacter(' + escape(name) + ')'"
        v-on:click="this.app.sheetImport(this.app.$data, item);"
        style="margin-right: 5px;"
        >{{ name }}</a>`
  });

Vue.component('info-category', {
    props: ['category'],
    template: `
    <div class="AttributeClass">
    <table>
        <tr is="info-item"
            v-for="(item, index) in category.list"
            v-bind:attr="item"
            v-bind:category="category"
        ></tr>
    </table>
    </div>`
  });

Vue.component('info-item', {
    props: ['attr', 'category'],
    data: function() { return {
        for_id: 'field[' + this.category.id + '][' + this.attr.id + ']'
    } },
    template: `
    <tr>
        <td><label
                v-bind:for="for_id"
                >{{ attr.id }}</label></td>
        <td class="input"><input type="text"
                              v-bind:id="for_id"
                              v-bind:name="for_id"
                              v-bind:value="attr.value"
        ></td>
    </tr>`
  });

Vue.component('attribute-category', {
    props: ['category'],
    computed: {
        tooMany: function() {
            return this.category.sum > (this.category.allowed)
        }
    },
    methods: {
        recomputeSum: function() { 
            var newsum = 0
            for(var i = 0; i < this.category.list.length; i++) {
                newsum = newsum + this.category.list[i].value
            }            
            this.category.sum = newsum
            this.$emit('sumchanged')
        }        
    },
    template: `
    <div class="AttributeClass">
    <h3>{{ category.id }}
    <span 
        v-bind:class="{textred: tooMany}"> 
        {{ category.sum }}
    </span> of {{ category.allowed }}</h3>
    <table>
        <tr is="attribute-item"
            v-for="item in category.list"
            v-bind:attr="item"
            v-bind:key="item.id"
            v-on:attrchanged="recomputeSum"
        ></tr>
    </table>
    </div>
    `
})

Vue.component('attribute-item', {
    props: ['attr'],
    data: function() { return {
	hover: -1
    } },
    methods: {
        setAttrValue: function(attr, index) {
            attr.value = attr.value==index ? index-1 : index
            if (isExist(attr.value_range))
                {
                attr.value = attr.value<attr.value_range[0] ? attr.value_range[0] : attr.value>attr.value_range[1] ? attr.value_range[1] : attr.value
                }
        },
        setClass: function(value, index, hover) { return {
	    'circle'     : true,
	    'circleBlack': index<=value,
	    'circleGreen': hover>=0 && hover >value && index>value  && index<=hover,
	    'circleRed'  : hover>=0 && hover<=value && index>=hover && index<=value
        } },
        mouseOver: function(event) {
	    this.hover = event.target.attributes.index.value * 1
	    this.$forceUpdate()
        },
        mouseOut: function(event) {
            this.hover = -1
	    this.$forceUpdate()
        }
    },
    template: `
    <tr>
        <td>{{ attr.id }}</td>
        <td>{{ attr.value }}</td>
        <td>
            <div v-for="n in 5" 
                v-bind:index="n"
                v-bind:class="setClass(attr.value, n, hover)"
                v-on:click="setAttrValue(attr, n); $emit('attrchanged')"
                v-on:mouseover="mouseOver"
                v-on:mouseout="mouseOut"
            ></div>
        </td>
    </tr>`
  })

var app = new Vue({ 
    el: '#chargen',
    data: function() {
        var data = {
            props: {}, 
            form: {}, 
            form_structure: {
                info: {
                    id: "header-id",
                    list: [
                        { id: "item-id", value: "" }
                    ]
                },
                attributes: {
                    id: "header-id",
                    list: [
                        { id: "item-id", value: 1, value_range: [1, 5] }
                    ],
                    //priority: "C",
                    sum: 3,
                    allowed: 5
                },
                skills: {
                    id: "header-id",
                    list: [
                        { id: "item-id", value: 0 }
                    ],
                    sum: 0
                },
                disciplines: {
                    id: "header-id",
                    list: [
                        { id: "item-id", value: 0 }
                    ],
                    sum: 0
                }
            }
        }
	data.props = config; // load from config (global variable in file 'app-data.js')
        this.sheetImport(data, data.props.blank); // load from blank sheet (build form-structure)
    return data;
    },

    methods: {

        /* used for generate program-errors */
        error: function (source, text) {
            console.log(['Error app', source].join('.') + ': ' + text); // alternative: document.getElementById('error').innerHTML += text
        },

        RecomputeAttributePriorities: function() { 
            /*
             * We compute how many attribute points are allowed based on highest assigned points
             * which will get most allowed points, other lower similarly
             * algorithm uses AttrAllowed as a source of restrictions
             * sorted AttrSums is used to recognize which Attribute category has most points assigned
             * changed array is used to track which categories were already modified to avoid double assigments 
             * and leaving other categories unchanged
             */
            var AttrAllowed = [7,6,5]
            var AttrSums = [
                app.form.attributes[0].sum,
                app.form.attributes[1].sum,
                app.form.attributes[2].sum
            ]            
            AttrSums.sort(function(a,b) {return a-b}).reverse()
            var changed = []
            while (AttrSums.length > 0) {
                for (var i=0; i < app.form.attributes.length; i++) {
                    if (app.form.attributes[i].sum == AttrSums[0] && !(changed.includes(i))) {
                        app.form.attributes[i].allowed = AttrAllowed[0]
                        AttrSums.shift()
                        AttrAllowed.shift()
                        changed.push(i)
                    }
                }
            }  
        },

        /* sheet import - 1. clone blank sheet, 2. add data from imported sheet (JSON), 3. create form-structure from sheet */
        sheetImport: function(data, imported_sheet)
            {
            var fn = 'sheetImport';
//console.log(fn);
            var 
              imported_sheet = isObject(imported_sheet) ? imported_sheet : {},
              new_sheet      = objectClone(data.props.blank);
            this.sheetParser(new_sheet, imported_sheet); // update new_sheet from imported_sheet
            this.formStructureParserFromSheet(data.form, data.form_structure, new_sheet); // update form-structure from new_sheet
            // todo: this.recomputeAll(); // recompute all computed (sum, allowed)
            return this.form_structure;
            },

        /* sheet export - 1. clone blank sheet, 3. create sheet (JSON) from imported form-structure */
        sheetExport: function(data, exported_form)
            {
            var fn = 'sheetExport';
//console.log(fn);
            var 
              exported_form = isObject(exported_form) ? exported_form : {},
              new_sheet     = objectClone(data.props.blank);
            // ?todo: re-parse form to form.structure this.formParser(new_form, exported_form); // update new_sheet from imported_sheet
            this.formStructureParserToSheet(exported_form, new_sheet);
            console.log(JSON.stringify(new_sheet))
            return new_sheet;
	    },

	/* parser for move sheet data to blank sheet; called repeatedly (recursion), output in variable blank */
        sheetParser: function (blank, sheet)
            {
            var fn, i;
            fn = 'sheetParser';
//console.log(fn);
            if (!isObject(blank) || !isObject(sheet))
                {
                return;
                }
            for(i in blank)
                {
                if (!isObject(sheet) || !(i in sheet))
                    {
                    continue;
                    }
                if (isObject(blank[i]))	// object, repeat
                    {
                    if (!isObject(sheet[i]))
                        {
                        this.error(fn, 'Value in "parsed object" is not type Object.');
                        }
                    this.sheetParser(blank[i], sheet[i]);
                    }
                else
                    {
                    blank[i] = sheet[i];	// copy value
                    }
                }
            },

	/* parser for move sheet data to form-structure */
        formStructureParserFromSheet: function(form, structure, sheet)
            {
//console.log('formStructureParserFromSheet');
            var group, cat, attr, jj, kk, temp, key, list_keys = {'sum':1, 'allowed':1};
            for (group in structure) // example: group = 'attributes'
                {
                if (!isObject(structure[group]) || !isObject(sheet[group]))
                    {
                    continue;
                    }
                if (!isArray(form[group]))
                    {
                    form[group] = [];
                    }
                jj = 0;
                for (cat in sheet[group]) // example: category = 'physical'
                    {
                    if (!isObject(sheet[group][cat]))
                        {
                        continue;
                        }
                    if (!isObject(form[group][jj]))
                        {
                        form[group][jj] = objectClone(structure[group]);
                        }
                    form[group][jj].id = cat; // add category id
                    if (isExist(structure[group].list) && (!isExist(form[group][jj].list) || !isArray(form[group][jj].list)))	// create plistriority, if not exist
                        {
                        form[group][jj].list = objectClone(structure[group].list);
                        }
                    for (key in list_keys) // create [sum, allowed] if not exist
			{
                        if (isExist(structure[group][key]) && !isExist(form[group][jj][key]))		
                            {
                            form[group][jj][key] = structure[group][key];
                            }
			}
                    temp = objectClone(structure[group].list[0]);
                    kk = 0;
                    for (attr in sheet[group][cat])
                        {
                        if (!isExist(form[group][jj].list[kk]))
                            {
                            form[group][jj].list[kk] = objectClone(temp);
                            }
                         form[group][jj].list[kk].id    = attr;
                         form[group][jj].list[kk].value = sheet[group][cat][attr];
                         kk++;
                        }
                    jj++;
                    }
                }
//console.log(JSON.stringify(out))
//console.log(JSON.stringify(this.formStructureParserToSheet(out, sheet)))
        return form;
	},

	/* parser for move form-structure data to sheet */
        formStructureParserToSheet: function(form, sheet)	// parse sheet to form-structure
            {
//console.log('formStructureParserToSheet');
            // example sheet: sheet = { group: { cat: { attr: value } } }
            var group, cat, cat_data, attr, attr_data;
            for (group in sheet) // example: group = 'attributes'
                {
                if (!isObject(sheet[group]) || !isArray(form[group]))
                    {
                    continue;
                    }
                for (cat in sheet[group]) // example: category = 'physical'
                    {
                    if (!isObject(sheet[group][cat]))
                        {
                        continue;
                        }
                    cat_data = arrayFindById(form[group], cat);
                    if (!isExist(cat_data))
                        {
                        continue;
                        }
                    if (!isArray(cat_data.list))
                        {
                        continue;
                        }
                    for (attr in sheet[group][cat])
                        {
                        attr_data = arrayFindById(cat_data.list, attr);
                        if (!isExist(attr_data))
                            {
                            continue;
                            }
                        if (!isExist(attr_data.value))
                            {
                            this.error(fn, 'attr_data.value not found');
                            continue;
                            }
			sheet[group][cat][attr] = attr_data.value
                        }
                    }
                }
            return sheet;
            }
    }
});