require "genZPK/version"
require 'genZPK/templates'
require 'tempfile'
require 'nokogiri'
require 'highline/import'


# Defines a not_nil? method for all objects
# @return [Boolean] the truth value associated with the nil state
class Object
  def not_nil?
    !nil?
  end
end


module GenZPK
  # Returns the path to the zdpack executable if it can find it, or nil otherwise.
  # @return [String] The path to zdpack or nil if unable to find it
  def getZdpack
    if File.exists? '/usr/local/zend/bin/zdpack'
      '/usr/local/zend/bin/zdpack'
    else
      nil
    end
  end

  # Returns the path to the given executable if it can find it, or nil otherwise.
  # @return [String] The path to the executable or nil if unable to find it
  def executablePath (executable=String.new)
    # Assume the string is a path if it has a '/' in it
    if executable.include? '/'
      if File.exists? executable
        executable
      else
        nil
      end
    else
      # Assume the executable is in the PATH if a full path is not passed
      whichOut = `which #{executable}`.chomp!

      # Check to see if the output matches the error signifying that a path wasn't found.
      if whichOut =~ /\/usr\/bin\/which: no (.*) in \(.*\)/
        nil
      else
        # It found it! Woo hoo!
        if File.exists? whichOut
          whichOut
        else
          nil
        end
      end
    end
  end

  # Get the path to the system text editor specified in the EDITOR env. variable.
  # If not defined, default to nano and then vi.
  # @return [String] The path to the executable
  def getEditor
    # Check if EDITOR is not defined
    if ENV['EDITOR'].nil?
      # It's not start nano and return the path to it.
      # If you can't find it, try vi.
      ['nano', 'vi'].each do |x|
        editor = executablePath x

        if not editor.nil?
          return editor
        end
      end

      # Couldn't find vi  or nano. Raise an exception for the user to deal with.
      raise "Valid text editor could not be found. Please specify one in the EDITOR environment variable and/or check your PATH."
    # It is, so lets use it.
    else
      # Get the path to the specified editor
      editor = executablePath ENV['EDITOR']

      # If the path was found, return it.
      if not editor.nil?
        editor
      else
        # It wasn't found so cycle through nano and vi like above
        ['nano', 'vi'].each do |x|
          editor = executablePath x

          if not editor.nil?
            return editor
          end
        end

        # Still couldn't find an editor. Raise an exception.
        raise "Valid text editor could not be found. Please specify one in the EDITOR environment variable and/or check your PATH."
      end
    end
  end

  def yesno(prompt = 'Continue?', default = true)
    a = ''
    s = default ? '[Y/n]' : '[y/N]'
    d = default ? 'y' : 'n'
    until %w[y n].include? a
      a = ask("#{prompt} #{s} ") { |q| q.limit = 1; q.case = :downcase }
      a = d if a.length == 0
    end
    a == 'y'
  end

  # Requires that some condition be true or otherwise display the given message and exit.
  # @param [Boolean] condition the truth value of the condition
  # @param [String] the message to display on failure of the condition
  # @example Require a condition
  #   requireCondition File.exists?("foo.bar"), "The file \"foo.bar\" doesn't exist. Exiting....
  def requireCondition(condition, message)
    if not condition
      puts message
      exit -1
    end
  end

  # Configures genZPK checks, definitions, and subdomain checks bia a user config file.
  # If the necessary files and paths do not exist, doConfigure will create them.
  # If the config files do exist, then a their contents will be loaded to a temporary file
  # for the user to edit. All configurations will be schema checked and the user must correct any
  # issues for the changes to be saved.
  def doConfigure
    # Check if the .genZPK folder exists in the user's home directory and create it if it doesn't.
    if not File.exists?(File.expand_path '~/.genZPK')
      FileUtils.mkpath(File.expand_path '~/.genZPK')
    end

    # Check if the .genZPK/checks.xml file exists and create it if it doesn't.
    if not File.exists?(File.expand_path '~/.genZPK/checks.xml')
      File.new(File.expand_path('~/.genZPK/checks.xml'), 'w')
    end

    # Open the checks file for reading
    checksFile = File.open(File.expand_path('~/.genZPK/checks.xml'), 'r')

    # Open a new temporary file. By default, it is writable.
    tmpChecksFile = Tempfile.new 'checks'

    # Read the contents of the existing check file and write them to the temporary file.
    while buff = checksFile.read(4096)
      tmpChecksFile.write(buff)
    end

    # Rewind the temporary file to the begining and close the existing file.
    tmpChecksFile.rewind
    checksFile.close

    # Get the system editor and allow the user to modify the temporary checksa file.
    editor = getEditor
    system("#{editor} #{tmpChecksFile.path}")

    # This block rescues the method in case the user enters bad XML
    begin
      # Read in the file and rewind it.
      content = tmpChecksFile.read
      tmpChecksFile.rewind

      # Parse the content with Nokogiri. A Nokogiri::XML::SyntaxError exception is raised in the event
      # of invalid XML syntax.
      doc = Nokogiri::XML(content) { |config| config.options = Nokogiri::XML::ParseOptions::STRICT }
    rescue Nokogiri::XML::SyntaxError => e
      # If the user wants to try to correct the file
      if yesno("Invalid XML entered. Would you like to reopen the file?")
        # Loop until they fix it
        loop do
          # Allow them to edit the file
          system("#{editor} #{tmpChecksFile.path}")

          begin
            # read in the content of the file and rewind
            content = tmpChecksFile.read
            tmpChecksFile.rewind

            # Check the xml syntax
            doc = Nokogiri::XML(content) { |config| config.options = Nokogiri::XML::ParseOptions::STRICT }

            # If it got here, the syntax was good and exit the oop
            break
          rescue
            # If the user doesn't want to try again, exit without saving the changes
            if not yesno("Invalid XML entered. Would you like to reopen the file?")
              exit -1
            end
          end
        end
      else
        # The user didn't want to fix the file. Exit.
        exit -1
      end
    end


    # Reopen the checks config file for writing
    checksFile = File.open(File.expand_path('~/.genZPK/checks.xml'), 'w+')

    # write the contents of the temporary file to the checks file
    while buff = tmpChecksFile.read(4096)
      checksFile.write(buff)
    end

    # Close all files and delete the temporary file.
    checksFile.close
    tmpChecksFile.close
    tmpChecksFile.unlink
  end
end