require 'fileutils'
require 'os'

require 'models/glimmer/gladiator/dir'
require 'models/glimmer/gladiator/file'
require 'models/glimmer/gladiator/command'

require 'views/glimmer/gladiator/text_editor'

Clipboard.implementation = Clipboard::Java
Clipboard.copy(Clipboard.paste) # pre-initialize library to avoid slowdown during use

module Glimmer
  # Gladiator (Glimmer Editor)
  class Gladiator
    include Glimmer::UI::CustomShell
    
    COMMAND_KEY = OS.mac? ? :command : :ctrl

    class << self
      attr_accessor :drag_and_drop
      attr_accessor :drag
    end

    ## Add options like the following to configure CustomShell by outside consumers
    #
    # options :title, :background_color
    # option :width, 320
    # option :height, 240
    
    ## Uncomment before_body block to pre-initialize variables to use in body
    #
    #
    before_body {
      Display.setAppName('Gladiator')
      @display = display {
        on_swt_keydown { |key_event|
          if key_event.stateMask == swt(COMMAND_KEY) && extract_char(key_event) == 'f'
            if @text_editor&.text_widget&.getSelectionText && @text_editor&.text_widget&.getSelectionText&.size.to_i > 0
              @find_text.swt_widget.setText @text_editor.text_widget.getSelectionText
            end
            @find_text.swt_widget.selectAll
            @find_text.swt_widget.setFocus
          elsif Glimmer::SWT::SWTProxy.include?(key_event.stateMask, COMMAND_KEY, :shift) && extract_char(key_event) == 'c'
            Clipboard.copy(Dir.local_dir.selected_child.path)
          elsif Glimmer::SWT::SWTProxy.include?(key_event.stateMask, COMMAND_KEY, :shift) && extract_char(key_event) == 'g'
            Dir.local_dir.selected_child.find_previous
          elsif Glimmer::SWT::SWTProxy.include?(key_event.stateMask, COMMAND_KEY, :shift) && extract_char(key_event) == 'w'
            @tab_folder.swt_widget.getItems.each do |tab_item|
              Dir.local_dir.selected_child_path_history.delete(tab_item.getData('file_path'))
              tab_item.getData('proxy')&.dispose
            end
            close_tab_folder
            @tab_item = @text_editor = Dir.local_dir.selected_child = nil 
            @filter_text.swt_widget.selectAll     
            @filter_text.swt_widget.setFocus            
          elsif Glimmer::SWT::SWTProxy.include?(key_event.stateMask, COMMAND_KEY, :alt) && extract_char(key_event) == 'w'
            other_tab_items.each do |tab_item|
              Dir.local_dir.selected_child_path_history.delete(tab_item.getData('file_path'))
              tab_item.getData('proxy')&.dispose
            end
          elsif key_event.stateMask == swt(COMMAND_KEY) && extract_char(key_event) == 'w'
            if selected_tab_item
              Dir.local_dir.selected_child_path_history.delete(Dir.local_dir.selected_child.path)
              selected_tab_item.getData('proxy')&.dispose
              close_tab_folder
              if selected_tab_item.nil?
                @tab_item = @text_editor = Dir.local_dir.selected_child = nil 
                @filter_text.swt_widget.selectAll     
                @filter_text.swt_widget.setFocus
              else
                @text_editor&.text_widget&.setFocus
              end
            end
          elsif Glimmer::SWT::SWTProxy.include?(key_event.stateMask, COMMAND_KEY, :shift) && extract_char(key_event) == ']'
            @tab_folder.swt_widget.setSelection((@tab_folder.swt_widget.getSelectionIndex() + 1) % @tab_folder.swt_widget.getItemCount) if @tab_folder.swt_widget.getItemCount > 0
            @text_editor&.text_widget&.setFocus
          elsif Glimmer::SWT::SWTProxy.include?(key_event.stateMask, COMMAND_KEY, :shift) && extract_char(key_event) == '['
            @tab_folder.swt_widget.setSelection((@tab_folder.swt_widget.getSelectionIndex() - 1) % @tab_folder.swt_widget.getItemCount) if @tab_folder.swt_widget.getItemCount > 0
            @text_editor&.text_widget&.setFocus
          elsif Glimmer::SWT::SWTProxy.include?(key_event.stateMask, COMMAND_KEY, :ctrl) && extract_char(key_event) == ']'
            if @tab_folder2
              if @tab_folder == @tab_folder1              
                @tab_folder = @tab_folder2
              else
                @tab_folder = @tab_folder1
              end
              @tab_item = @tab_folder.swt_widget.getData('selected_tab_item')
              Dir.local_dir.selected_child = @tab_item&.swt_tab_item&.getData('file')
              @tab_item&.swt_tab_item&.getData('text_editor')&.text_widget&.setFocus
            end
          elsif Glimmer::SWT::SWTProxy.include?(key_event.stateMask, COMMAND_KEY, :ctrl) && extract_char(key_event) == '['
            if @tab_folder2
              if @tab_folder == @tab_folder2
                @tab_folder = @tab_folder1
              else
                @tab_folder = @tab_folder2
              end
              @tab_item = @tab_folder.swt_widget.getData('selected_tab_item')
              Dir.local_dir.selected_child = @tab_item&.swt_tab_item&.getData('file')
              @tab_item&.swt_tab_item&.getData('text_editor')&.text_widget&.setFocus
            end
          elsif Glimmer::SWT::SWTProxy.include?(key_event.stateMask, COMMAND_KEY) && extract_char(key_event) == '1'
            @tab_folder.swt_widget.setSelection(0) if @tab_folder.swt_widget.getItemCount >= 1
            @text_editor&.text_widget&.setFocus
          elsif Glimmer::SWT::SWTProxy.include?(key_event.stateMask, COMMAND_KEY) && extract_char(key_event) == '2'
            @tab_folder.swt_widget.setSelection(1) if @tab_folder.swt_widget.getItemCount >= 2
            @text_editor&.text_widget&.setFocus
          elsif Glimmer::SWT::SWTProxy.include?(key_event.stateMask, COMMAND_KEY) && extract_char(key_event) == '3'
            @tab_folder.swt_widget.setSelection(2) if @tab_folder.swt_widget.getItemCount >= 3
            @text_editor&.text_widget&.setFocus
            @tab_folder.swt_widget.setSelection(3) if @tab_folder.swt_widget.getItemCount >= 4
            @text_editor&.text_widget&.setFocus
          elsif Glimmer::SWT::SWTProxy.include?(key_event.stateMask, COMMAND_KEY) && extract_char(key_event) == '5'
            @tab_folder.swt_widget.setSelection(4) if @tab_folder.swt_widget.getItemCount >= 5
            @text_editor&.text_widget&.setFocus
          elsif Glimmer::SWT::SWTProxy.include?(key_event.stateMask, COMMAND_KEY) && extract_char(key_event) == '4'
          elsif Glimmer::SWT::SWTProxy.include?(key_event.stateMask, COMMAND_KEY) && extract_char(key_event) == '6'
            @tab_folder.swt_widget.setSelection(5) if @tab_folder.swt_widget.getItemCount >= 6
            @text_editor&.text_widget&.setFocus
          elsif Glimmer::SWT::SWTProxy.include?(key_event.stateMask, COMMAND_KEY) && extract_char(key_event) == '7'
            @tab_folder.swt_widget.setSelection(6) if @tab_folder.swt_widget.getItemCount >= 7
            @text_editor&.text_widget&.setFocus
          elsif Glimmer::SWT::SWTProxy.include?(key_event.stateMask, COMMAND_KEY) && extract_char(key_event) == '8'
            @tab_folder.swt_widget.setSelection(7) if @tab_folder.swt_widget.getItemCount >= 8
            @text_editor&.text_widget&.setFocus
          elsif Glimmer::SWT::SWTProxy.include?(key_event.stateMask, COMMAND_KEY) && extract_char(key_event) == '9'
            @tab_folder.swt_widget.setSelection(@tab_folder.swt_widget.getItemCount - 1) if @tab_folder.swt_widget.getItemCount > 0
            @text_editor&.text_widget&.setFocus
          elsif Glimmer::SWT::SWTProxy.include?(key_event.stateMask, COMMAND_KEY) && extract_char(key_event) == 'g'
            Dir.local_dir.selected_child.find_next
          elsif Glimmer::SWT::SWTProxy.include?(key_event.stateMask, COMMAND_KEY) && extract_char(key_event) == 'l'
            @line_number_text.swt_widget.selectAll
            @line_number_text.swt_widget.setFocus
          elsif Glimmer::SWT::SWTProxy.include?(key_event.stateMask, COMMAND_KEY) && extract_char(key_event) == 'r'
            @filter_text.swt_widget.selectAll
            @filter_text.swt_widget.setFocus
          elsif Glimmer::SWT::SWTProxy.include?(key_event.stateMask, COMMAND_KEY) && extract_char(key_event) == 't'
            select_tree_item unless @rename_in_progress
            @tree.swt_widget.setFocus
          elsif key_event.keyCode == swt(:esc)
            if @text_editor
              Dir.local_dir.selected_child_path = @text_editor.file.path
              @text_editor&.text_widget&.setFocus
            end
          end
        }
      }

      local_dir = ENV['LOCAL_DIR'] || '.'
      @config_file_path = ::File.join(local_dir, '.gladiator')
      @config = {}
      load_config_ignore_paths
#       Dir.local_dir.all_children # pre-caches children            
    }

    ## Uncomment after_body block to setup observers for widgets in body
    #
    after_body {
      observe(Dir.local_dir, 'children') do
        select_tree_item unless @rename_in_progress
      end
      observe(Dir.local_dir, 'selected_child') do |selected_file|
        if selected_file
          if Gladiator.drag && !@tab_folder2
            @tab_folder1 = @tab_folder
            @tab_folder_sash_form.content {
              @tab_folder = @tab_folder2 = tab_folder
              @tab_folder.swt_widget.setData('proxy', @tab_folder)
            }
          end
          select_tree_item unless @rename_in_progress
          found_tab_item = selected_tab_item
          if found_tab_item
            @tab_folder.swt_widget.setSelection(found_tab_item)
            @tab_item = found_tab_item.getData('proxy')
            @text_editor = found_tab_item.getData('text_editor')
            @tab_folder.swt_widget.setData('selected_tab_item', @tab_item)
          elsif selected_file
            @tab_folder.content {
              @tab_item = tab_item { |the_tab_item|
                text selected_file.name
                fill_layout :horizontal
                @text_editor = the_text_editor = text_editor(file: selected_file)
                @tab_folder.swt_widget.setData('selected_tab_item', @tab_item)
                @text_editor.text_proxy.content {
                  on_focus_gained {
                    tab_folder = the_text_editor.swt_widget.getParent.getParent
                    @tab_folder = tab_folder.getData('proxy')
                    @tab_item = the_tab_item
                    @text_editor = the_text_editor
                    @tab_folder.swt_widget.setData('selected_tab_item', @tab_item)
                    @tab_folder.swt_widget.setSelection(@tab_item.swt_tab_item)
                    Dir.local_dir.selected_child = @tab_item.swt_tab_item.getData('file')
                  }
                }
                on_swt_show {
                  @tab_item = the_tab_item
                  @text_editor = the_text_editor
                  @tab_folder = @tab_item.swt_widget.getParent.getData('proxy')
                  @tab_folder.swt_widget.setData('selected_tab_item', @tab_item)
                  @tab_folder.swt_widget.setSelection(@tab_item.swt_tab_item)
                  Dir.local_dir.selected_child = selected_file
                  async_exec {
                    @text_editor&.text_widget&.setFocus
                  }
                }
              }
              @tab_item.swt_tab_item.setData('file_path', selected_file.path)
              @tab_item.swt_tab_item.setData('file', selected_file)
              @tab_item.swt_tab_item.setData('text_editor', @text_editor)
              @tab_item.swt_tab_item.setData('proxy', @tab_item)
            }                  
            @tab_folder.swt_widget.setSelection(@tab_item.swt_tab_item)
            body_root.pack_same_size
          end
          @text_editor&.text_widget&.setFocus
        end
      end
      observe(Dir.local_dir, 'selected_child') do
        save_config
      end
      observe(Dir.local_dir, 'selected_child.caret_position') do
        save_config
      end
      observe(Dir.local_dir, 'selected_child.top_index') do
        save_config
      end
      load_config
    }

    ## Add widget content inside custom shell body
    ## Top-most widget must be a shell or another custom shell
    #
    body {
      shell {
        text "Gladiator - #{::File.expand_path(Dir.local_dir.path)}"
        minimum_size 520, 250
        size 1440, 900 
        grid_layout(2, false)
        on_swt_show {
          swt_widget.setSize(@config[:shell_width], @config[:shell_height]) if @config[:shell_width] && @config[:shell_height]
          swt_widget.setLocation(@config[:shell_x], @config[:shell_y]) if @config[:shell_x] && @config[:shell_y]          
          @loaded_config = true
        }
        on_swt_close {
          Dir.local_dir.selected_child&.write_dirty_content
        }
        on_widget_disposed {
          Dir.local_dir.selected_child&.write_dirty_content
        }
        on_control_resized {
          save_config
        }
        on_control_moved {
          save_config
        }
        on_shell_deactivated {
          @text_editor&.file&.write_dirty_content
        }        
        composite {
          grid_layout 1, false
          layout_data(:fill, :fill, false, true) {
            width_hint 300
          }
          @filter_text = text {
            layout_data :fill, :center, true, false
            text bind(Dir.local_dir, 'filter')
            on_key_pressed { |key_event|
              if key_event.keyCode == swt(:tab) || 
                  key_event.keyCode == swt(:cr) || 
                  key_event.keyCode == swt(:arrow_up) ||
                  key_event.keyCode == swt(:arrow_down)
                @list.swt_widget.select(0) if @list.swt_widget.getSelectionIndex() == -1
                @list.swt_widget.setFocus
              end
            }    
          }
          composite {
            fill_layout(:vertical) {
              spacing 5
            }
            layout_data(:fill, :fill, true, true)
            @list = list(:border, :h_scroll, :v_scroll) {
              #visible bind(Dir, 'local_dir.filter') {|f| !!f}
              selection bind(Dir.local_dir, :filtered_path)
              on_mouse_up {
                Dir.local_dir.selected_child_path = @list.swt_widget.getSelection.first
              }
              on_key_pressed { |key_event|
                if Glimmer::SWT::SWTProxy.include?(key_event.keyCode, :cr)
                  Dir.local_dir.selected_child_path = @list.swt_widget.getSelection.first
                  @text_editor&.text_widget&.setFocus
                end
              }
              drag_source(DND::DROP_COPY) {
                transfer [TextTransfer.getInstance].to_java(Transfer)
                on_drag_set_data { |event|
                  Gladiator.drag = true
                  list = event.widget.getControl
                  event.data = list.getSelection.first
                }
              }              
            }
            @tree = tree(:virtual, :border, :h_scroll, :v_scroll) {
              #visible bind(Dir, 'local_dir.filter') {|f| !f}
              items bind(Dir, :local_dir), tree_properties(children: :children, text: :name)
              drag_source(DND::DROP_COPY) {
                transfer [TextTransfer.getInstance].to_java(Transfer)
                on_drag_set_data { |event|
                  Gladiator.drag = true
                  tree = event.widget.getControl
                  tree_item = tree.getSelection.first
                  event.data = tree_item.getData.path
                }
              }
              menu {
                @open_menu_item = menu_item {
                  text 'Open'
                  on_widget_selected {
                    Dir.local_dir.selected_child_path = extract_tree_item_path(@tree.swt_widget.getSelection.first)
                  }
                }
                menu_item(:separator)
                menu_item {
                  text 'Delete'
                  on_widget_selected {
                    tree_item = @tree.swt_widget.getSelection.first
                    delete_tree_item(tree_item)
                  }
                }
                menu_item {
                  text 'Refresh'
                  on_widget_selected {
                    Dir.local_dir.refresh
                  }
                }
                menu_item {
                  text 'Rename'
                  on_widget_selected {
                    rename_selected_tree_item
                  }
                }
                menu_item {
                  text 'New Directory'
                  on_widget_selected {
                    add_new_directory_to_selected_tree_item
                  }
                }
                menu_item {
                  text 'New File'
                  on_widget_selected {
                    add_new_file_to_selected_tree_item
                  }
                }
              }
              on_swt_menudetect { |event|
                path = extract_tree_item_path(@tree.swt_widget.getSelection.first)
                @open_menu_item.swt_widget.setEnabled(!::Dir.exist?(path)) if path
              }
              on_mouse_up {
                if Gladiator.drag_and_drop
                  Gladiator.drag_and_drop = false
                else
                  Dir.local_dir.selected_child_path = extract_tree_item_path(@tree.swt_widget.getSelection&.first)
                  @text_editor&.text_widget&.setFocus
                end
              }
              on_key_pressed { |key_event|
                if Glimmer::SWT::SWTProxy.include?(key_event.keyCode, :cr)
                  Dir.local_dir.selected_child_path = extract_tree_item_path(@tree.swt_widget.getSelection&.first)
                  @text_editor&.text_widget&.setFocus
                end
              }
              on_paint_control {
                root_item = @tree.swt_widget.getItems.first
                if root_item && !root_item.getExpanded
                  root_item.setExpanded(true)
                end
              }
            }
          }

          @tree_editor = TreeEditor.new(@tree.swt_widget);
          @tree_editor.horizontalAlignment = swt(:left);
          @tree_editor.grabHorizontal = true;
          @tree_editor.minimumHeight = 20;

        }
        @editor_container = composite {
          grid_layout 1, false
          layout_data :fill, :fill, true, true
          composite {
            grid_layout 3, false
            
            # row 1
            
            @file_path_label = styled_text(:none) {
              layout_data(:fill, :fill, true, false) {
                horizontal_span 3
              }
              background color(:widget_background)
              editable false
              caret nil
              text bind(Dir.local_dir, 'selected_child.path')
              on_mouse_up {
                @file_path_label.swt_widget.selectAll
              }
              on_focus_lost {
                @file_path_label.swt_widget.setSelection(0, 0)
              }
            }
            
            # row 2
            
            label {
              text 'Line:'
            }
            @line_number_text = text {
              layout_data(:fill, :fill, true, false) {
                minimum_width 400
              }
              text bind(Dir.local_dir, 'selected_child.line_number', on_read: :to_s, on_write: :to_i)
              on_key_pressed { |key_event|
                if key_event.keyCode == swt(:cr)
                  @text_editor&.text_widget&.setFocus
                end
              }
              on_verify_text { |event|
                event.doit = !event.text.match(/^\d*$/).to_a.empty? 
              }
            }
            label
            
            # row 3

            label {
              text 'Find:'
            }
            @find_text = text {
              layout_data(:fill, :center, true, false) {
                minimum_width 400
              }
              text bind(Dir.local_dir, 'selected_child.find_text')
              on_key_pressed { |key_event|
                if key_event.stateMask == swt(COMMAND_KEY) && key_event.keyCode == swt(:cr)
                  Dir.local_dir.selected_child.case_sensitive = !Dir.local_dir.selected_child.case_sensitive
                  Dir.local_dir.selected_child&.find_next
                end
                if key_event.keyCode == swt(:cr)
                  Dir.local_dir.selected_child&.find_next
                end
              }
            }
            composite {
              row_layout
              button(:check) {
                selection bind(Dir.local_dir, 'selected_child.case_sensitive')
                on_key_pressed { |key_event|
                  if key_event.keyCode == swt(:cr)
                    Dir.local_dir.selected_child&.find_next
                  end
                }                
              }
              label {
                text 'Case-sensitive'
              }
            }
            
            # row 4
            
            label {
              text 'Replace:'
            }
            @replace_text = text {
              layout_data(:fill, :fill, true, false) {
                minimum_width 300
              }
              text bind(Dir.local_dir, 'selected_child.replace_text')
              on_focus_gained {              
                Dir.local_dir.selected_child&.ensure_find_next
              }
              on_key_pressed { |key_event|
                if key_event.keyCode == swt(:cr)
                  if Dir.local_dir.selected_child
                    Command.do(Dir.local_dir.selected_child, :replace_next!)
                  end
                end
              }
            }
            label
          }
          @tab_folder_sash_form = sash_form {
            layout_data(:fill, :fill, true, true) {
              width_hint 640
              height_hint 480
            }
            sash_width 10
            @tab_folder = tab_folder {
              drag_source(DND::DROP_COPY) {
                transfer [TextTransfer.getInstance].to_java(Transfer)
                event_data = nil
                on_drag_start {|event|
                  Gladiator.drag = true
                  tab_folder = event.widget.getControl
                  tab_item = tab_folder.getItem(Point.new(event.x, event.y))
                  event_data = tab_item.getData('file_path')
                }
                on_drag_set_data { |event|
                  event.data = event_data
                }
              }
            }
            @tab_folder.swt_widget.setData('proxy', @tab_folder)            
          }
        }
      }
    }
    
    def load_config_ignore_paths
      # TODO eliminate duplication with load_config
      if ::File.exists?(@config_file_path)
        config_yaml = ::File.read(@config_file_path)
        return if config_yaml.to_s.strip.empty?
        @config = YAML.load(config_yaml)
        Dir.local_dir.ignore_paths = @config[:ignore_paths] if @config[:ignore_paths]
        Dir.local_dir.ignore_paths ||= ['packages', 'tmp']
      else
        @loaded_config = true
      end
    end
    
    def load_config
      if ::File.exists?(@config_file_path)
        config_yaml = ::File.read(@config_file_path)
        return if config_yaml.to_s.strip.empty?
        @config = YAML.load(config_yaml)
        Dir.local_dir.ignore_paths = @config[:ignore_paths] if @config[:ignore_paths]
        Dir.local_dir.ignore_paths ||= ['packages', 'tmp']
        open_file_paths1 = @config[:open_file_paths1] || @config[:open_file_paths]
        open_file_paths2 = @config[:open_file_paths2]
        open_file_paths1.to_a.each do |file_path|
          Dir.local_dir.selected_child_path = file_path
        end
        Gladiator.drag = true
        open_file_paths2.to_a.each do |file_path|
          Dir.local_dir.selected_child_path = file_path
        end
        Gladiator.drag = false
        Dir.local_dir.selected_child_path = @config[:selected_child_path] if @config[:selected_child_path]
        Dir.local_dir.selected_child&.caret_position  = Dir.local_dir.selected_child&.caret_position_for_caret_position_start_of_line(@config[:caret_position].to_i) if @config[:caret_position]
        Dir.local_dir.selected_child&.top_index = @config[:top_index].to_i if @config[:top_index]
      else
        @loaded_config = true
      end
    end
  
    def save_config
      return unless @loaded_config
      child = Dir.local_dir.selected_child
      return if child.nil?
      tab_folder1 = @tab_folder1 || @tab_folder
      tab_folder2 = @tab_folder2
      open_file_paths1 = tab_folder1&.swt_widget&.items&.to_a.map {|i| i.get_data('file_path')}
      open_file_paths2 = tab_folder2&.swt_widget&.items&.to_a.map {|i| i.get_data('file_path')}
      @config = {
        selected_child_path: child.path,
        caret_position: child.caret_position,
        top_index: child.top_index,
        shell_width: swt_widget&.getBounds&.width,
        shell_height: swt_widget&.getBounds&.height,
        shell_x: swt_widget&.getBounds&.x,
        shell_y: swt_widget&.getBounds&.y,
        open_file_paths1: open_file_paths1,
        open_file_paths2: open_file_paths2,
        ignore_paths: Dir.local_dir.ignore_paths
      }
      config_yaml = YAML.dump(@config)
      ::File.write(@config_file_path, config_yaml) unless config_yaml.to_s.empty?
    rescue => e
      puts e.full_message
    end
    
    def close_tab_folder
      if @tab_folder2 && !selected_tab_item
        if @tab_folder == @tab_folder2
          @tab_folder2.swt_widget.dispose
          @tab_folder = @tab_folder1
        else
          @tab_folder1.swt_widget.dispose
          @tab_folder = @tab_folder1 = @tab_folder2
        end
        @tab_folder2 = nil 
        
        @tab_item = @tab_folder.swt_widget.getData('selected_tab_item')
        @text_editor = @tab_item.swt_tab_item.getData('text_editor')
        Dir.local_dir.selected_child = @tab_item.swt_tab_item.getData('file')
       
        body_root.pack_same_size
      end
    end
    
    def selected_tab_item
      @tab_folder.swt_widget.getItems.detect { |ti| ti.getData('file_path') == Dir.local_dir.selected_child&.path }
    end

    def other_tab_items
      @tab_folder.swt_widget.getItems.reject { |ti| ti.getData('file_path') == Dir.local_dir.selected_child&.path }
    end

    def extract_tree_item_path(tree_item)
      return if tree_item.nil?
      if tree_item.getParentItem
        ::File.join(extract_tree_item_path(tree_item.getParentItem), tree_item.getText)
      else
        Dir.local_dir.path
      end
    end
    
    def select_tree_item
      return unless Dir.local_dir.selected_child&.name
      tree_items_to_select = @tree.depth_first_search { |ti| ti.getData.path == Dir.local_dir.selected_child.path }
      @tree.swt_widget.setSelection(tree_items_to_select)
    end

    def delete_tree_item(tree_item)
      return if tree_item.nil?
      file = tree_item.getData
      parent_path = ::File.dirname(file.path)
      file.delete! # TODO consider supporting command undo/redo
      Dir.local_dir.refresh(async: false)
      parent_tree_item = @tree.depth_first_search {|ti| ti.getData.path == parent_path}.first
      @tree.swt_widget.showItem(parent_tree_item)
      parent_tree_item.setExpanded(true)
      # TODO close text editor tab 
#       if file.is_a?(::File)
        # close tab
#       end
    rescue => e
      puts e.full_message      
    end
    
    def rename_selected_tree_item
      Dir.local_dir.pause_refresh
      tree_item = @tree.swt_widget.getSelection.first
      rename_tree_item(tree_item)
    end
    
    def add_new_directory_to_selected_tree_item
      Dir.local_dir.pause_refresh
      tree_item = @tree.swt_widget.getSelection.first
      directory_path = extract_tree_item_path(tree_item)
      return if directory_path.nil?
      if !::Dir.exist?(directory_path)
        tree_item = tree_item.getParentItem
        directory_path = ::File.dirname(directory_path)
      end
      new_directory_path = ::File.expand_path(::File.join(directory_path, 'new_directory'))
      FileUtils.mkdir_p(new_directory_path)
      Dir.local_dir.refresh(async: false, force: true)
      new_tree_item = @tree.depth_first_search {|ti| ti.getData.path == new_directory_path}.first
      @tree.swt_widget.showItem(new_tree_item)
      rename_tree_item(new_tree_item, true)
    end
    
    def add_new_file_to_selected_tree_item
      Dir.local_dir.pause_refresh
      tree_item = @tree.swt_widget.getSelection.first
      directory_path = extract_tree_item_path(tree_item)
      if !::Dir.exist?(directory_path)
        tree_item = tree_item.getParentItem
        directory_path = ::File.dirname(directory_path)
      end
      new_file_path = ::File.expand_path(::File.join(directory_path, 'new_file'))
      FileUtils.touch(new_file_path)
      # TODO look into refreshing only the parent directory to avoid slowdown
      Dir.local_dir.refresh(async: false, force: true)
      new_tree_item = @tree.depth_first_search {|ti| ti.getData.path == new_file_path}.first
      @tree.swt_widget.showItem(new_tree_item)
      rename_tree_item(new_tree_item, true)
    end
    
    def rename_tree_item(tree_item, open_afterwards = false)
      @tree.edit_tree_item(
        tree_item,
        after_write: -> (edited_tree_item) {
          file = edited_tree_item.getData
          file_path = file.path
          # TODO rename file in tab title
          Dir.local_dir.selected_child_path = file_path if open_afterwards
          Dir.local_dir.resume_refresh            
        },
        after_cancel: -> {
          Dir.local_dir.resume_refresh            
        }
      )
    end

    def extract_char(event)
      event.keyCode.chr
    rescue => e
      nil
    end
  end
end