import MessageException;
import FolderException;
import LoginException;
import NetworkConnection;
import SimpleOutput;
import MessageHeader;
import MailMessage;

/**
 * A protocal for Java to interact with an IMAP server
 *
 * @author Ben Kaiser
 * @author Josh Vickery
 * @version 2.0 of October 1999
 *
 * @modified from version 1.1 to become a class instead of an interface
 * @modified the interface was renamed MailboxInterface.java
 * @modified actual implimentation
 */

public class Mailbox 
    implements MailboxInterface {

    //+--------+
    //| Fields |
    //+--------+
    
    /** 
     * Since the IMAP server requires a unique number before each command we
     * must create a variable to keep track of that number and update that 
     * number each time we call a command.
     */
    int count = 0;
 
    /**
     * Create a new Socket object
     */
    java.net.Socket sock;
    
    /**
     * Create a new data input stream to read from the server
     */
    java.io.BufferedReader dis;
    
    /**
     * Create a new stream printer to print to the server
     */
    java.io.PrintWriter ps; 
    
    /**
     * an integer to store the number of messages in the current folder
     */
    int numMessages;

    /**
     * Prepare for MessageHeader return
     * Since the MessageHeader class is not implimented use, strngs for now
     */
    String[] headers;
    
    /**
     * Prepare for MessageReturn
     */

    /**
     * Create the mailbox (initializing all the damn fields), connect to the 
     * default server and port, and read the servers greating
     * @exception when we cannot establish a succesful connection to the server
     */
    public Mailbox() 
       throws ConnectionException
    {
           try { 
	       // open the socket
        sock = new java.net.Socket("pioneerserver.grinnell.edu", 143);
           } // try
           catch (Exception e1) {
               throw new ConnectionException("server unreachable");
	   } //catch
           try {
	       // prepare for input
               dis = 
                   new 
                   java.io.BufferedReader(new java.io.InputStreamReader(sock.getInputStream()));
           } //try
           catch (Exception e2) {
               throw new ConnectionException("unable to establish connection");
           } //catch
           try {
	       // prepare for output
        ps = new java.io.PrintWriter(sock.getOutputStream(), true);
           } //try
           catch (Exception e3) {
               throw new ConnectionException("unable to connect");
           } //catch
	   try {
	       // read the greating from the IMAP server
	       dis.readLine();
	   } //try
	   catch (Exception ex) {}
	   
    } // Mailbox()
    
    /**
     * Attempt to login to the server
     * This will attempt to create a NetworkConnection using the 
     * NetworkConnection class, with a server name and port deterimined by
     * the preferances class.  If successful it will attemp to login using
     * the given name and passwd.  It will then set the folder to inbox
     * @exception LoginExcpetion
     * when it is a bad UserName Password combo
     */
    
    public void login(String username, String passwd)
	throws LoginException, Exception {
	// set the count
	count = count + 1;
	SimpleOutput out = new SimpleOutput();
	// login with the given username and pass
	ps.print("a" + count + " LOGIN " + username + " " + passwd);
	ps.println('\r');
	//ps.print('\n');
        
	// check to see if the login worked
	try {
	    this.clear();
	    String store;
	    store = dis.readLine();
	    out.println(store);
	if (!(store.equals("a" + count + " OK LOGIN completed."))) {
	    throw new LoginException("badlogin");
        } // if badlogin
        } //try
        catch (Exception e4) {throw new Exception("IOException");}
	// if login is succesful set the folder to inbox
	try {
	    this.setFolder("Inbox");
	}
	catch (Exception fe) {/* this will not fail since Inbox must exist */ }
    } // Login(String, String)
    
    /**
     *closes the imap socket.
     *Pre:  there is an IMAP socket open.
     */
    public void bye() {
	SimpleOutput out = new SimpleOutput();
	// logout
        ps.print("bye" + " LOGOUT");
	ps.println('\r');
	//ps.print('\n');
	try {
	    out.println(dis.readLine());
	    out.println(dis.readLine());
	}
	catch(Exception x) {}
    } //bye()
   
    
    
    // +=================+
    // | Folder Commands |
    // +=================+

     

    /** list the folders in the account, in the form of an array.  
     * 
     *  pre:  we are currently logged in to an imap server.
     *  
     *  returns:  an array of strings, which are the folder names of the 
     *  account currently logged in to teh server.
     */
    
    public String listFolders() {    
    count = count + 1;
    SimpleOutput out = new SimpleOutput();
    // list the folders
    ps.print("a" + count + " list \"\" *");
    ps.println('\r');
    //ps.print('\n');
    try {
	// set the mark at the beggining of the first line
	dis.mark(10);
    } // try
    catch (Exception ex7) {}
    String list;
    list = null;
    boolean test;
    test = false;
    try {
	// while the first character is not "a" (97 ascii code)
	while (!test) {
		if (dis.read() == 97) {
		    test = true;
		    } // if
		else {
		    // reset to the beggining of the line
		dis.reset();
		// add the next line to the list
		list = list + '\n' + " " + dis.readLine();
		try {
		    // set the mark to the beggining of the next line
		    dis.mark(10);
		} // try
		catch (Exception ex2) {}
		} // else
	    } // while
    } // try
    catch (Exception ex4) {}
    try {
	dis.reset();
    } // try
    catch (Exception ex6) {}
    try {
	dis.readLine();
    } // try
    catch (Exception exc1) {}
    return list;
    } // listFolders()
    
    /** delete a specific folder and its contenets.  throws
     * FolderException if the given folder does not exist.
     * 
     *  pre:  we are currently logged in to an imap server.
     */
    
    public void deleteFolder(String folderName)
        throws  FolderException {
    count = count + 1;
    String store;
    SimpleOutput out = new SimpleOutput();
    // delete the folder of folderName
    ps.print("a" + count + " delete " + folderName);
    ps.println('\r');
    //ps.print('\n');
    try {
	this.clear();
	store = dis.readLine();
	out.println(store);
	// check if delete succeeded
        if (!(store.equals("a" + count + " OK DELETE completed."))) {
	throw new FolderException("badfolder");
        } // if folder does not exist
    }
    catch (Exception e6) {throw new FolderException();}
    } // deleteFolder(String)
    
    /**
     * create an empty folder with the specified name.  Throws folderException 
     * if a folder of that name cannot be created.
     * 
     * pre:  we are currently logged in to an imap server.
     */
    
    public void createFolder(String folderName)
        throws  FolderException {
	count = count + 1;
	String store;
	SimpleOutput out = new SimpleOutput();
	// create the folder
	ps.print("a" + count + " create " + folderName);
	ps.println('\r');
	//ps.print('\n');
        try {
	    this.clear();
	    store = dis.readLine();
	    out.println(store);
	    // chec to see if it worked
	    if (!(store.equals("a" + count + " OK CREATE completed."))) {
	    throw new FolderException("badfolder");
	} // if folder does not exist
        } // try
        catch (Exception e7) {throw new FolderException();}
    } // createFolder(String
    
    /** 
     * get the contents of the current  folder, returns an array of headers, 
     * pre:  we are currently logged into an IMAP server.  
     * returns an array of message headers
     *
     * @exception never, this method should not fail
     */
        
    public String[] getFolderContents() 
    throws Exception {
	SimpleOutput out = new SimpleOutput();
	String[] headers = new String[numMessages + 1];
	count = count + 1;
	// get the message headers
	ps.print("a" + count + " fetch " + "1:" + 
		 numMessages + " body[header]");
	ps.println('\r');
	//ps.print('\n');
	int store;
	store = 1;
	// while there are still messages
	while (store <= numMessages) {
            
	    
	    this.setHeader(store, headers);
	    
	    store = store + 1;
            
	} // while
	try {
	    this.clear();
	    dis.readLine();
	} // try
	catch (Exception x5) {}  
	// return the array of message headers
	return headers;
    } //getFolderContents(String)
    
    /**
     * renames a folder, throws FolderException when a folder cannot be created
     * with that name.  
     * 
     * Pre:  we are currently logged into an IMAP server.
     */ 
    public void renameFolder(String folderName, String newFolderName)
	throws FolderException
    {
	SimpleOutput out = new SimpleOutput();
	count = count + 1;
	// rename the folder
	ps.print("a" + count + " rename " + folderName + " " + newFolderName);
	ps.println('\r');
	//ps.print('\n');
	try {
	    this.clear();
	    String store;
	    store = dis.readLine();
	    out.println(store);
	    // check to see if it worked
        if (!(store.equals("a" + count + " OK RENAME completed."))) {
	    throw new FolderException("badfolder");
	} // if folder can not be created
        } // try
        catch (Exception e9) {throw new FolderException();}
    } // renameFolder(String, String)
    
    /**
     * Sets the active folder.  throws FolderException that folder 
     * doesn't exist, 
     * or is otherwise unavaliable.
     * 
     * Pre:  we are currently logged in to an IMAP server.  
     */
    public void setFolder(String folderName)
	throws FolderException, Exception
    {
	count = count + 1;
	SimpleOutput out = new SimpleOutput();
	// set the folder
	ps.print("a" + count + " select " + folderName);
	ps.println('\r');
	//ps.print('\n');
       	    // set the mark to the beggining of the first line
	    dis.mark(10);
	    // check to see if the value is a *
	    if (dis.read() == 42) {
		int store;
		// clear the next character
		dis.read();
		// store the number of messages
		store = dis.read();
		int n;
		n = dis.read();
		
		// if the next value is a space
		if (n == 32) {
		    // set the number of messages to the value
		numMessages = (store - 48);
		// reset the line to the beggining
		dis.reset();
		} //if
		else {
		// whule n is not a space
		    while (n != 32) {
			// multiple the value by 10 and add the next character
			store = ((store-48)*10 + (n-48));
			// set n to the next value
			n = dis.read();
		    } //while 
		    numMessages = store;
		    dis.reset();
	    } //else
		
	    } // if
	    
	    else {
		// otherwise, reset the line
		dis.reset();
		// check to see if the command failed
		this.clear();
		String line;
		line = dis.readLine();
		out.println(line);
	    if (!(line.equals("a" + 
			     count + 
			     " OK [READ-WRITE] SELECT completed."))) {
		out.println("bad");
		throw new FolderException("badfolder");
	    } // if folder does not exist
	    }// else
	    try {
		
		this.clear();
		out.println(dis.readLine());
		
	    } // try
	    catch (Exception exs) {}
	    count = count + 1;
	    ps.print("a" + count + " expunge");
	    ps.println('\r');
	    //ps.print('\n');
	    try {
		this.clear();
		out.println(dis.readLine());
	    } // try
	    catch (Exception exs2) {}
    } // setFolder(String)
    
    /**
                Message Commands
    */
    
    /** Deletes a message, throws MessageException if the message in question 
     * doesn't exist.
     * 
     * Pre:  we're logged in to an IMAP server, and have a valid folder set as 
     * active.
     */
    public void deleteMessage(int messageID)
	throws MessageException
    {
	SimpleOutput out = new SimpleOutput();
	count = count + 1;
	ps.print("a" + count + " store " + messageID + " +flags (\\Deleted)");
	ps.println('\r');
	//ps.print('\n');
	try {
	    this.clear();
	    String store;
	    store = dis.readLine();
	    out.println(store);
	    if (!(store.equals("a" + count + " OK STORE completed."))) {
		throw new MessageException("badmessage");
	    } // if invalid message name
	} // try
        catch (Exception e11) {throw new MessageException();}
	count = count + 1;
	ps.print("a" + count + " expunge");
	ps.println('\r');
	//ps.print('\n');
	
	try {
	    this.clear();
	    dis.readLine();
	}// try
	catch (Exception ex) {}
    } // deleteMessage(String)
    
    /** Get the contents of a message, returns a MailMessage, throws
     * MessageException when teh message in question doesn't exist, or is 
     * otherwise unabaliable.  
     
     * pre:  we are logged in to an IMAP server.
     */
    
    public String getMessage(int messageID, String[] headers)
	throws MessageException//, java.io.IOException
    {
	SimpleOutput out = new SimpleOutput();
	count = count + 1;
	
	ps.print("a" + count + " fetch " + messageID  + " body[text]");
	ps.println('\r');
	//ps.print('\n');
	try {
            dis.mark(10);
	} // try
	catch (Exception ex3) {}
	//try {
	String body;
	body = null;
	//body = dis.readLine();
	boolean test;
	test = false;
	try {
	while (!test) {
		if (dis.read() == 97) {
		    int temp;
                    temp = dis.read();
                    if ((temp >= 49) && (temp <= 57)) {
			test = true;
		    } // if
                    } // if 
                else {
		dis.reset();
                body = body + '\n' + " " + dis.readLine();
		try {
		    dis.mark(10);
                    } // try
                    catch (Exception ex2) {}
		} // else
	    } // while
	} // try
	catch (Exception ex4) {}
	try {
	    dis.reset();
	} // try
	catch (Exception rf) {}
	try {
	    
	    dis.readLine();
	} //try 
	catch (Exception ex6) {}
	int eoj = body.indexOf("{");
	body = body.substring(eoj);
	int eol = body.indexOf('\n');
	body = body.substring(eol);
	return body;
    } // getMessage(int)

    /**
     * copy a message to a specified folder
     * 
     * @exception when an invalid folder or message is given
     */
    
 public void copyMessage(int messageID, String folder)
	throws Exception
    {
	SimpleOutput out = new SimpleOutput();
	count = count + 1;
	ps.print("a" + count + " copy " + messageID + " " + folder);
	ps.println('\r');
	//ps.print('\n');
	try {
	    this.clear();
	    String store;
	    store = dis.readLine();
	    out.println(store);
	if (!(store.equals("OK COPY complete."))) {
	    throw new Exception("badmessage");
	} // if invalid message name
        } // try
        catch (Exception e11) {throw new Exception();}
	/*try {
	    this.clear();
	}// try
	catch (Exception ex) {} */
    } // deleteMessage(String)
    

    /**
     * ignore all responses that are not responses to the current command
     * @exception when mark, or readLine fails
     */
    public void clear() 
	throws Exception {
	SimpleOutput out = new SimpleOutput();
	dis.mark(10);
	int count;
	count = 0;
        int n;
	n = dis.read();
	while (n != 97) {
	    dis.reset();
	    dis.readLine();
	    dis.mark(10);
	    n = dis.read();
	    count = count + 1;
	    if (count >= 100) {throw new Exception("bad"); {} }
	} // while
	dis.reset();
	//out.println(dis.readLine());
    } // clear()

    /**
     * seach through a header for a specific field, and set that to the
     * String array
     */

    public void setHeader(int num, 
			  String[] headers) 
    throws Exception {
	SimpleOutput out = new SimpleOutput();
	dis.mark(10);
	int count;
	count = 0;
        int n;
	n = dis.read();
	headers[num] = dis.readLine();
        //out.println("searching for " + field + " message " + num);
	while (n != 41) {
	    dis.reset();
	    headers[num] = headers[num] + '\n' + dis.readLine();
	    dis.mark(10);
	    n = dis.read();
	    //count = count + 1;
            //out.println("count =" + count);
	    if (count >= 100) {throw new Exception("bad"); {} }
	} // while
    } // setHeader() 

    /*    public MessageHeader parseHeader(String oldHeader) {
	MessageHeader newHeader = new MessageHeader();
	int eol = oldHeader.indexOf('\n');
	int leol = oldHeader.lastIndexOf('\n');
	String header = oldHeader;
	int colon;
	String line;
	while (eol != leol) {
	    line = header.substring(0,eol);
	    header = header.substring(eol);
	    colon = line.indexOf(":");
	    newHeader.setField(line.substring(0,colon), 
			       line.substring(colon));
	} // while
	line = header.substring(0,leol);
	colon = line.indexOf(":");
	newHeader.setField(line.substring(0,colon),
			   line.substring(colon));
	return newHeader;
	} // parseHeader	*/		       
}//Mailbox


    
    

