require 'mime/message'

module Gmail
  class Message
    # Raised when given label doesn't exists.
    class NoLabelError < Exception; end 
  
    attr_reader :uid
    
    def initialize(mailbox, uid)
      @uid     = uid
      @mailbox = mailbox
      @gmail   = mailbox.instance_variable_get("@gmail") if mailbox
    end
    
    def uid
      @uid ||= @gmail.conn.uid_search(['HEADER', 'Message-ID', message_id])[0]
    end
    
    # Mark message with given flag.
    def flag(name)
      !!@gmail.mailbox(@mailbox.name) { @gmail.conn.uid_store(uid, "+FLAGS", [name]) }
    end
    
    # Unmark message. 
    def unflag(name)
      !!@gmail.mailbox(@mailbox.name) { @gmail.conn.uid_store(uid, "-FLAGS", [name]) }
    end
    
    # Do commonly used operations on message. 
    def mark(flag)
      case flag
        when :read    then read!
        when :unread  then unread!
        when :deleted then delete!
        when :spam    then spam!
      else
        flag(flag)
      end
    end
    
    # Mark this message as a spam.
    def spam!
      move_to('[Gmail]/Spam')
    end
    
    # Mark as read.
    def read!
      flag(:Seen)
    end
    
    # Mark as unread.
    def unread!
      unflag(:Seen)
    end
    
    # Mark message with star.
    def star!
      flag('[Gmail]/Starred')
    end
    
    # Remove message from list of starred.
    def unstar!
      unflag('[Gmail]/Starred')
    end
    
    # Move to trash.
    def delete!
      @mailbox.messages.delete(uid)
      flag(:Deleted)
    end

    # Archive this message.
    def archive!
      move_to('[Gmail]/All Mail')
    end
    
    # Move to given box and delete from others.  
    def move_to(name, from=nil)
      label(name, from) && delete!
    end
    alias :move :move_to
    
    # Move message to given and delete from others. When given mailbox doesn't 
    # exist then it will be automaticaly created. 
    def move_to!(name, from=nil)
      label!(name, from) && delete!
    end
    alias :move! :move_to!
    
    # Mark this message with given label. When given label doesn't exist then
    # it will raise <tt>NoLabelError</tt>. 
    #
    # See also <tt>Gmail::Message#label!</tt>.
    def label(name, from=nil)
      @gmail.mailbox(from || @mailbox.name) { @gmail.conn.uid_copy(uid, name) }
    rescue Net::IMAP::NoResponseError
      raise NoLabelError, "Label '#{name}' doesn't exist!"
    end
    alias :add_label :label

    # Mark this message with given label. When given label doesn't exist then
    # it will be automaticaly created. 
    #
    # See also <tt>Gmail::Message#label</tt>.
    def label!(name, from=nil)
      label(name, from) 
    rescue NoLabelError
      @gmail.labels.add(name)
      label!(name, from)
    end
    alias :add_label! :add_label
    
    # Remove given label from this message. 
    def remove_label!(name)
      move_to('[Gmail]/All Mail', name)
    end
    alias :delete_label! :remove_label!
    
    def inspect
      "#<Gmail::Message#{'0x%04x' % (object_id << 1)} mailbox=#{@mailbox.name}#{' uid='+@uid.to_s if @uid}#{' message_id='+@message_id.to_s if @message_id}>"
    end
    
    def method_missing(meth, *args, &block)
      # Delegate rest directly to the message.
      if envelope.respond_to?(meth)
        envelope.send(meth, *args, &block)
      elsif message.respond_to?(meth)
        message.send(meth, *args, &block)
      else
        super(meth, *args, &block)
      end
    end
    
    def envelope
      @envelope ||= @gmail.mailbox(@mailbox.name) {
        @gmail.conn.uid_fetch(uid, "ENVELOPE")[0].attr["ENVELOPE"]
      }
    end

    private
    
    def message
      @message ||= Mail.new(@gmail.mailbox(@mailbox.name) { 
        @gmail.conn.uid_fetch(uid, "RFC822")[0].attr["RFC822"] # RFC822
      })
    end
  end # Message
end # Gmail