// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import { Injectable } from '@angular/core';
import { NavController, ViewController } from 'ionic-angular';
import { AddonModFeedbackProvider } from './feedback';
import { CoreUserProvider } from '@core/user/providers/user';
import { CoreTextUtilsProvider } from '@providers/utils/text';
import { CoreTimeUtilsProvider } from '@providers/utils/time';
import { TranslateService } from '@ngx-translate/core';

/**
 * Service that provides helper functions for feedbacks.
 */
@Injectable()
export class AddonModFeedbackHelperProvider {

    protected MODE_RESPONSETIME = 1;
    protected MODE_COURSE = 2;
    protected MODE_CATEGORY = 3;

    constructor(protected feedbackProvider: AddonModFeedbackProvider, protected userProvider: CoreUserProvider,
            protected textUtils: CoreTextUtilsProvider, protected translate: TranslateService,
            protected timeUtils: CoreTimeUtilsProvider) {
    }

    /**
     * Check if the page we are going to open is in the history and returns the view controller in the stack to go back.
     *
     * @param {string} pageName       Name of the page we want to navigate.
     * @param {number} instance       Activity instance Id. I.e FeedbackId.
     * @param {string} paramName      Param name where to find the instance number.
     * @param {string} prefix         Prefix to check if we are out of the activity context.
     * @param {NavController} navCtrl Nav Controller of the view.
     * @return {ViewController}   Returns view controller found or null.
     */
    protected getPageView(pageName: string, instance: number, paramName: string, prefix: string,
            navCtrl: NavController): ViewController {
        let historyInstance, params,
            view = navCtrl.getActive();

        while (!view.isFirst()) {
            if (!view.name.startsWith(prefix)) {
                break;
            }

            params = view.getNavParams();

            historyInstance = params.get(paramName) ? params.get(paramName) : params.get('module').instance;

            // Check we are not changing to another activity.
            if (!historyInstance || historyInstance != instance) {
                break;
            }

            // Page found.
            if (view.name == pageName) {
                return view;
            }

            view = navCtrl.getPrevious(view);
        }

        return null;
    }

    /**
     * Retrieves a list of students who didn't submit the feedback with extra info.
     *
     * @param   {number}    feedbackId      Feedback ID.
     * @param   {number}    groupId         Group id, 0 means that the function will determine the user group.
     * @param   {number}    page            The page of records to return.
     * @return  {Promise<any>}              Promise resolved when the info is retrieved.
     */
    getNonRespondents(feedbackId: number, groupId: number, page: number): Promise<any> {
        return this.feedbackProvider.getNonRespondents(feedbackId, groupId, page).then((responses) => {
            return this.addImageProfileToAttempts(responses.users).then((users) => {
                responses.users = users;

                return responses;
            });
        });
    }

    /**
     * Get page items responses to be sent.
     *
     * @param   {any[]} items    Items where the values are.
     * @return  {any}            Responses object to be sent.
     */
    getPageItemsResponses(items: any[]): any {
        const responses = {};

        items.forEach((itemData) => {
            let answered = false;

            itemData.hasError = false;

            if (itemData.typ == 'captcha') {
                const value = itemData.value || '',
                    name = itemData.typ + '_' + itemData.id;

                answered = !!value;
                responses[name] = 1;
                responses['g-recaptcha-response'] = value;
                responses['recaptcha_element'] = 'dummyvalue';

                if (itemData.required && !answered) {
                    // Check if it has any value.
                    itemData.isEmpty = true;
                } else {
                    itemData.isEmpty = false;
                }
            } else if (itemData.hasvalue) {
                let name, value;
                const nameTemp = itemData.typ + '_' + itemData.id;

                if (itemData.typ == 'multichoice' && itemData.subtype == 'c') {
                    name = nameTemp + '[0]';
                    responses[name] = 0;
                    itemData.choices.forEach((choice, index) => {
                        name = nameTemp + '[' + (index + 1) + ']';
                        value = choice.checked ? choice.value : 0;
                        if (!answered && value) {
                            answered = true;
                        }
                        responses[name] = value;
                    });
                } else {
                    if (itemData.typ == 'multichoice' && itemData.subtype != 'r') {
                        name = nameTemp + '[0]';
                    } else {
                        name = nameTemp;
                    }

                    if (itemData.typ == 'multichoice' || itemData.typ == 'multichoicerated') {
                        value = itemData.value || 0;
                    } else if (itemData.typ == 'numeric') {
                        value = itemData.value || itemData.value  == 0 ? itemData.value : '';

                        if (value != '') {
                            if ((itemData.rangefrom != '' && value < itemData.rangefrom) ||
                                    (itemData.rangeto != '' && value > itemData.rangeto)) {
                                itemData.hasError = true;
                            }
                        }
                    } else {
                        value = itemData.value || itemData.value  == 0 ? itemData.value : '';
                    }

                    answered = !!value;
                    responses[name] = value;
                }

                if (itemData.required && !answered) {
                    // Check if it has any value.
                    itemData.isEmpty = true;
                } else {
                    itemData.isEmpty = false;
                }
            }
        });

        return responses;
    }

    /**
     * Returns the feedback user responses with extra info.
     *
     * @param   {number}    feedbackId      Feedback ID.
     * @param   {number}    groupId         Group id, 0 means that the function will determine the user group.
     * @param   {number}    page            The page of records to return.
     * @return  {Promise<any>}              Promise resolved when the info is retrieved.
     */
    getResponsesAnalysis(feedbackId: number, groupId: number, page: number): Promise<any> {
        return this.feedbackProvider.getResponsesAnalysis(feedbackId, groupId, page).then((responses) => {
            return this.addImageProfileToAttempts(responses.attempts).then((attempts) => {
                responses.attempts = attempts;

                return responses;
            });
        });
    }

    /**
     * Add Image profile url field on attempts
     *
     * @param  {any}          attempts Attempts array to get profile from.
     * @return {Promise<any>}          Returns the same array with the profileimageurl added if found.
     */
    protected addImageProfileToAttempts(attempts: any): Promise<any> {
        const promises = attempts.map((attempt) => {
            return this.userProvider.getProfile(attempt.userid, attempt.courseid, true).then((user) => {
                attempt.profileimageurl = user.profileimageurl;
            }).catch(() => {
                // Error getting profile, resolve promise without adding any extra data.
            });
        });

        return Promise.all(promises).then(() => {
            return attempts;
        });
    }

    /**
     * Helper function to open a feature in the app.
     *
     * @param {string}        feature   Name of the feature to open.
     * @param {NavController} navCtrl   NavController.
     * @param {any}           module    Course module activity object.
     * @param {number}        courseId  Course Id.
     * @param {number}        [group=0] Course module activity object.
     * @return {Promise<void>}    Resolved when navigation animation is done.
     */
    openFeature(feature: string, navCtrl: NavController, module: any, courseId: number, group: number = 0): Promise<void> {
        const pageName = feature && feature != 'analysis' ? 'AddonModFeedback' + feature + 'Page' : 'AddonModFeedbackIndexPage',
            stateParams = {
                module: module,
                moduleId: module.id,
                courseId: courseId,
                feedbackId: module.instance,
                group: group
            };

        // Only check history if navigating through tabs.
        if (pageName == 'AddonModFeedbackIndexPage') {
            stateParams['tab'] = feature == 'analysis' ? 'analysis' : 'overview';
            const view = this.getPageView(pageName, module.instance, 'feedbackId', 'AddonModFeedback', navCtrl);

            if (view) {
                // Go back to the found page.
                return navCtrl.popTo(view);
            }
        }

        // Not found, open new state.
        return navCtrl.push(pageName, stateParams);
    }

    /**
     * Helper funtion for item type Label.
     *
     * @param  {any} item Item to process.
     * @return {any}      Item processed to show form.
     */
    protected getItemFormLabel(item: any): any {
        item.template = 'label';
        item.name = '';
        item.presentation = this.textUtils.replacePluginfileUrls(item.presentation, item.itemfiles);

        return item;
    }

    /**
     * Helper funtion for item type Info.
     *
     * @param  {any} item Item to process.
     * @return {any}      Item processed to show form.
     */
    protected getItemFormInfo(item: any): any {
        item.template = 'label';

        const type = parseInt(item.presentation, 10);

        if (type == this.MODE_COURSE || type == this.MODE_CATEGORY) {
            item.presentation = item.otherdata;
            item.value = typeof item.rawValue != 'undefined' ? item.rawValue : item.otherdata;
        } else if (type == this.MODE_RESPONSETIME) {
            item.value = '__CURRENT__TIMESTAMP__';
            const tempValue = typeof item.rawValue != 'undefined' ? item.rawValue * 1000 : new Date().getTime();
            item.presentation = this.timeUtils.userDate(tempValue);
        } else {
            // Errors on item, return false.
            return false;
        }

        return item;
    }

    /**
     * Helper funtion for item type Numeric.
     *
     * @param  {any} item Item to process.
     * @return {any}      Item processed to show form.
     */
    protected getItemFormNumeric(item: any): any {
        item.template = 'numeric';

        const range = item.presentation.split(AddonModFeedbackProvider.LINE_SEP) || [];
        item.rangefrom = range.length > 0 ? parseInt(range[0], 10) || '' : '';
        item.rangeto = range.length > 1 ? parseInt(range[1], 10) || '' : '';
        item.value = typeof item.rawValue != 'undefined' ? parseFloat(item.rawValue) : '';

        return item;
    }

    /**
     * Helper funtion for item type Text field.
     *
     * @param  {any} item Item to process.
     * @return {any}      Item processed to show form.
     */
    protected getItemFormTextfield(item: any): any {
        item.template = 'textfield';
        item.length = item.presentation.split(AddonModFeedbackProvider.LINE_SEP)[1] || 255;
        item.value = typeof item.rawValue != 'undefined' ? item.rawValue : '';

        return item;
    }

    /**
     * Helper funtion for item type Textarea.
     *
     * @param  {any} item Item to process.
     * @return {any}      Item processed to show form.
     */
    protected getItemFormTextarea(item: any): any {
        item.template = 'textarea';
        item.value = typeof item.rawValue != 'undefined' ? item.rawValue : '';

        return item;
    }

    /**
     * Helper funtion for item type Multichoice.
     *
     * @param  {any} item Item to process.
     * @return {any}      Item processed to show form.
     */
    protected getItemFormMultichoice(item: any): any {
        let parts = item.presentation.split(AddonModFeedbackProvider.MULTICHOICE_TYPE_SEP) || [];
        item.subtype = parts.length > 0 && parts[0] ? parts[0] : 'r';
        item.template = 'multichoice-' + item.subtype;

        item.presentation = parts.length > 1 ? parts[1] : '';
        if (item.subtype != 'd') {
            parts = item.presentation.split(AddonModFeedbackProvider.MULTICHOICE_ADJUST_SEP) || [];
            item.presentation = parts.length > 0 ? parts[0] : '';
            // Horizontal are not supported right now. item.horizontal = parts.length > 1 && !!parts[1];
        }

        item.choices = item.presentation.split(AddonModFeedbackProvider.LINE_SEP) || [];
        item.choices = item.choices.map((choice, index) => {
            const weightValue = choice.split(AddonModFeedbackProvider.MULTICHOICERATED_VALUE_SEP) || [''];
            choice = weightValue.length == 1 ? weightValue[0] : '(' + weightValue[0] + ') ' + weightValue[1];

            return {value: index + 1, label: choice};
        });

        if (item.subtype === 'r' && item.options.search(AddonModFeedbackProvider.MULTICHOICE_HIDENOSELECT) == -1) {
            item.choices.unshift({value: 0, label: this.translate.instant('addon.mod_feedback.not_selected')});
            item.value = typeof item.rawValue != 'undefined' ? parseInt(item.rawValue, 10) : 0;
        } else if (item.subtype === 'd') {
            item.choices.unshift({value: 0, label: ''});
            item.value = typeof item.rawValue != 'undefined' ? parseInt(item.rawValue, 10) : 0;
        } else if (item.subtype === 'c') {
            if (typeof item.rawValue == 'undefined') {
                item.value = '';
            } else {
                item.rawValue = '' + item.rawValue;
                const values = item.rawValue.split(AddonModFeedbackProvider.LINE_SEP);
                item.choices.forEach((choice) => {
                    for (const x in values) {
                        if (choice.value == values[x]) {
                            choice.checked = true;

                            return;
                        }
                    }
                });
            }
        } else {
            item.value = typeof item.rawValue != 'undefined' ? parseInt(item.rawValue, 10) : '';
        }

        return item;
    }

    /**
     * Helper funtion for item type Captcha.
     *
     * @param  {any} item Item to process.
     * @return {any}      Item processed to show form.
     */
    protected getItemFormCaptcha(item: any): any {
        const data = this.textUtils.parseJSON(item.otherdata);
        if (data && data.length > 3) {
            item.captcha = {
                recaptchapublickey: data[3]
            };
        }
        item.template = 'captcha';
        item.value = '';

        return item;
    }

    /**
     * Process and returns item to print form.
     *
     * @param {any}  item        Item to process.
     * @param {boolean} preview  Previewing options.
     * @return {any}             Item processed to show form.
     */
    getItemForm(item: any, preview: boolean): any {
        switch (item.typ) {
            case 'label':
                return this.getItemFormLabel(item);
            case 'info':
                return this.getItemFormInfo(item);
            case 'numeric':
                return this.getItemFormNumeric(item);
            case 'textfield':
                return this.getItemFormTextfield(item);
            case 'textarea':
                return this.getItemFormTextarea(item);
            case 'multichoice':
                return this.getItemFormMultichoice(item);
            case 'multichoicerated':
                return this.getItemFormMultichoice(item);
            case 'pagebreak':
                if (!preview) {
                    // Pagebreaks are only used on preview.
                    return false;
                }
                break;
            case 'captcha':
                // Captcha is not supported right now. However label will be shown.
                return this.getItemFormCaptcha(item);
            default:
                return false;
        }

        return item;
    }

}
