#projectGetWorkflowActionDefinitionOffsets = () ->
#  true
#export default projectGetWorkflowActionDefinitionOffsets
import Ember from 'ember'

RecordNode = Ember.Object.extend
  resolved: false
  title: Ember.computed 'record.title', 'value_shift', ->
    title = @get('record.title')
    title = " #{title} (#{value_shift}d)" if value_shift = @get('value_shift')
    title

OffsetNode = Ember.Object.extend
  record: null
  total_offset: 0
  value_shift: 0
  resulting_absolute_value: 0

WorkflowActionDefinitionOffsetNodes = Ember.Object.extend
  iteration_context_counter: 0
  workflow_action_definitions: []
  reference_date: null
  batch_size: 0
  active_project_scenario: null

  forEach: ->
    @get('processed_workflow_action_definitions').forEach(arguments...)

  filterBy: ->
    @get('processed_workflow_action_definitions').filterBy(arguments...)

  get_resolved: ->
    @get('processed_workflow_action_definitions').filterBy('resolved')

  init: ->
    @set 'scope_workflow_action_definition_ids', @get('workflow_action_definitions').mapBy('id').toArray()
    @set 'processed_workflow_action_definitions', []
    @set 'working_workflow_action_definitions', []
    @get_workflow_action_definition_offsets()

  get_workflow_action_definition_offsets: ->
    base = @
    active_scenario_identifier = @get('active_project_scenario.identifier')
    scope_workflow_action_definition_ids = @get('scope_workflow_action_definition_ids')
    first_loop_actions = []
    working_workflow_action_definitions = []
    @get('workflow_action_definitions').forEach (workflow_action_definition) ->
      action_added = false
      if workflow_action_definition.get('offset_type') is 'configured'
        workflow_action_definition.get('workflow_action_offsets').forEach (workflow_action_offset) ->
          return if action_added
          return if active_scenario_identifier and workflow_action_offset.get('project_scenario.identifier') and workflow_action_offset.get('project_scenario.identifier') isnt active_scenario_identifier
          if not workflow_action_offset.get('base_workflow_action_definition.content') # x_based
            [offset_date, offset_value] = base.get_offset_for_target(workflow_action_offset, 0)
            [date,        value_shift]  = base.get_dynamic_offset_for_target(workflow_action_offset, offset_value)
            absolute_value = offset_value + value_shift
            record_wrapper = RecordNode.create
              record: workflow_action_definition
              record_id: workflow_action_definition.get('id')
              offset_wrappers: [
                OffsetNode.create(total_offset: absolute_value, value_shift: value_shift)
              ]
              absolute_value: absolute_value
              value_shift: value_shift
              date: date
              resolve_type: 'x'
              resolved: true
              resolve_level: 0
            first_loop_actions.push record_wrapper
            action_added = true
      working_workflow_action_definitions.push workflow_action_definition unless action_added

    @set 'processed_workflow_action_definitions', first_loop_actions
    @set 'working_workflow_action_definitions',  working_workflow_action_definitions

    @incrementProperty 'iteration_context_counter'
    #@set 'working_workflow_action_definitions',  @get('workflow_action_definitions').filterBy('offset_type', 'configured')
    @run_critical_path_resolve_loop()
    resolve_level = @get('iteration_context_counter')
    @get('working_workflow_action_definitions').forEach (unresolved_workflow_action_definition) => # add unresolved records
      offset_wrappers = []
      unresolved_workflow_action_definition.get('workflow_action_offsets').forEach (action_offset) ->
        return unless action_offset.get('base_workflow_action_definition.id') in scope_workflow_action_definition_ids # do not add offsets from other scenarios
        offset_wrappers.push OffsetNode.create
          total_offset: action_offset.get('offset_value')
          record: action_offset
      @get('processed_workflow_action_definitions').push RecordNode.create
        record: unresolved_workflow_action_definition
        record_id: unresolved_workflow_action_definition.get('id')
        offset_wrappers: offset_wrappers
        resolved: false
        resolve_level: resolve_level
        resolve_type: 'unresolved'

  get_dynamic_offset_for_target: (target, absolute_value) ->
    base_date = @get('reference_date').clone().add(absolute_value, 'day')
    empty = [base_date, 0]
    return empty unless batch_size = @get('batch_size')
    return empty unless threshold = target.get('batch_element_threshold_value')
    return empty unless threshold_exceedance = Math.floor(batch_size / (0.1 + parseInt(threshold))) # + 0.1 (delta) to exclude exact match of batch_size and threshold. First day is action day

    period_unit = target.get('batch_element_threshold_unit')
    offset_value = target.get('offset_value')
    operation = if offset_value < 0 then 'subtract' else 'add'
    if period_unit is 'businessday'
      operation = "business#{operation.capitalize()}"
      shifted_date = base_date.clone()[operation](threshold_exceedance, business_days: target.get('batch_element_threshold_business_days'))
    else
      shifted_date = base_date.clone()[operation](threshold_exceedance, period_unit)
    value_shift = shifted_date.diff(base_date, 'day')
    [shifted_date, value_shift]

  get_offset_for_target: (target, absolute_value) ->
    base_date = @get('reference_date').clone().add(absolute_value, 'day')
    empty = [base_date, 0]
    return empty unless offset_value = target.get('offset_value')
    period_unit = target.get('offset_unit')
    operation = if offset_value < 0 then 'subtract' else 'add'
    if period_unit is 'businessday'
      operation = "business#{operation.capitalize()}"
      shifted_date = base_date.clone()[operation](Math.abs(offset_value), business_days: target.get('offset_business_days'))
    else
      shifted_date = base_date.clone()[operation](Math.abs(offset_value), period_unit)
    value_shift = shifted_date.diff(base_date, 'day')
    #debugger if target.get('workflow_action_definition.title') is 'Initial check done'
    [shifted_date, value_shift]

  run_critical_path_resolve_loop: ->
    working_indices_to_be_removed = []
    active_scenario_identifier = @get('active_project_scenario.identifier')
    processed_workflow_action_definitions = @get('processed_workflow_action_definitions')
    scope_workflow_action_definition_ids = @get('scope_workflow_action_definition_ids')
    get_dynamic_offset_for_target = @get('get_dynamic_offset_for_target').bind(@)
    get_offset_for_target = @get('get_offset_for_target').bind(@)
    resolve_level = @get('iteration_context_counter')
    @get('working_workflow_action_definitions').forEach (working_workflow_action_definition, working_index) ->
      return unless working_workflow_action_definition.get('offset_type') is 'configured'
      farthest_absolute_value = 0
      farthest_absolute_value_value_shift = 0
      farthest_result_total_offset = 0
      date = null # scoping
      resolved_offset_wrappers = []
      all_offsets_resolved = working_workflow_action_definition.get('workflow_action_offsets').every (workflow_action_offset) ->
        return true if active_scenario_identifier and workflow_action_offset.get('project_scenario.identifier') and workflow_action_offset.get('project_scenario.identifier') isnt active_scenario_identifier
        unless processed_base_workflow_action_definition_wrapper = processed_workflow_action_definitions.findBy 'record_id', workflow_action_offset.get('base_workflow_action_definition.id')
          if workflow_action_offset.get('base_workflow_action_definition.id') not in scope_workflow_action_definition_ids # base is not present in this scenario, do not pursue this connection
            return true
          return false
        processed_absolute_value = processed_base_workflow_action_definition_wrapper.get('absolute_value')
        [offset_date, working_offset_value] = get_offset_for_target(workflow_action_offset, processed_absolute_value)
        [date, current_connection_value_shift] = get_dynamic_offset_for_target(workflow_action_offset, processed_absolute_value + working_offset_value)
        current_connection_total_offset_value = current_connection_value_shift + working_offset_value
        current_connection_absolute_value = processed_absolute_value + current_connection_total_offset_value
        #debugger if processed_base_workflow_action_definition_wrapper.get('record.title') is 'Initial check done'
        #debugger if working_workflow_action_definition.get('title') is 'Initial check done'
        if Math.abs(current_connection_absolute_value) > Math.abs(farthest_absolute_value)
          farthest_absolute_value = current_connection_absolute_value
          farthest_absolute_value_value_shift = current_connection_value_shift
          farthest_result_total_offset = current_connection_total_offset_value
        resolved_offset_wrappers.push OffsetNode.create
          record: workflow_action_offset
          value_shift: current_connection_value_shift
          total_offset: current_connection_total_offset_value
          resulting_absolute_value: current_connection_absolute_value
        true # indicate processed_base_workflow_action_definition_wrapper found for all_offsets_resolved value

      if all_offsets_resolved and resolved_offset_wrappers.length
        record_wrapper = RecordNode.create
          record: working_workflow_action_definition
          record_id: working_workflow_action_definition.get('id')
          offset_wrappers: resolved_offset_wrappers
          value_shift: farthest_absolute_value_value_shift
          absolute_value: farthest_absolute_value
          date: date
          resolved: true
          resolve_level: resolve_level
          resolve_type: 'critical-path'
        processed_workflow_action_definitions.push record_wrapper
        working_indices_to_be_removed.push working_index
    if working_indices_to_be_removed.length
      @get('working_workflow_action_definitions').splice(working_indices_to_be_removed.pop(), 1) while working_indices_to_be_removed.length
      @run_critical_path_resolve_loop()
    else
      @incrementProperty 'iteration_context_counter'
      @run_unresolved_tree_resolve_loop()

  run_unresolved_tree_resolve_loop: ->
    working_indices_to_be_removed = []
    active_scenario_identifier = @get('active_project_scenario.identifier')
    processed_workflow_action_definitions = @get('processed_workflow_action_definitions')
    scope_workflow_action_definition_ids = @get('scope_workflow_action_definition_ids')
    get_offset_for_target = @get('get_offset_for_target').bind(@)
    get_dynamic_offset_for_target = @get('get_dynamic_offset_for_target').bind(@)
    resolve_level = @get('iteration_context_counter')
    @get('working_workflow_action_definitions').forEach (working_workflow_action_definition, working_index) ->
      return unless working_workflow_action_definition.get('offset_type') is 'configured'
      farthest_absolute_value = 0
      farthest_absolute_value_value_shift = 0
      farthest_result_total_offset = 0
      date = null # scoping
      resolved_offset_wrappers = []
      working_workflow_action_definition.get('workflow_action_offsets').forEach (workflow_action_offset) ->
        return if active_scenario_identifier and workflow_action_offset.get('project_scenario.identifier') and workflow_action_offset.get('project_scenario.identifier') isnt active_scenario_identifier
        return unless workflow_action_offset.get('base_workflow_action_definition.id') in scope_workflow_action_definition_ids
        return unless processed_base_workflow_action_definition_wrapper = processed_workflow_action_definitions.findBy 'record_id', workflow_action_offset.get('base_workflow_action_definition.id')
        processed_absolute_value = processed_base_workflow_action_definition_wrapper.get('absolute_value')
        [offset_date, working_offset_value] = get_offset_for_target(workflow_action_offset, processed_absolute_value)
        [date, current_connection_value_shift] = get_dynamic_offset_for_target(workflow_action_offset, processed_absolute_value + working_offset_value)
        current_connection_total_offset_value = current_connection_value_shift + working_offset_value
        current_connection_absolute_value = processed_absolute_value + current_connection_total_offset_value
        #debugger if processed_base_workflow_action_definition_wrapper.get('record.title') is 'Initial check done'
        #debugger if working_workflow_action_definition.get('title') is 'Initial check done'
        if Math.abs(current_connection_absolute_value) > Math.abs(farthest_absolute_value)
          farthest_absolute_value = current_connection_absolute_value
          farthest_absolute_value_value_shift = current_connection_value_shift
          farthest_result_total_offset = current_connection_total_offset_value
        resolved_offset_wrappers.push OffsetNode.create
          record: workflow_action_offset
          value_shift: current_connection_value_shift
          total_offset: current_connection_total_offset_value
          resulting_absolute_value: current_connection_absolute_value
      if resolved_offset_wrappers.length
        record_wrapper = RecordNode.create
          record: working_workflow_action_definition
          record_id: working_workflow_action_definition.get('id')
          offset_wrappers: resolved_offset_wrappers # .clone()?
          value_shift: farthest_absolute_value_value_shift
          absolute_value: farthest_absolute_value
          date: date
          resolved: true
          resolve_level: resolve_level
          resolve_type: 'with-non-rooted-offsets'
        processed_workflow_action_definitions.push record_wrapper
        working_indices_to_be_removed.push working_index

    if working_indices_to_be_removed.length
      @get('working_workflow_action_definitions').splice(working_indices_to_be_removed.pop(), 1) while working_indices_to_be_removed.length
      @run_critical_path_resolve_loop()
    else
      @run_offset_forwarding_loop()

  run_offset_forwarding_loop: ->
    working_indices_to_be_removed = []
    active_scenario_identifier = @get('active_project_scenario.identifier')
    processed_workflow_action_definitions = @get('processed_workflow_action_definitions')
    scope_workflow_action_definition_ids = @get('scope_workflow_action_definition_ids')
    get_offset_for_target = @get('get_offset_for_target').bind(@)
    get_dynamic_offset_for_target = @get('get_dynamic_offset_for_target').bind(@)
    resolve_level = @get('iteration_context_counter')
    @get('working_workflow_action_definitions').forEach (working_workflow_action_definition, working_index) ->
      farthest_absolute_value = 0
      farthest_absolute_value_value_shift = 0
      farthest_result_total_offset = 0
      date = null # scoping
      resolved_offset_wrappers = []
      working_workflow_action_definition.get('dependent_workflow_action_offsets').forEach (workflow_action_offset) ->
        return if active_scenario_identifier and workflow_action_offset.get('project_scenario.identifier') and workflow_action_offset.get('project_scenario.identifier') isnt active_scenario_identifier
        return unless workflow_action_offset.get('workflow_action_definition.id') in scope_workflow_action_definition_ids
        return unless processed_base_workflow_action_definition_wrapper = processed_workflow_action_definitions.findBy 'record_id', workflow_action_offset.get('workflow_action_definition.id')
        #return if workflow_action_offset.get('workflow_action_definition.offset_type') is 'none' # do not resolve on none offset typed nodes
        processed_absolute_value = processed_base_workflow_action_definition_wrapper.get('absolute_value')
        [offset_date, working_offset_value] = get_offset_for_target(workflow_action_offset, processed_absolute_value)
        [date, current_connection_value_shift] = get_dynamic_offset_for_target(workflow_action_offset, processed_absolute_value + working_offset_value)
        current_connection_total_offset_value = current_connection_value_shift + working_offset_value
        #NOTE MINUS!!!!!!!!!!!!!!!!!!!!!
        current_connection_absolute_value = processed_absolute_value - current_connection_total_offset_value
        #debugger if processed_base_workflow_action_definition_wrapper.get('record.title') is 'Initial check done'
        #debugger if working_workflow_action_definition.get('title') is 'Initial check done'
        if Math.abs(current_connection_absolute_value) > Math.abs(farthest_absolute_value)
          farthest_absolute_value = current_connection_absolute_value
          farthest_absolute_value_value_shift = current_connection_value_shift
          farthest_result_total_offset = current_connection_total_offset_value
        resolved_offset_wrappers.push OffsetNode.create
          record: workflow_action_offset
          value_shift: current_connection_value_shift
          total_offset: current_connection_total_offset_value
          resulting_absolute_value: current_connection_absolute_value
      if resolved_offset_wrappers.length
        record_wrapper = RecordNode.create
          record: working_workflow_action_definition
          record_id: working_workflow_action_definition.get('id')
          offset_wrappers: resolved_offset_wrappers # .clone()?
          value_shift: farthest_absolute_value_value_shift
          absolute_value: farthest_absolute_value
          date: date
          resolved: true
          resolve_level: resolve_level
          resolve_type: 'offset-lookup-inversion'
        processed_workflow_action_definitions.push record_wrapper
        working_indices_to_be_removed.push working_index

    if working_indices_to_be_removed.length
      @get('working_workflow_action_definitions').splice(working_indices_to_be_removed.pop(), 1) while working_indices_to_be_removed.length
      @run_critical_path_resolve_loop()
export default WorkflowActionDefinitionOffsetNodes
