###
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# File: app.coffee
###

@taiga = taiga = {}
@.taigaContribPlugins = @.taigaContribPlugins or []

# Generic function for generate hash from a arbitrary length
# collection of parameters.
taiga.generateHash = (components=[]) ->
    components = _.map(components, (x) -> JSON.stringify(x))
    return hex_sha1(components.join(":"))


taiga.generateUniqueSessionIdentifier = ->
    date = (new Date()).getTime()
    randomNumber = Math.floor(Math.random() * 0x9000000)
    return taiga.generateHash([date, randomNumber])


taiga.sessionId = taiga.generateUniqueSessionIdentifier()


configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEventsProvider,
             $compileProvider, $translateProvider) ->
    $routeProvider.when("/",
        {
            templateUrl: "home/home.html",
            access: {
                requiresLogin: true
            },
            title: "HOME.PAGE_TITLE",
            description: "HOME.PAGE_DESCRIPTION",
            loader: true
        }
    )

    $routeProvider.when("/projects/",
        {
            templateUrl: "projects/listing/projects-listing.html",
            access: {
                requiresLogin: true
            },
            title: "PROJECTS.PAGE_TITLE",
            description: "PROJECTS.PAGE_DESCRIPTION",
            loader: true,
            controller: "ProjectsListing",
            controllerAs: "vm"
        }
    )

    $routeProvider.when("/project/:pslug/",
        {
            templateUrl: "projects/project/project.html",
            loader: true,
            controller: "Project",
            controllerAs: "vm"
            section: "project-timeline"
        }
    )

    $routeProvider.when("/project/:pslug/search",
        {
            templateUrl: "search/search.html",
            reloadOnSearch: false,
            section: "search"
        }
    )

    $routeProvider.when("/project/:pslug/backlog",
        {
            templateUrl: "backlog/backlog.html",
            loader: true,
            section: "backlog"
        }
    )

    $routeProvider.when("/project/:pslug/kanban",
        {
            templateUrl: "kanban/kanban.html",
            loader: true,
            section: "kanban"
        }
    )

    # Milestone
    $routeProvider.when("/project/:pslug/taskboard/:sslug",
        {
            templateUrl: "taskboard/taskboard.html",
            loader: true,
            section: "backlog"
        }
    )

    # User stories
    $routeProvider.when("/project/:pslug/us/:usref",
        {
            templateUrl: "us/us-detail.html",
            loader: true,
            section: "backlog-kanban"
        }
    )

    # Tasks
    $routeProvider.when("/project/:pslug/task/:taskref",
        {
            templateUrl: "task/task-detail.html",
            loader: true,
            section: "backlog-kanban"
        }
    )

    # Wiki
    $routeProvider.when("/project/:pslug/wiki",
        {redirectTo: (params) -> "/project/#{params.pslug}/wiki/home"}, )
    $routeProvider.when("/project/:pslug/wiki/:slug",
        {
            templateUrl: "wiki/wiki.html",
            loader: true,
            section: "wiki"
        }
    )

    # Team
    $routeProvider.when("/project/:pslug/team",
        {
            templateUrl: "team/team.html",
            loader: true,
            section: "team"
        }
    )

    # Issues
    $routeProvider.when("/project/:pslug/issues",
        {
            templateUrl: "issue/issues.html",
            loader: true,
            section: "issues"
        }
    )
    $routeProvider.when("/project/:pslug/issue/:issueref",
        {
            templateUrl: "issue/issues-detail.html",
            loader: true,
            section: "issues"
        }
    )

    # Admin - Project Profile
    $routeProvider.when("/project/:pslug/admin/project-profile/details",
        {
            templateUrl: "admin/admin-project-profile.html",
            section: "admin"
        }
    )
    $routeProvider.when("/project/:pslug/admin/project-profile/default-values",
        {
            templateUrl: "admin/admin-project-default-values.html",
            section: "admin"
        }
    )
    $routeProvider.when("/project/:pslug/admin/project-profile/modules",
        {
            templateUrl: "admin/admin-project-modules.html",
            section: "admin"
        }
    )
    $routeProvider.when("/project/:pslug/admin/project-profile/export",
        {
            templateUrl: "admin/admin-project-export.html",
            section: "admin"
        }
    )
    $routeProvider.when("/project/:pslug/admin/project-profile/reports",
        {
            templateUrl: "admin/admin-project-reports.html",
            section: "admin"
        }
    )

    $routeProvider.when("/project/:pslug/admin/project-values/status",
        {
            templateUrl: "admin/admin-project-values-status.html",
            section: "admin"
        }
    )
    $routeProvider.when("/project/:pslug/admin/project-values/points",
        {
            templateUrl: "admin/admin-project-values-points.html",
            section: "admin"
        }
    )
    $routeProvider.when("/project/:pslug/admin/project-values/priorities",
        {
            templateUrl: "admin/admin-project-values-priorities.html",
            section: "admin"
        }
    )
    $routeProvider.when("/project/:pslug/admin/project-values/severities",
        {
            templateUrl: "admin/admin-project-values-severities.html",
            section: "admin"
        }
    )
    $routeProvider.when("/project/:pslug/admin/project-values/types",
        {
            templateUrl: "admin/admin-project-values-types.html",
            section: "admin"
        }
    )
    $routeProvider.when("/project/:pslug/admin/project-values/custom-fields",
        {
            templateUrl: "admin/admin-project-values-custom-fields.html",
            section: "admin"
        }
    )

    $routeProvider.when("/project/:pslug/admin/memberships",
        {
            templateUrl: "admin/admin-memberships.html",
            section: "admin"
        }
    )
    # Admin - Roles
    $routeProvider.when("/project/:pslug/admin/roles",
        {
            templateUrl: "admin/admin-roles.html",
            section: "admin"
        }
    )

    # Admin - Third Parties
    $routeProvider.when("/project/:pslug/admin/third-parties/webhooks",
        {
            templateUrl: "admin/admin-third-parties-webhooks.html",
            section: "admin"
        }
    )
    $routeProvider.when("/project/:pslug/admin/third-parties/github",
        {
            templateUrl: "admin/admin-third-parties-github.html",
            section: "admin"
        }
    )
    $routeProvider.when("/project/:pslug/admin/third-parties/gitlab",
        {
            templateUrl: "admin/admin-third-parties-gitlab.html",
            section: "admin"
        }
    )
    $routeProvider.when("/project/:pslug/admin/third-parties/bitbucket",
        {
            templateUrl: "admin/admin-third-parties-bitbucket.html",
            section: "admin"
        }
    )
    # Admin - Contrib Plugins
    $routeProvider.when("/project/:pslug/admin/contrib/:plugin",
        {templateUrl: "contrib/main.html"})

    # User settings
    $routeProvider.when("/user-settings/user-profile",
        {templateUrl: "user/user-profile.html"})
    $routeProvider.when("/user-settings/user-change-password",
        {templateUrl: "user/user-change-password.html"})
    $routeProvider.when("/user-settings/mail-notifications",
        {templateUrl: "user/mail-notifications.html"})
    $routeProvider.when("/change-email/:email_token",
        {templateUrl: "user/change-email.html"})
    $routeProvider.when("/cancel-account/:cancel_token",
        {templateUrl: "user/cancel-account.html"})

    # User profile
    $routeProvider.when("/profile",
        {
            templateUrl: "profile/profile.html",
            loader: true,
            access: {
                requiresLogin: true
            },
            controller: "Profile",
            controllerAs: "vm"
        }
    )

    $routeProvider.when("/profile/:slug",
        {
            templateUrl: "profile/profile.html",
            loader: true,
            controller: "Profile",
            controllerAs: "vm"
        }
    )

    # Auth
    $routeProvider.when("/login",
        {
            templateUrl: "auth/login.html",
            title: "LOGIN.PAGE_TITLE"
            description: "LOGIN.PAGE_DESCRIPTION"
        }
    )
    $routeProvider.when("/register",
        {
            templateUrl: "auth/register.html",
            title: "REGISTER.PAGE_TITLE",
            description: "REGISTER.PAGE_DESCRIPTION"
        }
    )
    $routeProvider.when("/forgot-password",
        {
            templateUrl: "auth/forgot-password.html",
            title: "FORGOT_PASSWORD.PAGE_TITLE",
            description: "FORGOT_PASSWORD.PAGE_DESCRIPTION"
        }
    )
    $routeProvider.when("/change-password",
        {
            templateUrl: "auth/change-password-from-recovery.html",
            title: "CHANGE_PASSWORD.PAGE_TITLE",
            description: "CHANGE_PASSWORD.PAGE_TITLE",
        }
    )
    $routeProvider.when("/change-password/:token",
        {
            templateUrl: "auth/change-password-from-recovery.html",
            title: "CHANGE_PASSWORD.PAGE_TITLE",
            description: "CHANGE_PASSWORD.PAGE_TITLE",
        }
    )
    $routeProvider.when("/invitation/:token",
        {
            templateUrl: "auth/invitation.html",
            title: "INVITATION.PAGE_TITLE",
            description: "INVITATION.PAGE_DESCRIPTION"
        }
    )

    # Errors/Exceptions
    $routeProvider.when("/error",
        {templateUrl: "error/error.html"})
    $routeProvider.when("/not-found",
        {templateUrl: "error/not-found.html"})
    $routeProvider.when("/permission-denied",
        {templateUrl: "error/permission-denied.html"})

    $routeProvider.otherwise({redirectTo: "/not-found"})
    $locationProvider.html5Mode({enabled: true, requireBase: false})

    defaultHeaders = {
        "Content-Type": "application/json"
        "Accept-Language": window.taigaConfig.defaultLanguage || "en"
        "X-Session-Id": taiga.sessionId
    }

    $httpProvider.defaults.headers.delete = defaultHeaders
    $httpProvider.defaults.headers.patch = defaultHeaders
    $httpProvider.defaults.headers.post = defaultHeaders
    $httpProvider.defaults.headers.put = defaultHeaders
    $httpProvider.defaults.headers.get = {
        "X-Session-Id": taiga.sessionId
    }

    $httpProvider.useApplyAsync(true)

    $tgEventsProvider.setSessionId(taiga.sessionId)

    # Add next param when user try to access to a secction need auth permissions.
    authHttpIntercept = ($q, $location, $navUrls, $lightboxService) ->
        httpResponseError = (response) ->
            if response.status == 0
                $lightboxService.closeAll()
                $location.path($navUrls.resolve("error"))
                $location.replace()
            else if response.status == 401
                nextPath = $location.path()
                $location.url($navUrls.resolve("login")).search("next=#{nextPath}")

            return $q.reject(response)

        return {
            responseError: httpResponseError
        }

    $provide.factory("authHttpIntercept", ["$q", "$location", "$tgNavUrls", "lightboxService",
                                           authHttpIntercept])

    $httpProvider.interceptors.push("authHttpIntercept")


    loaderIntercept = ($q, loaderService) ->
        return {
            request: (config) ->
                loaderService.logRequest()

                return config

            requestError: (rejection) ->
                loaderService.logResponse()

                return $q.reject(rejection)

            responseError: (rejection) ->
                loaderService.logResponse()

                return $q.reject(rejection)

            response: (response) ->
                loaderService.logResponse()

                return response
        }


    $provide.factory("loaderIntercept", ["$q", "tgLoader", loaderIntercept])

    $httpProvider.interceptors.push("loaderIntercept")

    # If there is an error in the version throw a notify error.
    # IMPROVEiMENT: Move this version error handler to USs, issues and tasks repository
    versionCheckHttpIntercept = ($q) ->
        httpResponseError = (response) ->
            if response.status == 400 && response.data.version
                # HACK: to prevent circular dependencies with [$tgConfirm, $translate]
                $injector = angular.element("body").injector()
                $injector.invoke(["$tgConfirm", "$translate", ($confirm, $translate) =>
                    versionErrorMsg = $translate.instant("ERROR.VERSION_ERROR")
                    $confirm.notify("error", versionErrorMsg, null, 10000)
                ])

            return $q.reject(response)

        return {responseError: httpResponseError}

    $provide.factory("versionCheckHttpIntercept", ["$q", versionCheckHttpIntercept])

    $httpProvider.interceptors.push("versionCheckHttpIntercept")

    window.checksley.updateValidators({
        linewidth: (val, width) ->
            lines = taiga.nl2br(val).split("<br />")

            valid = _.every lines, (line) ->
                line.length < width

            return valid
    })

    $compileProvider.debugInfoEnabled(window.taigaConfig.debugInfo || false)

    if localStorage.userInfo
        userInfo = JSON.parse(localStorage.userInfo)

    # i18n
    preferedLangCode = userInfo?.lang || window.taigaConfig.defaultLanguage || "en"

    $translateProvider
        .useStaticFilesLoader({
            prefix: "/locales/locale-",
            suffix: ".json"
        })
        .addInterpolation('$translateMessageFormatInterpolation')
        .preferredLanguage(preferedLangCode)

    if not window.taigaConfig.debugInfo
        $translateProvider.fallbackLanguage(preferedLangCode)


i18nInit = (lang, $translate) ->
    # i18n - moment.js
    moment.locale(lang)

    # i18n - checksley.js
    messages = {
        defaultMessage: $translate.instant("COMMON.FORM_ERRORS.DEFAULT_MESSAGE")
        type: {
            email: $translate.instant("COMMON.FORM_ERRORS.TYPE_EMAIL")
            url: $translate.instant("COMMON.FORM_ERRORS.TYPE_URL")
            urlstrict: $translate.instant("COMMON.FORM_ERRORS.TYPE_URLSTRICT")
            number: $translate.instant("COMMON.FORM_ERRORS.TYPE_NUMBER")
            digits: $translate.instant("COMMON.FORM_ERRORS.TYPE_DIGITS")
            dateIso: $translate.instant("COMMON.FORM_ERRORS.TYPE_DATEISO")
            alphanum: $translate.instant("COMMON.FORM_ERRORS.TYPE_ALPHANUM")
            phone: $translate.instant("COMMON.FORM_ERRORS.TYPE_PHONE")
        }
        notnull: $translate.instant("COMMON.FORM_ERRORS.NOTNULL")
        notblank: $translate.instant("COMMON.FORM_ERRORS.NOT_BLANK")
        required: $translate.instant("COMMON.FORM_ERRORS.REQUIRED")
        regexp: $translate.instant("COMMON.FORM_ERRORS.REGEXP")
        min: $translate.instant("COMMON.FORM_ERRORS.MIN")
        max: $translate.instant("COMMON.FORM_ERRORS.MAX")
        range: $translate.instant("COMMON.FORM_ERRORS.RANGE")
        minlength: $translate.instant("COMMON.FORM_ERRORS.MIN_LENGTH")
        maxlength: $translate.instant("COMMON.FORM_ERRORS.MAX_LENGTH")
        rangelength: $translate.instant("COMMON.FORM_ERRORS.RANGE_LENGTH")
        mincheck: $translate.instant("COMMON.FORM_ERRORS.MIN_CHECK")
        maxcheck: $translate.instant("COMMON.FORM_ERRORS.MAX_CHECK")
        rangecheck: $translate.instant("COMMON.FORM_ERRORS.RANGE_CHECK")
        equalto: $translate.instant("COMMON.FORM_ERRORS.EQUAL_TO")
    }
    checksley.updateMessages('default', messages)


init = ($log, $rootscope, $auth, $events, $analytics, $translate, $location, $navUrls, appMetaService, projectService, loaderService) ->
    $log.debug("Initialize application")

    # Taiga Plugins
    $rootscope.contribPlugins = @.taigaContribPlugins
    $rootscope.adminPlugins = _.where(@.taigaContribPlugins, {"type": "admin"})

    $rootscope.$on "$translateChangeEnd", (e, ctx) ->
        lang = ctx.language
        i18nInit(lang, $translate)

    # bluebird
    Promise.setScheduler (cb) ->
        $rootscope.$evalAsync(cb)

    # Load user
    if $auth.isAuthenticated()
        $events.setupConnection()
        user = $auth.getUser()

    # Analytics
    $analytics.initialize()

    $rootscope.$on '$routeChangeSuccess',  (event, next) ->
        if next.access && next.access.requiresLogin
            if !$auth.isAuthenticated()
                $location.path($navUrls.resolve("login"))

        projectService.setSection(next.section)

        if next.params.pslug
            projectService.setProject(next.params.pslug)
        else
            projectService.cleanProject()

        if next.title or next.description
            title = next.title or ""
            description = next.description or ""
            $translate([title, description]).then (translations) =>
                appMetaService.setAll(translations[title], translations[description])

        if next.loader
            loaderService.startWithAutoClose()

modules = [
    # Main Global Modules
    "taigaBase",
    "taigaCommon",
    "taigaResources",
    "taigaResources2",
    "taigaAuth",
    "taigaEvents",

    # Specific Modules
    "taigaHome",
    "taigaNavigationBar",
    "taigaProjects",
    "taigaRelatedTasks",
    "taigaBacklog",
    "taigaTaskboard",
    "taigaKanban",
    "taigaIssues",
    "taigaUserStories",
    "taigaTasks",
    "taigaTeam",
    "taigaWiki",
    "taigaSearch",
    "taigaAdmin",
    "taigaProject",
    "taigaUserSettings",
    "taigaFeedback",
    "taigaPlugins",
    "taigaIntegrations",
    "taigaComponents",
    # new modules
    "taigaProfile",
    "taigaHome",
    "taigaUserTimeline",

    # template cache
    "templates",

    # Vendor modules
    "ngRoute",
    "ngAnimate",
    "pascalprecht.translate",
    "infinite-scroll",
    "tgRepeat"
].concat(_.map(@.taigaContribPlugins, (plugin) -> plugin.module))

# Main module definition
module = angular.module("taiga", modules)

module.config([
    "$routeProvider",
    "$locationProvider",
    "$httpProvider",
    "$provide",
    "$tgEventsProvider",
    "$compileProvider",
    "$translateProvider",
    configure
])

module.run([
    "$log",
    "$rootScope",
    "$tgAuth",
    "$tgEvents",
    "$tgAnalytics",
    "$translate",
    "$tgLocation",
    "$tgNavUrls",
    "tgAppMetaService",
    "tgProjectService",
    "tgLoader",
    init
])
