require 'commands/start'
require 'commands/init/init_model'
require 'commands/init/changelist_model'
require 'commands/init/depot_model'
require 'commands/init/file_definition'
require 'commands/init/group_model'
require 'commands/init/system_settings_model'
require 'commands/init/trigger_model'
require 'commands/init/user_model'

module Commands

  def Commands.init(options=nil)
    init_dir = 'p4init'

    p4port = '127.0.0.1:1666'
    version = nil
    auto = false

    if options and !options.params.empty?

      op = OptionParser.new do |op|
        op.on('-p PORT', '--port PORT', 'P4PORT setting') { |x| p4port = x }
        op.on('-a', '--auto', 'Force charset to be auto') { |x| auto = true }
        op.on('-v VERSION', '--version VERSION', 'ftp.perforce.com version') do |v|
          version = v
        end
      end

      op.parse!(options.params)

      if !options.params.empty?
        init_dir = options.params.first
      end
    end

    if options and !options.params.empty?
      init_dir = options.params.shift
    end

    initialize_p4d(init_dir, p4port, auto)
  end

  def Commands.print_init_help
    puts <<-END.gsub(/^ {6}/, '')
      p4util init [-p P4PORT] [-a] [p4_init_dir]

      Reads definitions in the directory p4_init_dir - by default, just
      'p4init' - and then calls methods on a p4d instance assumed to be running
      locally.

      This assumes we are basically starting from scratch. If you have an
      existing p4d instance, it's highly likely your initialization run will
      fail. Delete your settings and working area and reset.

      Options:

        -p P4PORT : Specify p4 port setting, defaults to ':1666' (uses localhost)
        -a        : Force our 'charset' to be 'auto'

      ## Definition Files ##

      Files are Ruby scripts, each basically extending a model class defined
      by this application.

      Each model type is defined separately later, however, you should have
      at least one User model that's marked as a super user. If you don't do
      this, you will always have a super user created with the login 'p4super'
      and the password 'superuser1A!'.

      ## Execution Order ##

      Each model you define has a 'rank'. Models classes generate instances, and
      each instance is sorted based on this rank. If you specify no rank, or
      any rank is equivalent, well, you submit your will to the gods of random.

      The only special model type that does not obey these rules is the
      SystemSettings model, which is always handled in a very particular order.


      ## SystemSettingsModel ##

      Example:

      class MySystemSettings < SystemSettingsModel
        # These are default settings
        @unicode = true
        @security_level = 0

        # By default this is empty, but here's an example of usage
        @configure_settings = {
          'dm.keys.hide' => '2'
        }
      end

      When `unicode` is enabled, this assumes that the `p4util init` command
      is run in the *same directory*  as `p4util start`.


      ## UserModel ##

      Example of a super user (you need one):

      class SuperUser < UserModel
        @super = true
        # if you don't set, we'll just use this for the full_name and email
        @login = 'super'
        @password = 'superuser1A'
      end

      Example of a normal user:

      class JohnDoeUser < UserModel
        def rank; 100; end
        @login = 'jdoe'
        @full_name = 'John Doe'
        @email = 'jdoe@example.com'
        @password = 'johndoe1A!'
      end

      Note that with the super user, you don't really need a rank, but with
      your other models, it's a good idea. (You can mix when users come and go
      with other changes.)


      ## ChangelistModel ##

      Example of a changelist with an add and edit:

      class Changelist2 < ChangelistModel
        def rank; 1001 end
        @description = 'An example add and edit'
        @user = 'jdoe'
        @edits = [
            FileDefinition.new(:path => 'depot/README.txt',
                           :content => <<-STOP.gsub(/^ {8}/, '')
              This is an example readme.
              Added a second line
            STOP
            )
          ]
          @adds = [
              FileDefinition.new(:path => 'depot/main/project2/example.txt',
                                 :local_path => 'p4init/some_text.txt')
          ]
      end

      Note that adds an edits are specified with 'FileDefinition' objects. Each
      file definition instance can define text content inline, or via a
      'local_path' to a file relative to the current working directory.

      The `@user` is not necessary, but you probably don't want to add everything
      as your super user, so set this to a UserModel instance that should exist
      at this point.

      ## DepotModel ##

      Example creating a new standard depot:

        class ProjectDepot < DepotModel
          def rank
            1001
          end
          @depot = 'my_project'
          @description = 'The My Project depot'
        end

      Other attributes that can be set that update the depot spec being written:

        :type, :address, :suffix, :map, :spec_map

      ## GroupModel ##

      Example:

        class SystemGroup < GroupModel
          def rank; 2000 end
          @group = 'system'
          @timeout = 'unset'
          @password_timeout = 'unset'
          @users = ['app']
        end

      Attributes:

          :group, :max_results, :max_scan_rows,
          :max_lock_time, :password_timeout, :timeout,
          :users, :subgroups, :owners

      ## TriggerModel ##

      Example:

        class ExampleTriggers < TriggerModel
          def rank; 50; end

          @triggers = [
            'trig1 change-submit //depot/dir/... "/usr/bin/s1.pl %changelist%"',
            'trig2 change-submit -//depot/z/... "/usr/bin/s1.pl %user%"'
          ]
        end
    END
  end

  private

  def Commands.initialize_p4d(init_dir, p4port, auto)
    # Go through our init_dir, and evaluate each script as if it were defined
    # in the Commands::Init module.
    if File.directory?(init_dir)
      Dir.glob("#{init_dir}/**/*.rb") do |file|
        contents = IO.read(file)
        Commands::Init.class_eval(contents, file)
      end
    elsif File.file?(init_dir)
      contents = IO.read(init_dir)
      Commands::Init.class_eval(contents, init_dir)
    end

    # Note that nothing is actually done until this line. This allows classes
    # to re-define methods and do fancy shit, like, 'oh in security_settings 0
    # this guy actually doesn't have a password'.
    Commands::Init::InitModel.run(p4port, auto)
  end
end