// imapd/FetchCmd.hh
// This file is part of Decimail; see http://decimail.org
// (C) 2004 Philip Endecott
 
 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
#ifndef Imapd_FetchCmd_hh
#define Imapd_FetchCmd_hh

#include "Command.hh"

#include <set>
using namespace std;


namespace Imapd {

  class FetchAtt {
  private:
    const string name;
  public:
    FetchAtt(string n): name(n) {}
    virtual ~FetchAtt() {}
    virtual string get_name(void) const { return name; }
    virtual void extract(Session& session, const Message& msg, ostream& s) = 0;
  };

  class FetchEnvelope: public FetchAtt {
  public:
    FetchEnvelope(void): FetchAtt("ENVELOPE") {}
    void extract(Session& session, const Message& msg, ostream& s);
  };

  class FetchFlags: public FetchAtt {
  public:
    FetchFlags(void): FetchAtt("FLAGS") {}
    void extract(Session& session, const Message& msg, ostream& s);
  };

  class FetchInternaldate: public FetchAtt {
  public:
    FetchInternaldate(void): FetchAtt("INTERNALDATE") {}
    void extract(Session& session, const Message& msg, ostream& s);
  };

  class FetchBodySection: public FetchAtt {
  public:
    void extract(Session& session, const Message& msg, ostream& s);

    class Range {
    public:
      virtual string get_name(void) const = 0;
      virtual string truncate(string s) const = 0;
      virtual ~Range() {}
    };

    class UnlimitedRange: public Range {
    public:
      string get_name(void) const { return ""; }
      string truncate(string s) const { return s; }
    };

    class LimitedRange: public Range {
    private:
      int start;
      int maxlen;
    public:
      LimitedRange(int s, int ml): start(s), maxlen(ml) {}
      string get_name(void) const;
      string truncate(string s) const { return s.substr(start,maxlen); }
    };
    
    class Section {
    public:
      virtual string get_name(void) const = 0;
      virtual ~Section() {}
      virtual void extract(Session& session, const Message& msg,
			   ostream& s, const Range& r) const;
      virtual void extract(Session& session, const mimetic::MimeEntity& me,
			   ostream& s, const Range& r) const = 0;
    };

    class SectionHeader: public Section {
    public:
      string get_name(void) const { return "HEADER"; };
      void extract(Session& session, const Message& msg,
		   ostream& s, const Range& r) const;
      void extract(Session& session, const mimetic::MimeEntity& me,
		   ostream& s, const Range& r) const;
      void extract(Session& session, const mimetic::Header& me,
		   ostream& s, const Range& r) const;
    };

    class SectionHeaderFields: public Section {
    private:
      bool inverse;
      set<ci_string> headers;
    public:
      SectionHeaderFields(bool i, set<ci_string> h): inverse(i), headers(h) {}
      string get_name(void) const;
      void extract(Session& session, const Message& msg, ostream& s,
		   const Range& r) const;
      void extract(Session& session, const mimetic::MimeEntity& me, ostream& s,
		   const Range& r) const;
      void extract(Session& session, const mimetic::Header& me, ostream& s,
		   const Range& r) const;
    };

    class SectionText: public Section {
    public:
      string get_name(void) const { return "TEXT"; }
      void extract(Session& session, const Message& msg,
		   ostream& s, const Range& r) const;
      void extract(Session& session, const mimetic::MimeEntity& me,
		   ostream& s, const Range& r) const;
    };

    class SectionMime: public Section {
    public:
      string get_name(void) const { return "MIME"; }
      void extract(Session& session, const mimetic::MimeEntity& me,
		   ostream& s, const Range& r) const;
    };

    class SectionNumbered: public Section {
    private:
      int num;
      Section* subsection;
    public:
      SectionNumbered(int n): num(n), subsection(NULL) {}
      SectionNumbered(int n, Section* ss): num(n), subsection(ss) {}
      ~SectionNumbered() { if (subsection) { delete subsection; } }
      string get_name(void) const;
      void extract(Session& session, const mimetic::MimeEntity& me,
		   ostream& s, const Range& r) const ;
    };

  private:
    const bool peek;
    const Section* section;
    const Range* range;

  public:
    FetchBodySection(string n, bool p, Section* s, Range* r):
      FetchAtt(n), peek(p), section(s), range(r) {}
    FetchBodySection(bool p, Section* s, Range* r);
    virtual ~FetchBodySection();
  };

  class FetchRfc822: public FetchBodySection {
  public:
    FetchRfc822(void):
      FetchBodySection("RFC822",false,NULL,new UnlimitedRange()) {}
  };

  class FetchRfc822Header: public FetchBodySection {
  public:
    FetchRfc822Header(void):
      FetchBodySection("RFC822.HEADER",true,new SectionHeader(),
		       new UnlimitedRange()) {}
  };

  class FetchRfc822Size: public FetchAtt {
  public:
    FetchRfc822Size(void): FetchAtt("RFC822.SIZE") {}
    void extract(Session& session, const Message& msg, ostream& s);
  };

  class FetchRfc822Text: public FetchBodySection {
  public:
    FetchRfc822Text(void):
      FetchBodySection("RFC822.TEXT",false,new SectionText, new UnlimitedRange) {}
  };

  class FetchBodyMaybeStructure: public FetchAtt {
  private:
    bool extensions;
  public:
    FetchBodyMaybeStructure(string n, bool e): FetchAtt(n), extensions(e) {}
    void extract(Session& session, const Message& msg, ostream& s);
  };

  class FetchBody: public FetchBodyMaybeStructure {
  public:
    FetchBody(void): FetchBodyMaybeStructure("BODY",false) {}
  };

  class FetchBodystructure: public FetchBodyMaybeStructure {
  public:
    FetchBodystructure(void): FetchBodyMaybeStructure("BODYSTRUCTURE",true) {}
  };

  class FetchUid: public FetchAtt {
  public:
    FetchUid(void): FetchAtt("UID") {}
    void extract(Session& session, const Message& msg, ostream& s);
  };



  class FetchCmd: public MessageSetCmd {
  private:
    const list<FetchAtt*> fetch_atts;
  public:
    FetchCmd(string t, const MessageSet* ms, list<FetchAtt*> fa):
      MessageSetCmd(t,"FETCH",ms), fetch_atts(fa) {}
    ~FetchCmd();
  private:
    void runbody(Session& session);
  };

}

#endif
