// imapd/Command.cc
// 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.
 
#include "Command.hh"

#include "utils.hh"

#include <sstream>
#include <boost/algorithm/string/replace.hpp>

using namespace std;


namespace Imapd {

  static string get_str(const Exception& E)
  {
    stringstream strm;
    E.report(strm);
    string s = strm.str();
    boost::algorithm::replace_all(s,"\n"," ");
    return s;
  }

  RespondNo::RespondNo(const Exception& E):
    msg(get_str(E))
  {
  }

  void RespondNo::report(ostream& strm) const
  {
    strm << "IMAP NO response: " << msg << endl;
  }

  void RespondBad::report(ostream& strm) const
  {
    strm << "IMAP BAD response: " << msg << endl;
  }

  class NotAllowedInThisState: public RespondNo {
  public:
    NotAllowedInThisState(void):
      RespondNo("command not allowed in current state") {}
  };


  void Command::run(Session& session)
  {
    try {
      runbody(session);
    }
    catch(RespondBad& B) {
      session.respond_bad(tag,B.get_msg());
      session.flush();
      return;
    }
    catch(RespondNo& N) {
      session.respond_no(tag,N.get_msg());
      session.flush();
      return;
    }
    session.send_any_unilateral_updates(true);
    session.respond_ok(tag,response_code+name+" done");
    session.flush();
  }


  void MessageSetSingleton::select_seqnum(const Session& session,
					  list<int>& wanted_msgids) const
  {
    int msg_id = session.seqnum_to_msgid[msgnum];
    wanted_msgids.push_back(msg_id);
  }

  void MessageSetSingleton::select_uid(const Session& session,
				       list<int>& wanted_msgids) const
  {
    // need to check if this msgnum is in this mailbox ??
    wanted_msgids.push_back(msgnum);
  }


  void MessageSetRange::select_seqnum(const Session& session,
				      list<int>& wanted_msgids) const
  {
    const int last_i = (end==0)?(session.seqnum_to_msgid.size()-1):(end);
    for (int i=start; i<=last_i; ++i) {
      int msg_id = session.seqnum_to_msgid[i];
      wanted_msgids.push_back(msg_id);
    }
  }

  void MessageSetRange::select_uid(const Session& session,
				   list<int>& wanted_msgids) const
  {
    map<int,int>::const_iterator first_i =
      session.msgid_to_seqnum.lower_bound(start);

    map<int,int>::const_iterator last_i =
      (end==0) ? session.msgid_to_seqnum.end()
      : session.msgid_to_seqnum.upper_bound(end);

    for (map<int,int>::const_iterator i=first_i; i!=last_i; ++i) {
      wanted_msgids.push_back(i->first);
    }
  }


  void MessageSetSet::select_seqnum(const Session& session,
				    list<int>& wanted_msgids) const
  {
    set1->select_seqnum(session,wanted_msgids);
    set2->select_seqnum(session,wanted_msgids);
  }


  void MessageSetSet::select_uid(const Session& session,
				 list<int>& wanted_msgids) const
  {
    set1->select_uid(session,wanted_msgids);
    set2->select_uid(session,wanted_msgids);
  }


  void MessageSetCmd::get_msg_ids(const Session& session, list<int>& msg_ids)
  {
    // Find a list of message IDs (UIDs) for this command to work on.

    if (uid_mode) {
      msgset->select_uid(session,msg_ids);
    } else {
      msgset->select_seqnum(session,msg_ids);
    }
  }

  MessageSetCmd::~MessageSetCmd()
  {
    delete msgset;
  }

};
