require 'fileutils'
require 'xcodeproj'

module Synx
  class Project < Xcodeproj::Project

    SYNXRONIZE_DIR = File.join(ENV["HOME"], '.synx')
    private_constant :SYNXRONIZE_DIR

    DEFAULT_EXCLUSIONS = %W(/Libraries /Frameworks /Products)
    private_constant :DEFAULT_EXCLUSIONS

    attr_accessor :delayed_groups_set_path, :group_exclusions, :prune

    def sync(options={})
      set_options(options)
      presync_check
      Synx::Tabber.increase
      Synx::Tabber.puts "Syncing files that are included in Xcode project...".bold.white
      main_group.all_groups.each { |gr| gr.sync(main_group) }
      puts "\n\n"
      Synx::Tabber.puts "Syncing files that are not included in Xcode project..".bold.white
      main_group.all_groups.each(&:move_entries_not_in_xcodeproj)
      transplant_work_project
      Synx::Tabber.decrease
      save
    end

    def presync_check
      forward_slash_groups = main_group.groups_containing_forward_slash
      unless forward_slash_groups.empty?
        Synx::Tabber.puts "Synx cannot sync projects with groups that contain '/'. Please rename the following groups before running synx again:".yellow
        Synx::Tabber.increase
        forward_slash_groups.each do |group|
          Synx::Tabber.puts group.hierarchy_path
        end
        abort
      end
    end

    def set_options(options)
      self.prune = options[:prune]

      if options[:no_default_exclusions]
        self.group_exclusions = []
      else
        self.group_exclusions = DEFAULT_EXCLUSIONS
      end

      self.group_exclusions |= options[:group_exclusions] if options[:group_exclusions]
    end
    private :set_options

    def transplant_work_project
      # Move the synced entries over
      Dir.glob(work_root_pathname + "*").each do |path|
        FileUtils.rm_rf(work_pathname_to_pathname(Pathname(path)))
        FileUtils.mv(path, root_pathname.to_s)
      end
    end
    private :transplant_work_project

    def root_pathname
      @root_pathname ||= Pathname(path).parent
    end

    def work_root_pathname
      if @work_root_pathname 
        @work_root_pathname
      else
        @work_root_pathname = Pathname(File.join(SYNXRONIZE_DIR, root_pathname.basename.to_s))
        # Clean up any previous synx and start fresh
        FileUtils.rm_rf(@work_root_pathname.to_s) if @work_root_pathname.exist?
        @work_root_pathname.mkpath
        @work_root_pathname
      end
    end

    # We build up the new project structure in a temporary workspace, so convert a file path in the project space to
    # one in the temp workspace.
    def pathname_to_work_pathname(pathname)
      work_root_pathname + pathname.relative_path_from(root_pathname)
    end

    def work_pathname_to_pathname(work_pathname)
      root_pathname + work_pathname.relative_path_from(work_root_pathname)
    end

    def pathname_is_inside_root_pathname?(grandchild_pathname)
      grandchild_pathname.realpath.to_s =~ /^#{root_pathname.realpath.to_s}/
    end

    def group_exclusions=(new_exclusions)
      new_exclusions.each do |exclusion|
        # Group paths always start with a '/', so put one there if it isn't already.
        exclusion.prepend("/") unless exclusion[0] == "/"
        # Don't check our own default exclusions -- they may not have it in their project.
        unless DEFAULT_EXCLUSIONS.include?(exclusion)
          # remove leading '/' for this check
          exclusion = exclusion.dup
          exclusion[0] = ''
          unless self[exclusion]
            raise IndexError, "No group #{exclusion} exists"
          end
        end
      end

      @group_exclusions = new_exclusions
    end

  end
end