
import java.lang.*;
import GUIDecoder;
import GUIEncoder;
import ClientTransferObject;
import EntryList;
import DatabaseEntry;

/**
 *This class takes requests from the GUI and calls an Encoder method to turn 
 *the request into a transferable String. Then that String is passed to the 
 *Network.  The Network will pass back a String and this class will call a 
 *Decoder method and return the result to the GUI.
 *   @author Graham Gelling
 *   @author Thomas Barnhart
 */ 
public class DatabaseClient
    implements DB_Interface {
    
    //Fields
    protected GUIDecoder decode = null;
    
    protected GUIEncoder encode = null;
    
    protected ClientTransferObject cto = null;

    //constuctors
    
        
    public DatabaseClient (String password)
    throws Exception {

	this.decode = new GUIDecoder();
	
	this.encode = new GUIEncoder();
    
	try {
	    cto = new ClientTransferObject();
	} catch (Exception e) {
	    System.out.println("Failed to make ClientTranferObject!"
			       + e.getMessage());
            throw new Exception("Failed to make ClientTransferObject!" + 
                                e.getMessage());
	}
    }

    public void QUIT()  throws Exception {
	try {
	cto.send("QUIT" + 03);
	} catch (Exception e) {
	    System.out.println("Failed to send" 
			       + e.getMessage());
            throw new Exception("Failed to make ClientTransferObject!" + 
                                e.getMessage());
	} 
    }

    /** Method update(DatabaseEntry newValues);
     *  Pre: The data base has been initialized
     *  Pre: The object is complete (all fields, in particular the key) are 
     *       initialized
     *  Pre: the key is in the proper format (see the note above about the key)
     *  Post: returns the Object with the given key
     *  Post: The database is not changed
     */
     public void update(DatabaseEntry newValues)
        throws Exception{ 
         String str = new String();
         String str2 = new String();
         int index;

	 if (newValues instanceof Art) {
	     System.out.println("Found Art");
	     str = this.encode.update1((Art) newValues);
	     System.out.println("String: " + str);
	 }
	 if (newValues instanceof Artist) {
	     System.out.println("Found Artist");
	     str = this.encode.update2((Artist) newValues);
	     System.out.println("String: " + str);
	 }
	 if (newValues instanceof Bidder) {
	     System.out.println("Found Bidder");
	     str = this.encode.update3((Bidder) newValues);
	     System.out.println("String: " + str);
	 }
         str = str.concat(this.end());
         this.cto.send(str);
         str2 = this.cto.receive();
         if (!str2.equals("0")){
             index = str2.indexOf("\n");
             str2 = str2.substring(index+1);
             throw new Exception(str2);
         }//if
     }//update
    
    /** Method lookup(String key)
     *  Pre: There exists an Object in the relevant database with the key 
     *       passed on as a parameter
     *  Pre: the key is in the proper format (see the note above about the key)
     *  Post: returns the Object with the given key
     *  Post: The database is not changed
     */
    public DatabaseEntry lookUp (String key)
        throws Exception{
        String str = new String();
        String str2 = new String();
        int index;
	DatabaseEntry entry = null;

        str = this.encode.lookup(key);
        str = str.concat(this.end());
        this.cto.send(str);
        str2 = this.cto.receive();
        index = str2.indexOf("\n");
        str = str2.substring(0,index);
        if (str.equals("error")){
            str2 = str2.substring(index+1);
            throw new Exception(str2);
         }//if
        else {
            entry = this.decode.lookup(key, str2);
	}
        return entry;
    }//lookup

    /** Method EntryList listByField(int objectType,  String fieldName, String pattern);
     * objectType: field in the objectType class
     *           objectType.ARTIST, for example.
     * pattern : #* words starting with letter #
     *         : other strings = exact match
     *
     *  Pre: The database is initialized.
     *  Post: Returns a list triplets of key, name, and id of all the Objects 
     *        in the appropriate array whose Field starts with FirstLetter.
     *  Post: If there is no match, the length of the output list is 0.
     *  Note: We will use an array based list for the triplets and the outer
     *        shell.
     */    

    public EntryList listByField(int objectType,
                                 String fieldName,
                                 String pattern)
        throws Exception{
        String str = new String();
        String str2 = new String();
        int index;
	EntryList entry = null;
        str = this.encode.listByField(objectType,fieldName,pattern);
        str = str.concat(this.end());
        this.cto.send(str);
        str2 = this.cto.receive();
        index = str2.indexOf("\n");
        str = str2.substring(0,index);
        if (str.equals("error")){
            str2 = str2.substring(index+1);
            throw new Exception(str2);
         }//if
        else{
            str = str2.substring(index+1);
            entry = this.decode.list(str);
	}
        return entry;
    }//listByField
    
    /** Method EntryList listByField(int objectType,  String fieldName, String pattern, 
     *   String displayField);
     * objectType: field in the objectType class
     *           objectType.ARTIST, for example.
     *
     * fieldName: Name of the field to search for pattern
     *
     * pattern : #* words starting with letter #
     *         : other strings = exact match
     *
     * displayField: An additional field to return in the EntryList. 
     *
     *  Pre: The database is initialized.
     *  Post: Returns a list triplets of key, name, id, and optional displayField 
     *  for all the Objects in the appropriate array whose Field starts with FirstLetter,
     *  (if pattern is X*) or completely match (if not #* pattern)
     *
     *  Post: If there is no match, the length of the output list is 0.
     *  Note: We will use an array based list for the triplets and the outer
     *        shell.
     */    
    
    public EntryList listByField(int objectType,
                                  String fieldName,
                                  String pattern,
                                  String optional)
        throws Exception{
        String str = new String();
        String str2 = new String();
        String str3 = new String();
        int index;
	EntryList entry = null;
        str = this.encode.listByField(objectType,fieldName,pattern,optional);
        str = str.concat(this.end());
	//System.out.println("listByField 4Arg: STR: " + str);
        this.cto.send(str);
        str2 = this.cto.receive();
	//System.out.println("listByField 4Arg: STR2: " + str2);
        index = str2.indexOf("\n");
        str3 = str2.substring(0,index);
        if (str3.equals("error")){
            str2 = str2.substring(index+1);
            throw new Exception(str2);
         }//if
        else{str3 = str2.substring(index+1);
	//System.out.println("listByField 4Arg: STR3: .." + str3 + "..");
             entry = this.decode.list(str3);}
        return entry;
    }//listByField(int, String, String, String)
    
    /** Method EntryList listAll(int objectType, String fieldName);
     *  Pre: The database is initialized.
     *  Post: Returns an array-based list of triplets for every object in the 
     *        array. The length of the list will be zero, if there are no
     *        object stored in the array as indicated by type.
     */    
    public EntryList listAll(int objectType, String fieldName)
        throws Exception{
        String str = new String();
        String str2 = new String();
        int index;
	EntryList entry = null;
        str = this.encode.listAll(objectType,fieldName);
        str = str.concat(this.end());
        this.cto.send(str);
        str2 = this.cto.receive();
        index = str2.indexOf("\n");
        str = str2.substring(0,index);
        if (str.equals("error")){
            str2 = str2.substring(index+1);
            throw new Exception(str2);
         }//if
        else {str = str2.substring(index+1);
             entry = this.decode.list(str);
	}
        return entry;
    }//listAll
    
    /** Method String add (int objectType, DatabaseEntry newObject)
     *  Pre: The database is initialized.
     *  Pre: The newObject is complete except for lacking a Database Key.
     *  Post: Adds the new object to the database.
     *  Post: Returns the key as confirmation of success.
     *  Post: Returns empty string to denote key collision, or other failure.
     */    
    public String add (int objectType, DatabaseEntry newObject)
        throws Exception{
        String str = new String();
        String str2 = new String();
        int index;
        str = this.encode.add(objectType, newObject);
        str = str.concat(this.end());
        this.cto.send(str);
        str2 = this.cto.receive();
        index = str2.indexOf("\n");
        str = str2.substring(0,index);
        if (str.equals("error")){
            index = str2.indexOf("\n");
            str2 = str2.substring(index+1);
            throw new Exception(str2);
        }//if
        else{
            str = str2.substring(index+1);}
        return str;
    }//add
    
    /**
     *This method creates an EOF marker for appending to the end of All strings sent to the Network
     *Post:A String is returned with byte value 03
     */
    public String end (){
        byte etx[] = new byte[1];
        etx[0]=03;
        String str_etx = new String(etx);
        return str_etx;
    }//end    
    public static void main(String arg[] ) {
	DatabaseClient dbc = null;
	try {
	    dbc = new DatabaseClient("foo");
	} catch (Exception e) {
	    System.out.println("Can't do it!" + e.getMessage());
	}

	EntryList el = null;

	try {
	el = dbc.listByField(ObjectType.ARTIST, "lastname", "K*", "firstname");
	el.reset();

	System.out.println("NAME: " + el.getCurrentName());

	} catch (Exception e) {
	    System.out.println("22 Can't get it!" + e.getMessage());
	}

	try {
	el = dbc.listByField(ObjectType.ART, "title", "M*", "fname");
	el.reset();
	System.out.println("NAME: " + el.getCurrentName());
	} catch (Exception e) {
	    System.out.println("11 Can't get it!" + e.getMessage());
	}

	try {
	dbc.QUIT();

	} catch (Exception e) {
	    System.out.println("Last: Can't get it!" + e.getMessage());
	}
    }
}//DatabaseClient


