define(
    ['utils'],
    function (utils) {
        "use strict";

        /**
         *
         * @param cfg
         * @param container
         * @param buttons
         * @constructor
         */
        function Feedback(cfg, container, buttons) {
            this.container = container || document.getElementsByTagName('body')[0];
            this.el = utils.createElement('div', 'general-feedback');
            this.type = 'general';
            this.attempts = 0;
            this.allowedAttempts = 1000;
            this.correct = 0;
            this.total = 0;
            this.visible = false;
            this.marked = [];
            this.allCorrect = false;
            this.maxAttemptsReached = false;
            this.feedback = {
                general: [
                    'Your answer is incorrect.',
                    'Your answer is correct.',
                    'Your answer is partially correct.'
                ],
                score: [
                    'All of your answers are correct',
                    ' of your answers are correct',
                    ' of your answers is correct'
                ],
                specific: [],
                branch: [
                    'That is one possible answer.',
                    'That is the other possible answer.',
                    'That is another possible answer.'
                ],
                final: {
                    branch: {
                        general: [], //shown in order answered, e.g. first answered will show [0]
                        specific: [] //shown relative to specific branch
                    },
                    exercise: [
                        'Try another question.',
                        'That is the end of the exercise.'
                    ]
                },
                correct: '',
                incorrect: '',
                partial: '',
                always: ''
            };

            if (cfg) {
                utils.extendDeep(this, cfg);
            }

            //this.incrementTotal = (this.total === 0);
            //console.log(this.total);

            if (buttons) {
                this.buttons = buttons;
            }

            this.setControllers();
        }

        Feedback.prototype = {
            get: function() {
                var fb = this.feedback,
                    str;

                switch (this.type) {
                    case 'general':
                        switch (this.correct) {
                            case 0:
                                str = '<p>' + fb.general[0] + '</p>';
                                break;
                            case this.total:
                                str = '<p>' + fb.general[1] + '</p>';
                                break;
                            default:
                                str = '<p>' + fb.general[2] + '</p>';
                        }
                        break;
                    case 'score':
                        switch (this.correct) {
                            case 0:
                                str = '<p>None ' + fb.score[1] + '.</p>';
                                break;
                            case 1:
                                str = '<p>' + utils.capFirstLetter(utils.inWords(this.correct)) + fb.score[2] + '.</p>';
                                break;
                            case this.total:
                                str = '<p>' + fb.score[0] + '.</p>';
                                break;
                            default:
                                str = '<p>' + utils.capFirstLetter(utils.inWords(this.correct)) + fb.score[1] + '.</p>';
                        }
                }

                //response specific
                /*if (this.specific.length > 0 && index >= 0) {
                    str += ' ' + this.specific[index];
                 }*/

                //correct/incorrect
                switch (this.correct) {
                    case 0:
                        str += (fb.incorrect.length ? ' <p>' + fb.incorrect + '</p>' : '');
                        break;
                    case this.total:
                        str += (fb.correct.length ? ' <p>' + fb.correct + '</p>' : '');
                        break;
                    default:
                        str += (fb.partial.length ? ' <p>' + fb.partial + '</p>' : (fb.incorrect.length ? ' <p>' + fb.incorrect + '</p>' : ''));
                }

                str += fb.always.length ? ' <p>' + fb.always + '</p>' : '';

                if (this.attempts == this.allowedAttempts) {
                    this.maxAttemptsReached = true;
                    str += '<p>The correct answer is now shown.</p>';
                    utils.triggerCustomEvent(this.el, 'max-attempts-reached');

                    if (this.buttons.active('check')) {
                        this.buttons.get('check').disable();
                    }
                    if (this.buttons.active('reveal')) {
                        this.buttons.get('reveal').disable();
                    }
                }

                return str;
            },

            show: function(attempt, answer, orderless) {
                var bb;

                if (this.visible) {
                    this.hide();
                }

                this.attempts++;
                this.marked.splice(0);
                this.correct = 0;

                if (this.total === 0) {
                    this.getTotal(answer);
                }

                this.check(attempt, answer, !!orderless);
                this.calculate();
                this.trigger();
                console.log(this.correct, this.total);

                this.el.textContent = '';
                this.el.insertAdjacentHTML('beforeend', '<p>' + this.get() + '</p>');
                this.el.style.display = 'block';
                this.visible = true;

                if (!this.maxAttemptsReached && !this.allCorrect) {
                    if (this.buttons.active('retry')) {
                        this.el.appendChild(this.buttons.get('retry').el);
                    }
                    else {
                        this.el.insertAdjacentHTML('beforeend', '<p>Try again.</p>');
                        utils.triggerCustomEvent(this.el, 'retry', { auto: true });
                    }
                }

                if (VLE && VLE.serverversion) {
                    VLE.resize_iframe();
                }

                ///TODO check behaviour!
                if (window.frameElement) {
                    bb = window.frameElement.getBoundingClientRect();

                    if (bb.bottom > window.top.innerHeight) {
                        //window.top.scrollTo(0, window.top.pageYOffset + bb.bottom - window.top.innerHeight);
                    }
                }
                else {
                    //window.scrollTo(0, document.body.scrollHeight);
                }
            },

            /**
             * Compares attempt to answer and stores result in this.marked
             * @param {Object[]} attempt - an array/array of nested arrays etc.
             * @param {Object[]} answer - an array/array of nested arrays etc.
             * @param {boolean} orderless - performs indexOf check if true, index by index otherwise
             * @param {Object[]} marked - the result of the comparison, same structure as input arrays with boolean/null values
             */
            check: function(attempt, answer, orderless, marked) {
                marked = marked || this.marked;
                attempt = Array.isArray(attempt) ? attempt : [attempt];
                answer = Array.isArray(answer) ? answer : [answer];

                attempt.forEach(function(attempt, i) {
                    if (Array.isArray(attempt)) {
                        marked[i] = marked[i] || [];
                        this.check(attempt, answer[i], orderless, marked[i]);
                    }
                    else {
                        if (attempt === null) {
                            marked[i] = null;
                            return;
                        }

                        if (Array.isArray(answer[i])) {
                            console.log(attempt, answer[i]);
                            marked[i] = (answer[i].indexOf(attempt) > -1) ? 1 : 0;
                        }
                        else {
                            if (orderless) {
                                marked[i] = (answer.indexOf(attempt) > -1) ? 1 : 0;
                            }
                            else {
                                marked[i] = (attempt === answer[i]) ? 1 : 0;
                                //console.log(marked[i], attempt, answer[i]);
                            }
                        }
                    }
                }.bind(this));
            },

            getTotal: function(answer) {
                answer.forEach(function(answer) {
                    if (Array.isArray(answer)) {
                        this.getTotal(answer);
                    }
                    else {
                        if (answer !== null) {
                            this.total++;
                        }
                    }
                }.bind(this));
            },

            calculate: function(marked) {
                marked = marked || this.marked;

                marked.forEach(function(marked) {
                    if (Array.isArray(marked)) {
                        this.calculate(marked);
                    }
                    else {
                        if (marked !== null) {
                            this.correct = marked ? ++this.correct : this.correct;
                        }
                    }
                }.bind(this));
            },

            trigger: function() {
                var event;

                switch (true) {
                    case this.correct === 0:
                        event = 'incorrect';
                        break;
                    case this.correct > 0 && this.correct < this.total:
                        event =  'partially-correct';
                        break;
                    default:
                        event = 'correct';
                        this.allCorrect = true;
                }

                utils.triggerCustomEvent(this.el, event);
            },

            hide: function() {
                this.el.textContent = '';
                this.el.style.display = 'none';
                this.visible = false;

                if (!this.allCorrect && !this.maxAttemptsReached) {
                    this.buttons.enable('check');
                }

                if (VLE && VLE.serverversion) {
                    VLE.resize_iframe();
                }
            },

            reset: function() {
                this.attempts = 0;
                this.allCorrect = false;
                this.maxAttemptsReached = false;
                this.marked.splice(0);
                this.hide();
            },

            setControllers: function() {
                this.container.addEventListener('feedback', this);
                this.container.addEventListener('reveal', this);
                this.container.addEventListener('retry', this);
                this.container.addEventListener('reset', this);
            },

            handleEvent: function(e) {
                console.log(e);

                switch (e.type) {
                    case 'feedback':
                        console.log(e.detail.attempt);
                        this.show(e.detail.attempt, e.detail.answer, e.detail.orderless);
                        break;
                    case 'reveal':
                    case 'retry':
                        if (e.detail && !e.detail.auto) {
                            this.hide();
                        }
                        break;
                    case 'reset':
                        this.reset();
                }
            }
        };

        return {
            construct: function(cfg, container, buttons) {
                return new Feedback(cfg, container, buttons);
            }
        };
    }
);