require 'yaml'
require 'roll/attributes'

module Roll

  class Package
    include Attributes

    # Read package information from a YAML file.
    def self.open(file=nil, options={})
      unless file
        file = Dir.glob(filename, File::FNM_CASEFOLD).first
        raise "Manifest file is required." unless file
      end
      data = YAML::load(File.open(file))
      data.update(options)
      data.update(:file => file)
      new(data)
    end

    # Possible file name (was for Fileable).
    def self.filename
      '{,meta/}*.roll'
    end

    # New Package. Pass in a data hash to populate the object.
    # TODO Support self setter block?
    def initialize(data={}) #, &yld)
      data.each do |k,v|
        send( "#{k}=", v ) rescue nil
      end
      #if yld
      #  yld.to_h.each do |k,v|
      #    send( "#{k}=", v ) rescue nil
      #  end
      #end

      base = File.basename(file).chomp('.roll')
      if base.index('-')  # Just in case you want to load ir from a non-conforming file.
        name, version = base.split('-')
        name = name.downcase # TODO Is this too restrictive?
        @name = name
        @version = version
      end

      self
    end

    # Name of the package file (if used).
    attr_accessor :file

    # Path to the project file (if used).
    def location
      File.dirname(@file) if @file
    end

    # Indicates if this project information was read from a file.
    # Returns the file's name if so.
    def read? ; file ; end

    #---------------------#
    # GENERAL INFORMATION #
    #---------------------#

    # The title of the project (free-form, defaults to name).
    attr_accessor :title

    # One-line description of the package (Max 60 chars.)
    attr_accessor :subtitle, :brief

    # More detailed description of the package.
    attr_accessor :description, :summary

    # "Unix" name of the project.
    attr_accessor :name, :project

    # The date the project was started.
    attr_accessor :created

    # Copyright notice.
    attr_accessor :copyright

    # Distribution License.
    attr_accessor :license

    # Project slogan or "trademark" phrase.
    attr_accessor :slogan

    # General one-word software category.
    attr_accessor :category

    # Author(s) of this project.
    # (Usually in "name <email>" format.)
    attr_accessor :author

    # Contact(s) (defaults to authors).
    attr_accessor :contact

    # Gerenal Email address (defaults to first contact's address, if given).
    attr_accessor :email

    # Official domain associated with this package.
    attr_accessor :domain

    # Project's homepage.
    attr_accessor :homepage

    # Project's development site.
    attr_accessor :development, :devsite

    # Internet address(es) to documentation pages.
    attr_accessor :documentation, :docs

    # Internet address(es) to downloadable packages.
    attr_accessor :download

    # Internet address for project wiki.
    attr_accessor :wiki

    # Project's mailing list or other contact email.
    attr_accessor :list

    # Generate documentation on installation?
    attr_accessor :document

    def name        ; @name         || project        ; end
    def title       ; @title        || project        ; end
    def contact     ; @contact      || author         ; end
    def email       ; @email        || contact        ; end
    def license     ; @license      || 'Ruby'         ; end

    # Subtitle is limited to 60 characters.
    def subtitle ; @subtitle.to_s[0..59] ; end

    # Returns a standard taguri id for the library and release.
    def project_taguri
      "tag:#{name}.#{domain},#{created}"  # or released?
    end

    # Version Information

    module Version
      include Attributes

      # Version number (eg. '1.0.0').
      attr_accessor :version

      # Status of this release: alpha, beta, RC1, etc.
      attr_accessor :status

      # Date of release (defaults to Time.now).
      attr_accessor :released

      # Build number (if true, defaults to a number based on current date-time).
      attr_accessor :buildno

      # Current release code name.
      attr_accessor :codename

      # If buildno is set to true, than returns a time stamp.
      def buildno
        bn = stamp.buildno if stamp
        unless bn
          if TrueClass === @buildno
            bn = Time.now.strftime("%H*60+%M")
          else
            bn = @buildno
          end
        end
        return bn
      end
    end

    include Version

    # Content Descriptions

    module Contents
      include Attributes

      # Files in this package that are executables.
      attr_accessor :executable, :executables

      # Library files in this package that are *public*.
      # This is akin to load_path but specifies specific files
      # that can be loaded from the outside --where as those
      # not listed are considerd *private*.
      #
      # NOTE: This is not enforced --and may never be. It
      # complicates library loading. Ie. how to distinguish public
      # loading from external loading. But it something that can be
      # consider more carfully in the future. For now it can serve 
      # as an optional reference.
      attr_accessor :library, :libraries

      # Location(s) of executables.
      attr_accessor :bin_path, :bin_paths

      # Root location(s) of libraries (used by Rolls).
      # If you plan to support Gems, this would be something like:
      #
      #   'lib/facets'
      #
      # If not, then the default ('lib') is nice b/c it means one less
      # layer in your project heirarchy.
      attr_accessor :lib_path, :lib_paths, :load_path, :load_paths

      # Traditional load path (used by RubyGems).
      # The default is 'lib', which is usually fine.
      attr_accessor :gem_path, :gem_paths

      # Default lib to load when requiring only on a package name. Eg.
      #
      #   require 'facets'
      #
      attr_accessor :index_library

      # Root location(s) of libraries.
      #--
      # TODO This is an intersting idea. Instead of fixed locations in 
      # the file system one could register "virtual locations" which map
      # to real locations. Worth the added flexability?
      #++
      #attr_accessor :register

      def executable ; [@executable || 'bin/**/*'].flatten ; end
      def library    ; [@library    || 'lib/**/*'].flatten ; end

      def gem_path   ; [@gem_path   || 'lib'].flatten ; end
      def lib_path   ; [@lib_path   || 'lib'].flatten ; end

      def index_library ; @index_library || 'index.rb' ; end

      #def register    ; @register    || { name => 'lib' } ; end
    end

    include Contents

    # Security Information

    module Security
      include Attributes

      # Encryption digest type used.
      #   (md5, sha1, sha128, sha256, sha512).
      attr_accessor :digest

      # Public key file associated with this library. This is useful
      # for security purposes especially remote loading. [pubkey.pem]
      attr_accessor :public_key

      # Private key file associated with this library. This is useful
      # for security purposes especially remote loading. [_privkey.pem]
      attr_accessor :private_key

      def digest      ; @digest       || 'md5'          ; end
      def public_key  ; @public_key   || 'pubkey.pem'   ; end
      def private_key ; @private_key  || '_privkey.pem' ; end
    end

    include Security

    # Source Control Managment Information

    module Control
      include Attributes

      # Specifices the type of revision control system used.
      #   darcs, svn, cvs, etc.
      attr_accessor :scm

      # Files that are tracked under revision control.
      # Default is all less standard exceptions.
      # '+' and '-' prefixes can be used to augment the list
      # rather than fully override it.
      attr_accessor :track, :scm_files

      # Internet address to source code repository.
      # (http://, ftp://, etc.)
      attr_accessor :repository, :repo

      # Changelog file.
      attr_accessor :changelog
    end

    include Control

    # Validation

    validate "name is required" do
      name
    end

    validate "version is required" do
      version
    end

  end

end