/**
 *
 * Created on Jan 19, 2005
 *
 */
package thegroup.projectcddb.server;

import java.util.StringTokenizer;
import java.util.Date;
import thegroup.projectcddb.database.ClientInterface;
import thegroup.projectcddb.database.ClientFactory;

/**
 * Implements CDDB layer 1 protocol; provides appropriate responses to client (music player) requests
 * @author Zachary Carter <zcarter @ umail ucsb edu >
 * @version 1.0
 */
public final class ServerProtocol {
    private StringTokenizer reader;
    private String clientCommand;
    private int status; // status indicates server condition
    private static String hostname = "ProjectCDDB.ucsb.edu";
    private static String version = "v1.0";
    private Date todaysDate = new Date();
    private String date = todaysDate.toString();
    private ClientInterface clientInterface; //An EJBClient returns an xmcd file's contents as a string given its discid

  /**
   * ServerProtocol Default Constructor
   */
  ServerProtocol(){
    this(201); //if status not specified, use 201 (OK, read only)
  }

  /**
   * ServerProtocol constructs a ServerProtocol Object
   *
   * @param status int a number specifying the service status of the server
   * 200 = OK, read/write allowed
   * 201 = OK, read only
   * 432 = No connections allowed: permission denied
   * 433 = No connections allowed: X users allowed, Y currently active
   * 434 = No connections allowed: system load too high
   */
  ServerProtocol(int status){
    this.status = status;
    clientCommand = null;
    reader = null;
  }

  /**
   * Parses input from client and provides appropriatly formatted response
   * @param clientCommand String
   * @return String a response to the client's input
   */
  public String processInput(String clientCommand) {
    this.clientCommand = clientCommand;
    reader = new StringTokenizer(clientCommand, " "); //delimiter is a space
    String firstToken = reader.nextToken();
    if (firstToken.compareTo("cddb") == 0){
      String secondToken = reader.nextToken();
      if (secondToken.compareTo("hello") == 0) {
        return handshake();
      }
      if (secondToken.compareTo("lscat") == 0) {
        return listGenre();
      }
      if (secondToken.compareTo("query") == 0) {
        return queryResponse();
      }
      if (secondToken.compareTo("read") == 0) {
	return readEntry();
      }
      if (secondToken.compareTo("unlink") == 0) {
        return ("401 permission denied"); //do not allow database entry deletion
      }
      if (secondToken.compareTo("write") == 0) {
        return ("401 permission denied"); //do not allow database modification
      }
    }
    if (firstToken.compareTo("discid") == 0){
      return ("500 Command Syntax error: discid calculation not yet supported");
    }
    if (firstToken.compareTo("get") == 0){
      //return getDataFile(); //implement this method, get file from database
      return ("401 permission denied."); //temporary
    }
    if (firstToken.compareTo("help") == 0){
      return ("401 no help information available");
    }
    if (firstToken.compareTo("log") == 0){
      return("402 No log information available");
    }
    if (firstToken.compareTo("motd") == 0){
      return motd();
    }
    if (firstToken.compareTo("proto") == 0){
      return protocolLevel();
    }
    if (firstToken.compareTo("put") == 0){
      return ("401 permission denied");
    }
    if (firstToken.compareTo("quit") == 0){
      return ("230 " + hostname + " Closing connection. Goodbye.");
    }
    if (firstToken.compareTo("sites") == 0){
      return ("210 OK, site information follows (until terminating `.')\n127.0.0.1 7777 N034.42 W119.86 Santa Barbara, CA USA\n.");
    }
    if (firstToken.compareTo("stat") == 0){
      return ("210 OK, status information follows (until terminating `.')\n1\n1\nno\nno\nno\nno\n1\n9\nno\n999999\n category1\n category2\n.");
    }
    if (firstToken.compareTo("update") == 0){
      return ("401 Permission denied.");
    }
    if (firstToken.compareTo("validate") == 0){
      return ("503 Validation not required.");
    }
    if (firstToken.compareTo("ver") == 0){
      return ("200 ProjectCDDB v1.0 Copyright not copyrighted");
    }
    if (firstToken.compareTo("whom") == 0){
      return ("401 No user information available.");
    }
      return null;
  } // end processInput

  //The following private methods support public method processInput

  /**
   * to be called when a connection is established with a client, after getBanner()
   *
   * @return String a welcome message
   */
  private String handshake(){
    return ("200 hello and welcome " + reader.nextToken() + "@" + reader.nextToken() + " running " + reader.nextToken() + " " + reader.nextToken());
  }

  /**
   * listGenre
   *
   * @return String a line-separated list of genres in database, end with "."
   */
  private String listGenre(){
    String categories = "210 Okay category list follows (until terminating marker)";
    //Need to figure out the following with the database side
    //for (int c = 1; c <= database.getNumberCategories(); c++){
      //categories += database.getCategoryName(c);
    //}
    //The following is just to pass the test (temporary)
    categories += "\nrock";
    categories += "\njazz";
    categories += "\nblues";
    categories += "\nclassical";
    categories += "\ncountry";
    categories += "\ndata";
    categories += "\nfolk";
    categories += "\nmisc";
    categories += "\nnewage";
    categories += "\nreggae";
    categories += "\nsoundtrack";
    categories += "\n.";
    return categories;
  }

  /**
   * queryResponse
   *
   * @return String matched category, discid, disc title, or close mathes
   */
  private String queryResponse(){
        String discid = reader.nextToken();
        String ntrks = reader.nextToken();
        String offsets = "";
        
        for (int c = 0; c < Integer.parseInt(ntrks); c++){
        offsets += reader.nextToken();
        offsets += " ";
        }
        
        String nsecs = reader.nextToken();
        
        try {
	        clientInterface = ClientFactory.getClientInterface();
	} catch(Exception e) {
		e.printStackTrace(System.out);
	}
        
        String cdInfo = null;
        cdInfo = clientInterface.getFile(discid);
        if (cdInfo == null){
	    	return "202 no match found";  
	    }
        
        StringTokenizer parser = new StringTokenizer(cdInfo, "\n");
        String line;
        String dtitle = null;
        String dgenre = null;
        try {
                while(parser.hasMoreTokens()) {
                        line = parser.nextToken();
                        if(line.startsWith("DTITLE"))
                                dtitle = line.substring(7);
                        if(line.startsWith("DGENRE"))
                                dgenre = line.substring(7);
                        if(dtitle != null && dgenre != null)
                                break;
                }
                parser = new StringTokenizer(dgenre, " ");
                dgenre = "categ";
                while (parser.hasMoreTokens()){
	            	dgenre+= parser.nextToken();    
	            }
        } catch (Exception e) {
                e.printStackTrace();
        }
    
        return ("200 " + dgenre + " "+ discid + " " + dtitle + "\n");
        
  }

  /**
   * readEntry
   *
   * @return String a matching database entry
   */
  private String readEntry() {
	MultiServer.log.finest("readEntry");
	String category = reader.nextToken();
	MultiServer.log.finest(category);
        String discid = reader.nextToken();
	MultiServer.log.finest(discid);
	MultiServer.log.finest("Creating ClientInterface");
	try {
	        clientInterface = ClientFactory.getClientInterface();
	} catch(Exception e) {
		e.printStackTrace(System.out);
	}
	MultiServer.log.finest("Attempting to retrieve disc information");
	String entry = null;
	try {
		entry = clientInterface.getFile(discid);
		if (entry == null){
			return "401";
		}
	} catch(Exception e) {
        	e.printStackTrace();
	}
	MultiServer.log.finest(entry);
        
	return ("210 " + category + " " + discid + "\n" + entry + "\n.");
  }

  /**
   * motd
   *
   * @return String the Message of the Day
   */
  private String motd(){
    return ("210 Last modified: 01/27/05 22:52:14 MOTD follows (until terminating marker) \nThanks for using ProjectCDDB\n.");
  }

  /**
   * protocolLevel
   *
   * @return String
   */
  private String protocolLevel(){
    String secondToken = reader.nextToken();
    if (secondToken.compareTo("1") == 0){
      return ("502 CDDB protocol level: current 1, supported 1");
    }
    else
      return ("501 CDDB protocol level: current 1, supported 1");
  }

  //accessor and mutator methods

  /**
  * called immediately after a connection is established with a CDDB client, prompts CDDB client request
  *
  * @return String this CDDBP Server's Banner
  */
  public String getBanner(){
    return (status + " " + hostname + " CDDB server " + version + " ready at " + todaysDate);
  }

  /**
  * called immediately after a connection is established with a CDDB client, prompts CDDB client request
  *
  * @return String this CDDBP Server's Banner
  */
  public void setStatus(int status){
    this.status = status;
  }
} // end class
