require 'builder'
require 'nokogiri'

require File.join(File.dirname(__FILE__), "..", "..", "app", "models", 'project.rb')

require File.join(File.dirname(__FILE__), 'build_step.rb')
require File.join(File.dirname(__FILE__), 'publisher', 'base.rb')
require File.join(File.dirname(__FILE__), 'publisher', 'mail.rb')
require File.join(File.dirname(__FILE__), 'publisher', 'storywise.rb')
require File.join(File.dirname(__FILE__), 'publisher', 'x10.rb')
require File.join(File.dirname(__FILE__), 'publisher', 'wemo.rb')
require File.join(File.dirname(__FILE__), 'publisher', 'slack.rb')
require File.join(File.dirname(__FILE__), 'publisher', 'tplink.rb')
require File.join(File.dirname(__FILE__), 'publisher', 'http_callback.rb')

module BuildWise
  class ProjectConfiguration


    attr_accessor :is_parallel     

    

    attr_accessor :is_load_testing


    attr_accessor :is_performance_testing
    
    attr_accessor :filename # the configuration file path
    attr_accessor :application_root
    attr_accessor :application_name

    attr_accessor :bin_path # the path to add to $PATH

    attr_accessor :scm_type
    attr_accessor :scm_url
    attr_accessor :scm_user
    attr_accessor :scm_password
    attr_accessor :scm_branch # for Git

    attr_accessor :builder_cmd # the customize path of command

    attr_accessor :builder_prepare_command
    attr_accessor :build_steps
    attr_accessor :builder_cleanup_command

    attr_accessor :builder_ui_test_task_enabled
    attr_accessor :builder_ui_test_framework    

    attr_accessor :remove_suite_name_from_test_case
  
    
    attr_accessor :publisher_mail_active
    attr_accessor :publisher_mail_delivery_method # SMTP or
    attr_accessor :publisher_mail_authentication # Plain
    attr_accessor :publisher_mail_address #
    attr_accessor :publisher_mail_port #
    attr_accessor :publisher_domain
    attr_accessor :publisher_mail_user
    attr_accessor :publisher_mail_password
    attr_accessor :publisher_mail_recipients # email address

    attr_accessor :publisher_storywise_active
    attr_accessor :publisher_storywise_url
    attr_accessor :publisher_storywise_release

    attr_accessor :publisher_x10_active
    attr_accessor :publisher_x10_command
    attr_accessor :publisher_x10_failure_house_and_unit_code
    attr_accessor :publisher_x10_success_house_and_unit_code

    attr_accessor :publisher_wemo_active
    attr_accessor :publisher_wemo_success_ip
    attr_accessor :publisher_wemo_failure_ip

    attr_accessor :publisher_tplink_active
    attr_accessor :publisher_tplink_success_device_name
    attr_accessor :publisher_tplink_failure_device_name

    attr_accessor :publisher_slack_active
    attr_accessor :publisher_slack_webhook_url
    attr_accessor :publisher_slack_channel
    attr_accessor :publisher_slack_username

    attr_accessor :publisher_http_callback_active
    attr_accessor :publisher_http_callback_webhook_url

    attr_accessor :environment_variables

    attr_accessor :scheduler_polling
    attr_accessor :scheduler_at_time

    attr_accessor :ui_tests_dir
    
    attr_accessor :ui_tests_report_dir  # for sequential only
    attr_accessor :ui_tests_local_working_dir # invoke test execution from server

    attr_accessor :enable_log


    attr_accessor :files_to_archive


    attr_accessor :retry_on_another_machine # only for distributed mode, retry failed test on another machine
    attr_accessor :app_name # the application name as the key for looking up settings
    attr_accessor :agent_work_dir # checkout project folder on BuildWise Agents, eg. c:\work/agiletravel




    attr_accessor :default_agents # customize which agents used for this project (full build)


    attr_accessor :api_key
    

    attr_accessor :show_test_scripts_to_public
    

    attr_accessor :enable_dynamic_ordering # within project build history, run most often failed first, ENV["DYNAMIC_ORDERING"] 
    


    attr_accessor :enable_dynamic_feedback # enable ENV["DYNAMIC_FEEDBACK"]
    attr_accessor :dynamic_feedback_project_identifier # typically geting failed test from other project, ENV["DYNAMIC_FEEDBACK_PROJECT_IDENTIFIER"]

    attr_accessor :target_browser # ENV["BROWSER"]
    

    attr_accessor :force
    attr_accessor :quiet
    attr_accessor :build_dir    

    attr_accessor :log_request_response_for_individual_test_execution
    
    attr_accessor :user_story_prefix_url
    attr_accessor :highlight_test_output
    
    attr_accessor :load_server_url
    attr_accessor :load_max_agent_count
    attr_accessor :load_max_iteration_count
    attr_accessor :load_max_duration # in minutes
    attr_accessor :load_repeat_times_within_test
    
    attr_accessor :load_criteria_error_rate
    attr_accessor :load_operation_criteria 
    
    attr_accessor :deploy_rake_task # the optinal task to invoke to deploy on a green build
    
    def initialize(file_path, opts = {})
      @filename = file_path
      @scheduler_polling = false
      @scheduler_at_time = false
      @retry_on_another_machine = false
      @target_browser = "chrome"
      @environment_variables = { }
      @enable_log = true
      @files_to_archive
      @remove_suite_name_from_test_case = true
      @show_test_scripts_to_public = false      
      @enable_dynamic_ordering = false
      @enable_dynamic_feedback = false
      
      @enable_request_response_logging_for_individual_test_execution = false
      @highlight_test_output = false
      
      @build_steps = []
      
      @load_server_url = ""
      @load_max_agent_count = 10
      @load_max_iteration_count = 3
      @load_max_duration = 3 # minutes
      @load_repeat_times_within_test = 2
      
      @load_criteria_error_rate = 0.1 # default 99.9% pass regarded as success
      @load_operation_criteria = {}
      
      @deploy_rake_task = nil
      


    end


    def self.default_load_test_step
      step = ::BuildWise::BuildStep.new("Load Test")
      step.builder = "Rake"
      step.order = "25"
      step.task = "-f Rakefile ci:load_tests:mix"
      step.enabled = true
      step.add_attribute("test_syntax_framework", "RSpec")
      step.add_attribute("remove_suite_name_from_test_case", true)
      return step
    end
    

    def self.default_ui_test_step
      step = ::BuildWise::BuildStep.new("UI Test")
      step.builder = "Rake"
      step.order = "25"
      step.task = "-f Rakefile ci:ui_tests:quick"        
      step.enabled = true
      step.add_attribute("test_syntax_framework", "RSpec")
      step.add_attribute("remove_suite_name_from_test_case", true)
      return step
    end
    

    def self.default_test_stats_step
      step = ::BuildWise::BuildStep.new("UI Test Stats")
      step.builder = "Rake"
      step.order = "20"
      step.task = "-f Rakefile test:stats"      
      step.enabled = false      
      return step
    end
    
    


    def build_environment_variables
      all_env_vars = @environment_variables.dup
      the_user_set_browser_env_var =  all_env_vars["BROWSER"]
      if the_user_set_browser_env_var && ["chrome", "firefox", "ie", "safari", "edge"].include?(the_user_set_browser_env_var.downcase)
        @target_browser = the_user_set_browser_env_var.downcase
      end
    
      all_env_vars["BROWSER"] = @target_browser if @target_browser

      all_env_vars["DYNAMIC_ORDERING"] = @enable_dynamic_ordering

      all_env_vars["INTELLIGENT_ORDERING"] = @enable_dynamic_ordering
      

      all_env_vars["DYNAMIC_FEEDBACK"] = @enable_dynamic_feedback
      all_env_vars["DYNAMIC_FEEDBACK_PROJECT_IDENTIFIER"] = @dynamic_feedback_project_identifier

      return all_env_vars
    end
    
     

    def normalize_build_steps
      the_order = 0
      self.build_steps.each do |step|
        the_order += 2
        step.order = the_order
      end
    end
    
    def save(to_file = nil)
      to_file ||= self.filename      
      @errors = []
      
      file_parent_dir =  File.dirname(to_file)
      unless File.exist?(file_parent_dir)
        FileUtils.mkdir_p(file_parent_dir)
      end
      

      if self.is_parallel
        @errors << "The directory for UI tests not set" if self.ui_tests_dir.blank?
        @errors << "Application name is not set for parallel execution" if self.app_name.blank?
        @errors << "The checked out directory (on build agents) not specified for parallel execution" if self.agent_work_dir.blank?          
      end      
      
      begin
        the_xml = self.to_xml

        File.open(to_file, "w") { |f| f.print the_xml }
      rescue => e

        puts "\n\nfailed to save project work space (folder exists?), ignore. #{e}, #{e.backtrace}"
        $logger.warn "failed to save project work space (folder exists?), ignore. #{e}" if $logger
        @errors << "failed to save project: #{e}"
      end

      if @errors.empty? == 0 
        return true, []
      else
        return false, @errors
      end
    end

    def is_not_empty?(var)
      return var && var.size > 0
    end

    def to_xml
      
      normalize_build_steps
      
      xml = ::Builder::XmlMarkup.new(:indent => 2)
      xml.instruct! :xml, :version => "1.1", :encoding => "UTF-8"
      xml.configuration(:parallel => @is_parallel, :load_testing => @is_load_testing, :performance_testing => @is_performance_testing, :version => "1.8") do

        xml.filename @filename

        xml.scm do
          xml.type @scm_type if @scm_type
          xml.url @scm_url if @scm_url
          xml.login @scm_user if is_not_empty?(@scm_user)
          xml.password @scm_password if is_not_empty?(@scm_password)
          xml.branch @scm_branch if is_not_empty?(@scm_branch)
        end

        xml.build_steps do
          xml.prepare_command @builder_prepare_command if is_not_empty?(@builder_prepare_command)
          self.build_steps.each do |step|                      
            xml.step step.to_hash
          end
          xml.cleanup_command @builder_cleanup_command if is_not_empty?(@builder_cleanup_command)
        end

        if @environment_variables && @environment_variables.size > 0
          xml.environment_variables do
            @environment_variables.each do |key, value|
              xml.entry(:key => key, :value => value)
            end
          end
        end

        xml.scheduler do
          xml.polling @scheduler_polling
          xml.at_time @scheduler_at_time




        end



        xml.sequential(:enable_dynamic_ordering => @enable_dynamic_ordering) do
          xml.dynamic_feedback_project_identifier @dynamic_feedback_project_identifier
          xml.target_browser @target_browser
        end
        
        xml.parallel do
          xml.app_name @app_name
          xml.retry_on_another_machine @retry_on_another_machine
          xml.agent_work_dir @agent_work_dir
          xml.default_agents @default_agents
          
          xml.load_testing do
            xml.load_server_url @load_server_url
            xml.max_agent_count @load_max_agent_count
            xml.max_iteration_count @load_max_iteration_count
            xml.max_duration @load_max_duration # in minutes
            xml.repeat_times_within_test @load_repeat_times_within_test
            xml.success_criteria do
              xml.load_criteria_error_rate @load_criteria_error_rate
              xml.load_operation_criteria do
                @load_operation_criteria.each do |key, val|
                  xml.entry :operation => key,  :average_time => val[:average_time], :slowest_time => val[:slowest_time], :is_active => val[:is_active].to_s
                end              
              end
            end
          end                    
        end          

        xml.ui_tests do
          xml.dir @ui_tests_dir if is_not_empty?(@ui_tests_dir)
          xml.report_dir @ui_tests_report_dir if is_not_empty?(@ui_tests_report_dir)
          xml.local_working_dir @ui_tests_local_working_dir if is_not_empty?(@ui_tests_local_working_dir)
        end

        xml.files_to_archive @files_to_archive if @files_to_archive

        if is_not_empty?(@api_key)            
          xml.api do
            xml.api_key  @api_key
          end            
        end
        
        if is_not_empty?(@user_story_prefix_url)            
          xml.requirement_traceability do
            xml.user_story_prefix_url  @user_story_prefix_url
          end            
        end
              
        xml.user_interface do
          xml.show_test_scripts_to_public @show_test_scripts_to_public
          xml.highlight_test_output @highlight_test_output
        end
        
        xml.publisher do
          if is_not_empty?(@publisher_mail_recipients)
            xml.mail do
              xml.active @publisher_mail_active
              xml.delivery_method @publisher_mail_delivery_method if @publisher_mail_delivery_method
              xml.authentication @publisher_mail_authentication if @publisher_mail_authentication
              xml.address @publisher_mail_address if @publisher_mail_address
              xml.port @publisher_mail_port if @publisher_mail_port
              xml.user @publisher_mail_user if @publisher_mail_user
              xml.password @publisher_mail_password if @publisher_mail_password
              xml.recipents @publisher_mail_recipients if @publisher_mail_recipients
            end
          end

          if is_not_empty?(@publisher_storywise_url)
            xml.storywise do
              xml.active @publisher_storywise_active
              xml.server @publisher_storywise_url if @publisher_storywise_url
              xml.release @publisher_storywise_release if @publisher_storywise_release
            end
          end

          if is_not_empty?(@publisher_x10_command)
            xml.x10 do
              xml.active @publisher_x10_active
              xml.command @publisher_x10_command if @publisher_x10_command
              xml.failure_house_and_unit_code @publisher_x10_failure_house_and_unit_code if @publisher_x10_failure_house_and_unit_code
              xml.success_house_and_unit_code @publisher_x10_success_house_and_unit_code if @publisher_x10_success_house_and_unit_code
            end
          end

          
          if is_not_empty?(@publisher_wemo_failure_ip)
            xml.wemo do
              xml.active @publisher_wemo_active
              xml.success_ip @publisher_wemo_success_ip if @publisher_wemo_success_ip
              xml.failure_ip @publisher_wemo_failure_ip if @publisher_wemo_failure_ip
            end
          end

          
          if is_not_empty?(@publisher_tplink_failure_device_name)
            xml.tplink do
              xml.active @publisher_tplink_active
              xml.success_device @publisher_tplink_success_device_name if @publisher_tplink_success_device_name
              xml.failure_device @publisher_tplink_failure_device_name if @publisher_tplink_failure_device_name
            end
          end

          if is_not_empty?(@publisher_slack_webhook_url)
            xml.slack do
              xml.active @publisher_slack_active
              xml.webhook_url @publisher_slack_webhook_url if @publisher_slack_webhook_url
              xml.channel @publisher_slack_channel if @publisher_slack_channel
              xml.username @publisher_slack_username if @publisher_slack_username
            end
          end
          
          if is_not_empty?(@publisher_http_callback_webhook_url)
            xml.http_callback do
              xml.active @publisher_http_callback_active
              xml.webhook_url @publisher_http_callback_webhook_url if @publisher_http_callback_webhook_url
            end
          end      
          
        end
        
        xml.deploy do
          xml.rake_task @deploy_rake_task
        end
        
      end

      xml.target!
    end

    def self.load_from(file)
      return nil if file.nil? || !File.exist?(file)
      the_config = self.new(file)
      xml = File.read(file)
      doc = Nokogiri::XML(xml)
      
      the_config.scm_type = doc.at("configuration/scm/type").inner_text if doc.at("configuration/scm/type")
      the_config.scm_url = doc.at("configuration/scm/url").inner_text if doc.at("configuration/scm/url")
      the_config.scm_user = doc.at("configuration/scm/user").inner_text if doc.at("configuration/scm/user")
      the_config.scm_password = doc.at("configuration/scm/password").inner_text if doc.at("configuration/scm/password")
      the_config.scm_branch = doc.at("configuration/scm/branch").inner_text if doc.at("configuration/scm/branch")

      the_config.builder_cmd = doc.at("configuration/builder/cmd").inner_text if doc.at("configuration/builder/cmd")

      the_config.builder_prepare_command = doc.at("configuration/build_steps/prepare_command").inner_text if doc.at("configuration/build_steps/prepare_command")

      the_config.build_steps = []
      (doc/:configuration/:build_steps/:step).each do |step|        
        hash = {}
        step.keys.each do |key|
          hash[key] = step[key]
        end
                
        build_step = ::BuildWise::BuildStep.new_from_hash(hash)
        build_step_test_framework = build_step.get_attribute("test_syntax_framework") || build_step.get_attribute("ui_test_framework")

        if !build_step_test_framework.blank? && !build_step.task.blank?
          the_config.builder_ui_test_task_enabled = true
          the_config.builder_ui_test_framework = build_step_test_framework
          the_config.remove_suite_name_from_test_case =  build_step.get_attribute("remove_suite_name_from_test_case")
        end
        the_config.build_steps << build_step        
      end
      
      the_config.builder_cleanup_command = doc.at("configuration/build_steps/cleanup_command").inner_text if doc.at("configuration/build_steps/cleanup_command")      

      the_config.publisher_mail_active = false
      the_config.publisher_mail_active = doc.at("configuration/publisher/mail/active").inner_text == "true" if doc.at("configuration/publisher/mail/active")
      the_config.publisher_mail_delivery_method = doc.at("configuration/publisher/mail/delivery_method").inner_text if doc.at("configuration/publisher/mail/delivery_method")
      the_config.publisher_mail_authentication = doc.at("configuration/publisher/mail/authentication").inner_text if doc.at("configuration/publisher/mail/authentication")
      the_config.publisher_mail_address = doc.at("configuration/publisher/mail/address").inner_text if doc.at("configuration/publisher/mail/address")
      the_config.publisher_mail_port = doc.at("configuration/publisher/mail/port").inner_text if doc.at("configuration/publisher/mail/port")
      the_config.publisher_mail_user = doc.at("configuration/publisher/mail/user").inner_text if doc.at("configuration/publisher/mail/user")
      the_config.publisher_mail_password = doc.at("configuration/publisher/mail/password").inner_text if doc.at("configuration/publisher/mail/password")
      the_config.publisher_mail_recipients = doc.at("configuration/publisher/mail/recipents").inner_text if doc.at("configuration/publisher/mail/recipents")

      the_config.publisher_x10_active = false
      the_config.publisher_storywise_active = doc.at("configuration/publisher/storywise/active").inner_text == "true" if doc.at("configuration/publisher/storywise/active")
      the_config.publisher_storywise_url = doc.at("configuration/publisher/storywise/server").inner_text if doc.at("configuration/publisher/storywise/server")
      the_config.publisher_storywise_release = doc.at("configuration/publisher/storywise/release").inner_text if doc.at("configuration/publisher/storywise/release")

      the_config.publisher_x10_active = false
      the_config.publisher_x10_active = doc.at("configuration/publisher/x10/active").inner_text == "true" if doc.at("configuration/publisher/x10/active")
      the_config.publisher_x10_command = doc.at("configuration/publisher/x10/command").inner_text if doc.at("configuration/publisher/x10/command")
      the_config.publisher_x10_failure_house_and_unit_code = doc.at("configuration/publisher/storywise/failure_house_and_unit_code").inner_text if doc.at("configuration/publisher/storywise/failure_house_and_unit_code")
      the_config.publisher_x10_success_house_and_unit_code = doc.at("configuration/publisher/storywise/success_house_and_unit_code").inner_text if doc.at("configuration/publisher/storywise/success_house_and_unit_code")

      the_config.publisher_wemo_active = false
      the_config.publisher_wemo_active = doc.at("configuration/publisher/wemo/active").inner_text == "true" if doc.at("configuration/publisher/wemo/active")
      the_config.publisher_wemo_success_ip = doc.at("configuration/publisher/wemo/success_ip").inner_text if doc.at("configuration/publisher/wemo/success_ip")
      the_config.publisher_wemo_failure_ip = doc.at("configuration/publisher/wemo/failure_ip").inner_text if doc.at("configuration/publisher/wemo/failure_ip")

      the_config.publisher_tplink_active = false
      the_config.publisher_tplink_active = doc.at("configuration/publisher/tplink/active").inner_text == "true" if doc.at("configuration/publisher/tplink/active")
      the_config.publisher_tplink_success_device_name = doc.at("configuration/publisher/tplink/success_device").inner_text if doc.at("configuration/publisher/tplink/success_device")
      the_config.publisher_tplink_failure_device_name = doc.at("configuration/publisher/tplink/failure_device").inner_text if doc.at("configuration/publisher/tplink/failure_device")

      the_config.publisher_slack_active = false 
      the_config.publisher_slack_active = doc.at("configuration/publisher/slack/active").inner_text == "true" if doc.at("configuration/publisher/slack/active")
      the_config.publisher_slack_webhook_url = doc.at("configuration/publisher/slack/webhook_url").inner_text if doc.at("configuration/publisher/slack/webhook_url")
      the_config.publisher_slack_channel = doc.at("configuration/publisher/slack/channel").inner_text if doc.at("configuration/publisher/slack/channel")
      the_config.publisher_slack_username = doc.at("configuration/publisher/slack/username").inner_text if doc.at("configuration/publisher/slack/username")
      
      the_config.publisher_http_callback_active = false 
      the_config.publisher_http_callback_active = doc.at("configuration/publisher/http_callback/active").inner_text == "true" if doc.at("configuration/publisher/http_callback/active")
      the_config.publisher_http_callback_webhook_url = doc.at("configuration/publisher/http_callback/webhook_url").inner_text if doc.at("configuration/publisher/http_callback/webhook_url")
      
      
      the_config.scheduler_polling = false 
      the_config.scheduler_polling = doc.at("configuration/scheduler/polling").inner_text == "true" if doc.at("configuration/scheduler/polling")
      the_config.scheduler_at_time = false
      the_config.scheduler_at_time = doc.at("configuration/scheduler/at_time").inner_text == "true" if doc.at("configuration/scheduler/at_time")

      if doc.at("configuration/parallel")
      
        the_config.retry_on_another_machine = doc.at("configuration/parallel/retry_on_another_machine").inner_text == "true" if doc.at("configuration/parallel/retry_on_another_machine")
        the_config.app_name = doc.at("configuration/parallel/app_name").inner_text if doc.at("configuration/parallel/app_name")
        the_config.agent_work_dir = doc.at("configuration/parallel/agent_work_dir").inner_text if doc.at("configuration/parallel/agent_work_dir")

        if doc.at("configuration/parallel/default_agents")
          default_agents_text = doc.at("configuration/parallel/default_agents").inner_text
          if default_agents_text && default_agents_text.strip.size > 1
            the_config.default_agents = default_agents_text.split(",")
          else
            the_config.default_agents = nil
          end

        else
          the_config.default_agents = nil
        end

        the_config.load_server_url = doc.at("configuration/parallel/load_testing/load_server_url").inner_text.strip rescue ""
        the_config.load_max_agent_count = doc.at("configuration/parallel/load_testing/max_agent_count").inner_text.to_i rescue 10
        the_config.load_max_iteration_count = doc.at("configuration/parallel/load_testing/max_iteration_count").inner_text.to_i rescue 2
        the_config.load_max_duration = doc.at("configuration/parallel/load_testing/max_duration").inner_text.to_i rescue 10
        the_config.load_repeat_times_within_test = doc.at("configuration/parallel/load_testing/repeat_times_within_test").inner_text.to_i rescue 2
              
      end
            
      the_config.ui_tests_dir = doc.at("configuration/ui_tests/dir").inner_text if doc.at("configuration/ui_tests/dir")
      the_config.ui_tests_report_dir = doc.at("configuration/ui_tests/report_dir").inner_text if doc.at("configuration/ui_tests/report_dir")
      the_config.ui_tests_local_working_dir = doc.at("configuration/ui_tests/local_working_dir").inner_text if doc.at("configuration/ui_tests/local_working_dir")

      (doc/:configuration/:environment_variables/:entry).each do |item|
        the_config.environment_variables[item['key']] = item['value']
      end

      the_config.files_to_archive = doc.at("configuration/files_to_archive").inner_text if doc.at("configuration/files_to_archive")

      if doc.at("configuration/api/api_key") 
        the_config.api_key = doc.at("configuration/api/api_key").inner_text
      end
            
      if doc.at("configuration/requirement_traceability/user_story_prefix_url") 
        the_config.user_story_prefix_url = doc.at("configuration/requirement_traceability/user_story_prefix_url").inner_text
      end
            
      the_config.show_test_scripts_to_public = false
      if doc.at("user_interface/show_test_scripts_to_public")
         the_config.show_test_scripts_to_public = doc.at("user_interface/show_test_scripts_to_public").inner_text == "true"
      end
      if doc.at("user_interface/highlight_test_output")
        the_config.highlight_test_output = doc.at("user_interface/highlight_test_output").inner_text == "true"
      end
       
      parallel_attribute = doc.root.get_attribute('parallel')
      if parallel_attribute  # new version
        the_config.is_parallel = parallel_attribute.to_s == "true"
      else
        the_config.is_parallel = (!the_config.app_name.blank? && !the_config.agent_work_dir.blank?)        
      end            
            
      load_testing_attribute = doc.root.get_attribute('load_testing')
      the_config.is_load_testing =  the_config.is_parallel && load_testing_attribute.to_s == "true"

      performance_testing_attribute = doc.root.get_attribute('performance_testing')
      the_config.is_performance_testing = performance_testing_attribute.to_s == "true"
            
      begin
        the_config.enable_dynamic_ordering = doc.at("configuration/sequential").get_attribute('enable_dynamic_ordering').to_s == "true"






        the_config.target_browser = doc.at("configuration/sequential/target_browser").inner_text
      rescue => e

      end
      
      if doc.at("configuration/parallel/load_testing/success_criteria/load_criteria_error_rate")
        the_config.load_criteria_error_rate = doc.at("configuration/parallel/load_testing/success_criteria/load_criteria_error_rate").inner_text.to_f
      end
      
      
      (doc/:configuration/:parallel/:load_testing/:success_criteria/:load_operation_criteria/:entry).each do |item|
        op_name        = item["operation"]
        avg_timing     = item['average_time']
        slowest_timing = item['slowest_time']
        is_active = item['is_active'].to_s == "true" rescue false
        the_config.load_operation_criteria[op_name] = {average_time: avg_timing, slowest_time: slowest_timing, is_active: is_active.to_s}
      end
      
      if doc.at("configuration/deploy/rake_task")
        the_config.deploy_rake_task = doc.at("configuration/deploy/rake_task").inner_text.strip rescue nil
      end
      









      return the_config
    end

    def self.get(project_id)

      project_config_file = nil

      the_project = Project.retrieve_by_identifier(project_id).first
      buildwise_root_dir = nil
      if the_project && the_project.working_dir && File.exist?(the_project.working_dir)
        buildwise_root_dir = File.expand_path( File.join(the_project.working_dir, "..", ".."))
        if File.exist?( File.join(buildwise_root_dir, "config", "#{project_id.downcase}.xml") )
          project_config_file =  File.join(buildwise_root_dir, "config", "#{project_id.downcase}.xml")
        end
      end
        
      project_config_file ||= File.join(BUILDWISE_HOME, "config", "#{project_id.downcase}.xml")
      self.load_from(project_config_file)
    end

    def update_attributes(hash)
      
      hash.each do |key, value|

        if self.respond_to?("#{key}=")
          if value.class == String
            eval("self.#{key} = '#{value}'")
          else
            if key.to_s == "environment_variables"
              @environment_variables = value
            else
              puts "[WARN] invalid #{key}, can't update attribute #{key}"
            end
          end

        end
      end
    end

    def reset

      @scm_type = @scm_url = @scm_user = @scm_password = @scm_branch = nil


      @builder_prepare_command  = nil
      @builder_cleanup_command  = nil

      @publisher_mail_delivery_method = @publisher_mail_authentication = @publisher_mail_address = @publisher_mail_port = @publisher_mail_user = @publisher_mail_password = @publisher_mail_recipients = nil

      @publisher_storywise_url = @publisher_storywise_release = nil

      @target_browser = "chrome"
      @environment_variables = { }
      @scheduler_polling = false
      @scheduler_at_time = []

      @ui_tests_dir = @ui_tests_report_dir = @ui_tests_local_working_dir = nil

      @_publishers = []
      @build_steps = []
      
      @deploy_rake_task = nil
    end


    def refresh(hash)
      reset();
      update_attributes(hash)
      
      @publisher_tplink_active = @publisher_tplink_active.to_s == "true" rescue false
      @publisher_slack_active  = @publisher_slack_active.to_s == "true" rescue false
      @publisher_mail_active   = @publisher_mail_active.to_s == "true" rescue false
      @publisher_wemo_active   = @publisher_wemo_active.to_s == "true" rescue false
      @publisher_http_callback_active   = @publisher_http_callback_active.to_s == "true" rescue false
    end












    
    def ui_test_step
      build_steps.select{|x| x.name == "UI Test"}.first
    end
    
    def load_test_step
      build_steps.select{|x| x.name == "Load Test"}.first
    end
    

    def publishers
      if @_publishers && !@_publishers.empty?
        return @_publishers
      end

      @_publishers = []
      if @publisher_mail_active && is_not_empty?(@publisher_mail_recipients)
        mail_publisher = BuildWise::Publisher::Mail.new
        mail_publisher.active = @publisher_mail_active
        mail_publisher.recipients = @publisher_mail_recipients
        mail_publisher.delivery_method = @publisher_mail_delivery_method.to_sym if @publisher_mail_delivery_method
        mail_publisher.address = @publisher_mail_address
        mail_publisher.port = @publisher_mail_port
        mail_publisher.domain = @publisher_domain
        mail_publisher.user_name = @publisher_mail_user
        mail_publisher.password = @publisher_mail_password
        mail_publisher.authentication = @publisher_mail_authentication
        mail_publisher.configure
        @_publishers << mail_publisher
      end

      if @publisher_storywise_active && is_not_empty?(@publisher_storywise_url)
        storywise_publisher = BuildWise::Publisher::StoryWise.new
        storywise_publisher.active = @publisher_storywise_active
        storywise_publisher.url = @publisher_storywise_url
        storywise_publisher.release = @publisher_storywise_release

        @_publishers << storywise_publisher
      end

      if @publisher_x10_active
        x10_publisher = BuildWise::Publisher::X10.new
        x10_publisher.active = @publisher_x10_active
        x10_publisher.command = @publisher_x10_command

        @_publishers << x10_publisher
      end

      if @publisher_wemo_active
        wemo_publisher = BuildWise::Publisher::Wemo.new
        wemo_publisher.active = @publisher_wemo_active
        wemo_publisher.success_wemo = "http://#{@publisher_wemo_success_ip}:49153/"
        wemo_publisher.failure_wemo = "http://#{@publisher_wemo_failure_ip}:49153/"
        @_publishers << wemo_publisher
      end

      if @publisher_slack_active
        slack_publisher = BuildWise::Publisher::Slack.new(:webhook_url => @publisher_slack_webhook_url, 
        :channel => @publisher_slack_channel, :username =>  @publisher_slack_username)
        slack_publisher.active = true
        @_publishers << slack_publisher
      end

      if @publisher_tplink_active
        tplink_publisher = BuildWise::Publisher::Tplink.new
        tplink_publisher.active = @publisher_tplink_active
        tplink_publisher.success_device = @publisher_tplink_success_device_name
        tplink_publisher.failure_device = @publisher_tplink_failure_device_name
        @_publishers << tplink_publisher
      end

      if @publisher_http_callback_active
        callback_publisher = BuildWise::Publisher::HttpCallback.new(:webhook_url => @publisher_http_callback_webhook_url)
        callback_publisher.active = true
        @_publishers << callback_publisher
      end
      
      return @_publishers
    end


    def active_publishers
      publishers.select { |x| x.active }
    end

  end
end