'use strict'

### COMMON CODE ###

##
# Provides a set of common properties and methods to the $scope parameter. They are used
# in the various events' admin controllers.
#
# Provides :
#  - $scope.datePicker = {}
#  - $scope.submited(content)
#  - $scope.cancel()
#  - $scope.addFile()
#  - $scope.deleteFile(file)
#  - $scope.fileinputClass(v)
#  - $scope.toggleStartDatePicker($event)
#  - $scope.toggleEndDatePicker($event)
#  - $scope.toggleRecurrenceEnd(e)
#
# Requires :
#  - $scope.event.event_files_attributes = []
#  - $state (Ui-Router) [ 'app.public.events_list' ]
##
class EventsController
  constructor: ($scope, $state) ->

    ## default parameters for AngularUI-Bootstrap datepicker
    $scope.datePicker =
      format: Fablab.uibDateFormat
      startOpened: false # default: datePicker is not shown
      endOpened: false
      recurrenceEndOpened: false
      options:
        startingDay: Fablab.weekStartingDay



    ##
    # For use with ngUpload (https://github.com/twilson63/ngUpload).
    # Intended to be the callback when an upload is done: any raised error will be stacked in the
    # $scope.alerts array. If everything goes fine, the user is redirected to the project page.
    # @param content {Object} JSON - The upload's result
    ##
    $scope.submited = (content) ->
      if !content.id?
        $scope.alerts = []
        angular.forEach content, (v, k)->
          angular.forEach v, (err)->
            $scope.alerts.push({msg: k+': '+err, type: 'danger'})
      else
        $state.go('app.public.events_list')



    ##
    # Changes the user's view to the events list page
    ##
    $scope.cancel = ->
      $state.go('app.public.events_list')



    ##
    # For use with 'ng-class', returns the CSS class name for the uploads previews.
    # The preview may show a placeholder or the content of the file depending on the upload state.
    # @param v {*} any attribute, will be tested for truthiness (see JS evaluation rules)
    ##
    $scope.fileinputClass = (v)->
      if v
        'fileinput-exists'
      else
        'fileinput-new'



    ##
    # This will create a single new empty entry into the event's attachements list.
    ##
    $scope.addFile = ->
      $scope.event.event_files_attributes.push {}



    ##
    # This will remove the given file from the event's attachements list. If the file was previously uploaded
    # to the server, it will be marked for deletion on the server. Otherwise, it will be simply truncated from
    # the attachements array.
    # @param file {Object} the file to delete
    ##
    $scope.deleteFile = (file) ->
      index = $scope.event.event_files_attributes.indexOf(file)
      if file.id?
        file._destroy = true
      else
        $scope.event.event_files_attributes.splice(index, 1)



    ##
    # Show/Hide the "start" datepicker (open the drop down/close it)
    ##
    $scope.toggleStartDatePicker = ($event) ->
      $event.preventDefault()
      $event.stopPropagation()
      $scope.datePicker.startOpened = !$scope.datePicker.startOpened



    ##
    # Show/Hide the "end" datepicker (open the drop down/close it)
    ##
    $scope.toggleEndDatePicker = ($event) ->
      $event.preventDefault()
      $event.stopPropagation()
      $scope.datePicker.endOpened = !$scope.datePicker.endOpened



    ##
    # Masks/displays the recurrence pane allowing the admin to set the current event as recursive
    ##
    $scope.toggleRecurrenceEnd = (e)->
      e.preventDefault()
      e.stopPropagation()
      $scope.datePicker.recurrenceEndOpened = !$scope.datePicker.recurrenceEndOpened



    ##
    # Initialize a new price item in the additional prices list
    ##
    $scope.addPrice = ->
      $scope.event.prices.push({
        category: null,
        amount: null,
      })



##
# Controller used in the events listing page (admin view)
##
Application.Controllers.controller "AdminEventsController", ["$scope", "$state", 'dialogs', '$uibModal', 'growl', 'Event', 'Category', 'EventTheme', 'AgeRange', 'PriceCategory', 'eventsPromise', 'categoriesPromise', 'themesPromise', 'ageRangesPromise', 'priceCategoriesPromise', '_t'
, ($scope, $state, dialogs, $uibModal, growl, Event, Category, EventTheme, AgeRange, PriceCategory, eventsPromise, categoriesPromise, themesPromise, ageRangesPromise, priceCategoriesPromise, _t) ->



  ### PUBLIC SCOPE ###

  ## By default, the pagination mode is activated to limit the page size
  $scope.paginateActive = true

  ## The events displayed on the page
  $scope.events = eventsPromise

  ## Current virtual page
  $scope.page = 2

  ## Temporary datastore for creating new elements
  $scope.inserted =
    category: null
    theme: null
    age_range: null

  ## List of categories for the events
  $scope.categories = categoriesPromise

  ## List of events themes
  $scope.themes = themesPromise

  ## List of age ranges
  $scope.ageRanges = ageRangesPromise

  ## List of price categories for the events
  $scope.priceCategories = priceCategoriesPromise

  ##
  # Adds a bucket of events to the bottom of the page, grouped by month
  ##
  $scope.loadMoreEvents = ->
    Event.query {page: $scope.page}, (data)->
      $scope.events = $scope.events.concat data
      paginationCheck(data, $scope.events)
    $scope.page += 1


  ##
  # Saves a new element / Update an existing one to the server (form validation callback)
  # @param model {string} model name
  # @param data {Object} element name
  # @param [id] {number} element id, in case of update
  ##
  $scope.saveElement = (model, data, id) ->
    if id?
      getModel(model)[0].update {id: id}, data
    else
      getModel(model)[0].save data, (resp)->
        getModel(model)[1][getModel(model)[1].length-1].id = resp.id



  ##
  # Deletes the element at the specified index
  # @param model {string} model name
  # @param index {number} element index in the $scope[model] array
  ##
  $scope.removeElement = (model, index) ->
    if model == 'category' and getModel(model)[1].length == 1
      growl.error(_t('at_least_one_category_is_required')+' '+_t('unable_to_delete_the_last_one'))
      return false
    if getModel(model)[1][index].related_to > 0
      growl.error(_t('unable_to_delete_ELEMENT_already_in_use_NUMBER_times', {ELEMENT:model, NUMBER:getModel(model)[1][index].related_to}, "messageformat"))
      return false
    dialogs.confirm
      resolve:
        object: ->
          title: _t('confirmation_required')
          msg: _t('do_you_really_want_to_delete_this_ELEMENT', {ELEMENT:model}, "messageformat")
    , -> # delete confirmed
      getModel(model)[0].delete getModel(model)[1][index], null, ->
        getModel(model)[1].splice(index, 1)
      , ->
        growl.error(_t('unable_to_delete_an_error_occured'))



  ##
  # Creates a new empty entry in the $scope[model] array
  # @param model {string} model name
  ##
  $scope.addElement = (model) ->
    $scope.inserted[model] =
      name: ''
      related_to: 0
    getModel(model)[1].push($scope.inserted[model])



  ##
  # Removes the newly inserted but not saved element / Cancel the current element modification
  # @param model {string} model name
  # @param rowform {Object} see http://vitalets.github.io/angular-xeditable/
  # @param index {number} element index in the $scope[model] array
  ##
  $scope.cancelElement = (model, rowform, index) ->
    if getModel(model)[1][index].id?
      rowform.$cancel()
    else
      getModel(model)[1].splice(index, 1)



  ##
  # Open a modal dialog allowing the definition of a new price category.
  # Save it once filled and handle the result.
  ##
  $scope.newPriceCategory = ->
    $uibModal.open
      templateUrl: '<%= asset_path "admin/events/price_form.html" %>'
      size: 'md'
      resolve:
        category: -> {}
      controller: 'PriceCategoryController'
    .result['finally'](null).then (p_cat) ->
      # save the price category to the API
      PriceCategory.save p_cat, (cat) ->
        $scope.priceCategories.push(cat)
        growl.success(_t('price_category_successfully_created'))
      , (err)->
        growl.error(_t('unable_to_add_the_price_category_check_name_already_used'))
        console.error(err)



  ##
  # Update the given price category with the new properties
  # to specify in a modal dialog
  # @param index {number} index of the caterory in the $scope.priceCategories array
  # @param id {number} price category ID, must match the ID of the category at the index specified above
  ##
  $scope.editPriceCategory = (id, index) ->
    if $scope.priceCategories[index].id != id
      growl.error(_t('unexpected_error_occurred_please_refresh'))
    else
      $uibModal.open
        templateUrl: '<%= asset_path "admin/events/price_form.html" %>'
        size: 'md'
        resolve:
          category: -> $scope.priceCategories[index]
        controller: 'PriceCategoryController'
      .result['finally'](null).then (p_cat) ->
        # update the price category to the API
        PriceCategory.update {id: id}, {price_category: p_cat}, (cat) ->
          $scope.priceCategories[index] = cat
          growl.success(_t('price_category_successfully_updated'))
        , (err)->
          growl.error(_t('unable_to_update_the_price_category'))
          console.error(err)



  ##
  # Delete the given price category from the API
  # @param index {number} index of the caterory in the $scope.priceCategories array
  # @param id {number} price category ID, must match the ID of the category at the index specified above
  ##
  $scope.removePriceCategory = (id, index) ->
    if $scope.priceCategories[index].id != id
      growl.error(_t('unexpected_error_occurred_please_refresh'))
    else if $scope.priceCategories[index].events > 0
      growl.error(_t('unable_to_delete_this_price_category_because_it_is_already_used'))
    else
      dialogs.confirm
        resolve:
          object: ->
            title: _t('confirmation_required')
            msg: _t('do_you_really_want_to_delete_this_price_category')
      , -> # delete confirmed
        PriceCategory.remove {id: id}, -> # successfully deleted
          growl.success _t('price_category_successfully_deleted')
          $scope.priceCategories.splice(index, 1)
        , ->
          growl.error _t('price_category_deletion_failed')



  ### PRIVATE SCOPE ###

  ##
  # Kind of constructor: these actions will be realized first when the controller is loaded
  ##
  initialize = ->
    paginationCheck(eventsPromise, $scope.events)


  ##
  # Check if all events are already displayed OR if the button 'load more events'
  # is required
  # @param lastEvents {Array} last events loaded onto the diplay (ie. last "page")
  # @param events {Array} full list of events displayed on the page (not only the last retrieved)
  ##
  paginationCheck = (lastEvents, events)->
    if lastEvents.length > 0
      $scope.paginateActive = false if events.length >= lastEvents[0].nb_total_events
    else
      $scope.paginateActive = false

  ##
  # Return the model and the datastore matching the given name
  # @param name {string} 'category', 'theme' or 'age_range'
  # @return {[Object, Array]} model and datastore
  ##
  getModel = (name) ->
    switch name
      when 'category' then [Category, $scope.categories]
      when 'theme' then [EventTheme, $scope.themes]
      when 'age_range' then [AgeRange, $scope.ageRanges]
      else [null, []]


  # init the controller (call at the end !)
  initialize()

]



##
# Controller used in the reservations listing page for a specific event
##
Application.Controllers.controller "ShowEventReservationsController", ["$scope", 'eventPromise', 'reservationsPromise', ($scope, eventPromise, reservationsPromise) ->

  ## retrieve the event from the ID provided in the current URL
  $scope.event = eventPromise

  ## list of reservations for the current event
  $scope.reservations = reservationsPromise
]



##
# Controller used in the event creation page
##
Application.Controllers.controller "NewEventController", ["$scope", "$state", 'CSRF', 'categoriesPromise', 'themesPromise', 'ageRangesPromise', 'priceCategoriesPromise', '_t'
, ($scope, $state, CSRF, categoriesPromise, themesPromise, ageRangesPromise, priceCategoriesPromise, _t) ->
  CSRF.setMetaTags()

  ## API URL where the form will be posted
  $scope.actionUrl = "/api/events/"

  ## Form action on the above URL
  $scope.method = 'post'

  ## List of categories for the events
  $scope.categories = categoriesPromise

  ## List of events themes
  $scope.themes = themesPromise

  ## List of age ranges
  $scope.ageRanges = ageRangesPromise

  ## List of availables price's categories
  $scope.priceCategories = priceCategoriesPromise

  ## Default event parameters
  $scope.event =
    event_files_attributes: []
    start_date: new Date()
    end_date: new Date()
    start_time: new Date()
    end_time: new Date()
    all_day: 'false'
    recurrence: 'none'
    category_id: null
    prices: []

  ## Possible types of recurrences for an event
  $scope.recurrenceTypes = [
    {label: _t('none'), value: 'none'},
    {label: _t('every_days'), value: 'day'},
    {label: _t('every_week'), value: 'week'},
    {label: _t('every_month'), value: 'month'},
    {label: _t('every_year'), value: 'year'}
  ]

  ## Using the EventsController
  new EventsController($scope, $state)
]



##
# Controller used in the events edition page
##
Application.Controllers.controller "EditEventController", ["$scope", "$state", "$stateParams", 'CSRF', 'eventPromise', 'categoriesPromise', 'themesPromise', 'ageRangesPromise', 'priceCategoriesPromise'
, ($scope, $state, $stateParams, CSRF, eventPromise, categoriesPromise, themesPromise, ageRangesPromise, priceCategoriesPromise) ->

  ### PUBLIC SCOPE ###



  ## API URL where the form will be posted
  $scope.actionUrl = "/api/events/" + $stateParams.id

  ## Form action on the above URL
  $scope.method = 'put'

  ## Retrieve the event details, in case of error the user is redirected to the events listing
  $scope.event = eventPromise

  ## List of categories for the events
  $scope.categories = categoriesPromise

  ## List of availables price's categories
  $scope.priceCategories = priceCategoriesPromise

  ## List of events themes
  $scope.themes = themesPromise

  ## List of age ranges
  $scope.ageRanges = ageRangesPromise



  ### PRIVATE SCOPE ###



  ##
  # Kind of constructor: these actions will be realized first when the controller is loaded
  ##
  initialize = ->
    CSRF.setMetaTags()

    # init the dates to JS objects
    $scope.event.start_date = moment($scope.event.start_date).toDate()
    $scope.event.end_date = moment($scope.event.end_date).toDate()

    ## Using the EventsController
    new EventsController($scope, $state)



  ## !!! MUST BE CALLED AT THE END of the controller
  initialize()
]
