# encoding: UTF-8
module Axlsx

  # The Storage class represents a storage object or stream in a compound file.
  class Storage

    # Packing for the Storage when pushing an array of items into a byte stream
    # Name, name length, type, color, left sibling, right sibling, child, classid, state, created, modified, sector, size
    PACKING = "s32 s1 c2 l3 x16 x4 q2 l q".freeze

    # storage types
    TYPES = {
      :root=>5,
      :stream=>2,
      :storage=>1
    }.freeze

    # Creates a byte string for this storage
    # @return [String] 
    def to_s
     data = [@name.concat(Array.new(32-@name.size, 0)), 
             @name_size, 
             @type, 
             @color, 
             @left, 
             @right, 
             @child, 
             @created,
             @modified, 
             @sector, 
             @size].flatten
      data.pack(PACKING)
    end

    # storage colors
    COLORS = {
      :red=>0, 
      :black=>1
    }

    # The color of this node in the directory tree. Defaults to black if not specified
    # @return [Integer] color
    attr_reader :color
    
    # Sets the color for this storage
    # @param [Integer] v Must be one of the COLORS constant hash values
    def color=(v)
      RestrictionValidator.validate :storage_color, COLORS.values, v      
      @color = v
    end

    # The size of the name for this node.
    # interesting to see that office actually uses 'R' for the root directory and lists the size as 2 bytes - thus is it *NOT* null 
    # terminated. I am making this r/w so that I can override the size
    # @return [Integer] color
    attr_reader :name_size

    # the name of the stream    
    attr_reader :name

    # sets the name of the stream.
    # This will automatically set the name_size attribute
    # @return [String] name
    def name=(v)
      @name = v.bytes.to_a << 0
      @name_size = @name.size * 2
      @name
    end

    # The size of the stream
    attr_reader :size

    # The stream associated with this storage
    attr_reader :data

    # Set the data associated with the stream. If the stream type is undefined, we automatically specify the storage as a stream type.    # with the exception of storages that are type root, all storages with data should be type stream.
    # @param [String] v The data for this storages stream
    # @return [Array]
    def data=(v)
      Axlsx::validate_string(v)
      self.type = TYPES[:stream] unless @type
      @size = v.size
      @data = v.bytes.to_a
    end

    # The starting sector for the stream. If this storage is not a stream, or the root node this is nil
    # @return [Integer] sector
    attr_accessor :sector

    # The 0 based index in the directoies chain for this the left sibling of this storage. 
    
    # @return [Integer] left
    attr_accessor :left 

    # The 0 based index in the directoies chain for this the right sibling of this storage. 
    # @return [Integer] right
    attr_accessor :right 

    # The 0 based index in the directoies chain for the child of this storage. 
    # @return [Integer] child
    attr_accessor :child

    # The created attribute for the storage
    # @return [Integer] created
    attr_accessor :created

    # The modified attribute for the storage
    # @return [Integer] modified
    attr_accessor :modified

    # The type of storage
    # see TYPES
    # @return [Integer] type
    attr_reader :type

    # Sets the type for this storage. 
    # @param [Integer] v the type to specify must be one of the TYPES constant hash values. 
    def type=(v)
      RestrictionValidator.validate :storage_type, TYPES.values, v      
      @type = v
    end

    # Creates a new storage object. 
    # @param [String] name the name of the storage
    # @option options [Integer] color @default black
    # @option options [Integer] type @default storage
    # @option options [String] data 
    # @option options [Integer] left @default -1
    # @option options [Integer] right @default -1
    # @option options [Integer] child @default -1
    # @option options [Integer] created @default 0
    # @option options [Integer] modified @default 0
    # @option options [Integer] sector @default 0
    def initialize(name, options= {})
      @left = @right = @child = -1
      @sector = @size = @created = @modified = 0
      options.each do |o|
        self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
      end
      @color ||= COLORS[:black]
      @type ||= (data.nil? ? TYPES[:storage] : TYPES[:stream])
      self.name = name
    end
 
  end
end
