import Ember from 'ember'
import { mapHasMany } from 'ember-cli-dunlop/utils/map-action'
import WorkflowActionDefinitionOffsetNodes from 'ember-panda/utils/project/workflow-action-definition-offset-nodes'
import Viz from 'npm:viz.js' # browserify
GraphWrapper = Ember.Object.extend {}
critical_path_box_color          = '#ccfff2'
secondary_resolve_box_color      = '#ffe1b8'
higher_resolve_box_color         = '#ffe1b8'
base_x_box_color                 = '#b9e0ff'
unresolved_node_box_color        = '#c2f9c0'
attached_node_box_color          = '#dddddd'
attached_node_connection_color   = '#aaaaaa'
info_node_box_color              = '#f7f7f7'
info_node_connection_color       = '#dddddd'
constraint_node_box_color        = '#ffcc7d'
constraint_node_connection_color = '#ff5d5d'
{get} = Ember
export default Ember.Component.extend
  digraph_contents: []
  classNames: ['workflow-action-offsets-graph-component']
  batch_size_simulation_range: Ember.computed 'project.{batch_size_lower_value,batch_size_upper_value}',->
    {min: @get('project.batch_size_lower_value'), max: @get('project.batch_size_upper_value')}
  batch_size_simulation_value: 0
  batch_size_formatter: (-> [to: (v) -> Math.round(v)]).property()
  reference_date_day_of_week: Ember.computed.oneWay 'project.reference_date_day_of_week'
  has_content: false
  choose_reference_date: true
  graph_title: ''
  show_attached: true
  show_pointing_infos: false
  show_pointing_constraints: false
  active_workflow_action_definitions: []
  active_project_scenario: null

  days_offset_format: (v) ->
    v = Math.round(v) # could be float from slider
    if @get('chosen_absolute_date')
      date = moment(@get('chosen_absolute_date'))
      date.add(v, 'day') if v
      date.format("D-M-YYYY")
    else
      return 'X' unless v
      return '' unless v and Number.isFinite(v)
      if v % 7 is 0
        v = v / 7
        if v > 0 then "X+#{v}w" else "X#{v}w"
      else
        if v > 0 then "X+#{v}" else "X#{v}"

  didReceiveAttrs: ->
    console.log 'Reload workflow-action-offset-graph call from after render'
    Ember.run.scheduleOnce 'afterRender', @, @_setup_graph

  #_graph_observer: Ember.observer 'project.update_revision', '{chosen_absolute_date,reference_date_day_of_week,batch_size_simulation_value,show_connected}', 'active_workflow_action_definitions.@each.id', ->
  #  console.log 'Reload workflow-action-offset-graph call from observer'
  #  Ember.run.debounce  @, @_setup_graph, 150
  _graph_observer: Ember.observer 'project.update_revision', '{show_attached,show_pointing_infos,show_pointing_constraints}', ->
    console.log 'Reload workflow-action-offset-graph call from observer'
    Ember.run.debounce  @, @_setup_graph, 150
  #init: ->
  #  @_super arguments...
  #  #@set 'batch_size_simulation_value', 0 + @get('project.batch_size_lower_value') if @get('project.batch_size_lower_value')
  #  @set 'batch_size_simulation_value', @get('project.batch_size_lower_value') if not @get('batch_size_simulation_value') and @get('project.batch_size_lower_value')
  _setup_graph: ->
    @set 'digraph_contents', []
    digraph_labels = ''
    digraph_connections = ''
    component = @
    scenario = @get('active_project_scenario')
    show_pointing_infos = @get('show_pointing_infos')
    show_pointing_constraints = @get('show_pointing_constraints')
    show_attached = @get('show_attached')
    return unless @get('active_workflow_action_definitions.length')

    if @get('chosen_absolute_date')
      reference_date = moment(@get('chosen_absolute_date'))
    else
      reference_date = moment()
      reference_date.day reference_date_day_of_week if reference_date_day_of_week = @get('reference_date_day_of_week') # updates reference_date instance
    batch_size = @get('batch_size_simulation_value')
    result_nodes = WorkflowActionDefinitionOffsetNodes.create
      workflow_action_definitions: @get('active_workflow_action_definitions')
      reference_date: reference_date
      batch_size: batch_size
      active_project_scenario: scenario
    result_nodes.forEach (node) ->
      return unless node.resolved or node.offset_wrappers?.length or node.get('record.dependent_workflow_action_offsets.length')
      node.offset_wrappers ||= [] # iteration security
      workflow_action_definition_id = "q#{node.get('record_id')}" # start with a letter
      label_title = "#{node.get('record.title')}"
      if node.resolved
        label_title += "\\n#{component.days_offset_format(node.get('absolute_value'))}"
        label_title += " (#{value_shift}d)" if value_shift = node.get('value_shift') # since X-based boxes don't have a connector displaying the value_shift. NOTE: not absolute
      fill_color = if node.resolved then null else unresolved_node_box_color
      farthest_absolute_value = Math.max(node.offset_wrappers.map((o) -> Math.abs(o.resulting_absolute_value))...) || 0
      inversed_lookup = node.resolve_type is 'offset-lookup-inversion'
      #Ember.makeArray(node.offset_wrappers).forEach (node_offset) ->
      node.offset_wrappers.forEach (node_offset) ->
        base = if inversed_lookup then node_offset.get('record.workflow_action_definition.content') else node_offset.get('record.base_workflow_action_definition.content')
        if base
          base_id = "q#{base.get('id')}"
          penwidth = if Math.abs(node_offset.resulting_absolute_value) is farthest_absolute_value then 2 else 1
          connection_label = "#{Math.abs(node_offset.total_offset)}d"
          connection_label += " (#{Math.abs(node_offset.value_shift)}d)" if node_offset.value_shift
          if node_offset.total_offset < 0
            connection_color = '#f5b042'
            if not inversed_lookup
              digraph_connections += "#{workflow_action_definition_id} -> #{base_id} [color=\"#{connection_color}\" label=\"#{connection_label}\" penwidth=#{penwidth}];\n"
            else
              digraph_connections += "#{base_id} -> #{workflow_action_definition_id} [color=\"#{connection_color}\" label=\"#{connection_label}\" penwidth=#{penwidth}];\n"
          else
            connection_color = '#f5b041'
            if not inversed_lookup
              digraph_connections += "#{base_id} -> #{workflow_action_definition_id} [color=\"#{connection_color}\" label=\"#{connection_label}\" penwidth=#{penwidth}];\n"
            else
              digraph_connections += "#{workflow_action_definition_id} -> #{base_id} [color=\"#{connection_color}\" label=\"#{connection_label}\" penwidth=#{penwidth}];\n"
          if not fill_color
            if node.resolve_level <= 1
              fill_color = critical_path_box_color
            else if node.resolve_level is 2
              fill_color = secondary_resolve_box_color
            else
              fill_color = higher_resolve_box_color

        else
          fill_color ||= base_x_box_color
      digraph_labels += "  #{workflow_action_definition_id} [label=\"#{label_title}\" fillcolor=\"#{fill_color}\" tooltip=\"#{node.get('record.workflow_group.name')}\"];\n"

      if show_pointing_infos
        node.get('record.pointing_workflow_action_connections').filterBy('connection_type', 'info').forEach (info_connection) ->
          pointing_action = info_connection.get('workflow_action_definition')
          if scenario
            return if info_connection.get('project_scenario.id') and info_connection.get('project_scenario.id') isnt scenario.get('id')
            return if scenario.get('id') not in pointing_action.get('project_scenarios').mapBy('id')
          info_id = "qi#{pointing_action.get('id')}"
          connection_label = 'ℹ'
          if digraph_labels.indexOf(info_id) < 0
            info_label_title = "#{pointing_action.get('title')}"
            digraph_labels += "  #{info_id} [label=\"#{info_label_title}\" fillcolor=\"#{info_node_box_color}\" tooltip=\"#{pointing_action.get('workflow_group.name')}\"];\n"
          digraph_connections += "#{info_id} -> #{workflow_action_definition_id} [color=\"#{info_node_connection_color}\" label=\"#{connection_label}\" penwidth=2];\n"

      if show_pointing_constraints
        node.get('record.pointing_workflow_action_connections').filterBy('connection_type', 'constraint').forEach (constraint_connection) ->
          pointing_action = constraint_connection.get('workflow_action_definition')
          if scenario
            return if constraint_connection.get('project_scenario.id') and constraint_connection.get('project_scenario.id') isnt scenario.get('id')
            return if scenario.get('id') not in pointing_action.get('project_scenarios').mapBy('id')
          constraint_id = "qi#{pointing_action.get('id')}"
          connection_label = '☝'
          if digraph_labels.indexOf(constraint_id) < 0
            constraint_label_title = "#{pointing_action.get('title')}"
            digraph_labels += "  #{constraint_id} [label=\"#{constraint_label_title}\" fillcolor=\"#{constraint_node_box_color}\" tooltip=\"#{pointing_action.get('workflow_group.name')}\"];\n"
          digraph_connections += "#{constraint_id} -> #{workflow_action_definition_id} [color=\"#{constraint_node_connection_color}\" label=\"#{connection_label}\" penwidth=2];\n"


    if show_attached
      # attached nodes might be pointed at, but never are pointing themselves. Whe have no knowledge about if these nodes or even the target nodes are already available on the canvas
      # so do some checking. Attached nodes that are not yet available on the canvas will have no offset
      @get('active_workflow_action_definitions').filterBy('offset_type', 'attached').forEach (attached_workflow_action_definition) =>
        # initialization
        return unless connection = attached_workflow_action_definition.get('workflow_action_connections').findBy('connection_type', 'attached')
        return unless connection_target_workflow_action_definition = connection.get('base_workflow_action_definition')

        # base label
        attached_workflow_action_definition_id = "q#{attached_workflow_action_definition.get('id')}"
        if digraph_labels.indexOf(attached_workflow_action_definition_id) < 0
          attached_label_title = "#{attached_workflow_action_definition.get('title')}"
          digraph_labels += "  #{attached_workflow_action_definition_id} [label=\"#{attached_label_title}\" fillcolor=\"#{attached_node_box_color}\" tooltip=\"#{attached_workflow_action_definition.get('workflow_group.name')}\"];\n"

        # target label
        connection_target_workflow_action_definition_id = "q#{connection_target_workflow_action_definition.get('id')}"
        if digraph_labels.indexOf(connection_target_workflow_action_definition_id) < 0
          connection_target_label_title = "#{connection_target_workflow_action_definition.get('title')}"
          digraph_labels += "  #{connection_target_workflow_action_definition_id} [label=\"#{connection_target_label_title}\" fillcolor=\"#{unresolved_node_box_color}\" tooltip=\"#{connection_target_workflow_action_definition.get('workflow_group.name')}\"];\n"

        # add the connection
        connection_label = connection.get('conditional_base_value') || ''
        connection_label = '✓' if connection_label is 'true'
        digraph_connections += "#{attached_workflow_action_definition_id} -> #{connection_target_workflow_action_definition_id} [color=\"#{attached_node_connection_color}\" label=\"#{connection_label}\" penwidth=2];\n"

    content = "digraph \"Offset diagram\" {\n  ratio=fill;\n   node [style=filled fillcolor=\"#{critical_path_box_color}\" shape=box];\n  rankdir=TB;\n#{digraph_labels}#{digraph_connections}}"
    @set 'has_content', true
    @get('digraph_contents').push content


  svg_contents: Ember.computed "digraph_contents.length", ->
    return '' unless @get('digraph_contents.length')
    result = Ember.A()
    data = @get('digraph_contents').forEach (data) ->
      svg = Viz data,
        format: 'svg'
        engine: 'dot'
      svg = svg.replace(/\<svg width="\d+pt"/, '<svg width="100%"') # force 100%
      result.push svg.htmlSafe()
    result

  attach_events: Ember.on 'didRender', Ember.observer 'svg_content', ->
    component = @
    @$('.edge').each (index, edge_element) ->
      edge = $(edge_element)
      return unless title = edge.find('title').text()
      return unless color = edge.find('path:first').attr('stroke')
      return unless color in ['#f5b041', '#f5b042'] # two flavors
      if color is '#f5b042' # determines what the defining action is
        target_workflow_action_definition_id = title.replace(/->.*/, '').replace(/^q/, '')
      else
        target_workflow_action_definition_id = title.replace(/.*->q/, '')
      return unless target_workflow_action_definition = component.store.peekRecord('panda/workflow-action-definition', target_workflow_action_definition_id)
      edge.click ->
        component.modal 'workflow-action-definition/edit-workflow-action-definition',
          model: target_workflow_action_definition
    @$('.node').each (index, node_element) ->
      node = $(node_element)
      return unless title = node.find('title').text()
      target_workflow_action_definition_id = title.replace(/^qi?/, '')
      return unless target_workflow_action_definition = component.store.peekRecord('panda/workflow-action-definition', target_workflow_action_definition_id)
      node.click ->
        component.modal 'workflow-action-definition/edit-workflow-action-definition',
          model: target_workflow_action_definition

  actions:
    batch_size_simulation_change: (value) ->
      #@incrementProperty 'project.update_revision'
      @set 'batch_size_simulation_value', Math.round(value)
    hide_absolute_date_choice: ->
      @set 'chosen_absolute_date', null # important! determinse wether the labels use X or dates
      @set 'choose_reference_date', false
