import java.util.Random;

class ad extends ActiveCacheInterface {
  // maximal 10 positions in document,
  static int MAXPOS = 10;
  // 10 possible choices for rotation banner for each position
  static int MAXOPT = 10;

// the main function of the applet
public static int FromCache(String User_HTTP_Request,
                            String Client_IP_Address,
                            String Client_Name,
                            int Cache_File,
                            int New_File)
  {
    boolean inCache;
    // ads.obj is the specification file, which specifies the
    // positions in documents and ratation banners, check
    // whether the ads.obj object is in ActiveCache
    inCache = is_in_cache("ads.obj");
    // if the object is not in cache, we need to get it
    // from the server and save it in an object in ActiveCache
    if (!inCache) {
      int result;
      result = getAdsObj();
      if (result == -1) return -1;
    }
    // the positions in the document
    String position[] = new String[MAXPOS];
    // the rotation banners for all the positions
    String htmls[][] = new String[MAXPOS][MAXOPT];
    // the probability of each roataion banner
    double freqs[][] = new double[MAXPOS][MAXOPT];
    // the seed for each position
    long seeds[] = new long[MAXPOS];
    // initialize these variables
    init(htmls, position, freqs, seeds);
    // read the content of the ads.obj into the variables
    // including the positions, rotation banners, probabilities
    if (readAdsObj(position, htmls, freqs) == -1)
      return -1;
    // read the seeds into the variable seeds
    readSeed(seeds);

    // now we scan the document and insert rotation banners
    // in their positions
    String line, posStr;
    while (true) {
      // read a line from the cached document
      line = readLine(Cache_File);
      // if there is nothing left from the document, exit
      if (line.equals(""))
        break;
      // check whether this line contains start pattern of position
      // which is the form: <!-- Begin Rotate Banner position i -->
      posStr = checkBeginBanner(line);
      if (posStr.equals("")) {
        // no pattern in this line, just copy this line to new document
        byte tmp[] = new byte[line.length()];
        line.getBytes(0, line.length(), tmp, 0);
        write(New_File, tmp, tmp.length);
      } else {
        // rotate banner section begins, posStr is the position string
        int pos, choice;
        // check whether this position string is specified by ads.obj
        pos = findPosition(position, posStr);
        // if not specified, ignore this pattern
        if (pos == -1) continue;
        // we make a choice as which banner to insert for this position
        choice = makeChoice(freqs[pos], seeds[pos]);
        // change the seed for this position, so next time it is different
        changeSeed(seeds, pos);
        // write the rotation banner corresponding to the choice into new document
        byte tmp[] = new byte[htmls[pos][choice].length()];
        htmls[pos][choice].getBytes(0, htmls[pos][choice].length(), tmp, 0);
        write(New_File, tmp, tmp.length);
        // sacan the cached document until we found the end pattern
        while (true) {
          line = readLine(Cache_File);
          if (line.equals("")) break;
          if (checkEndBanner(line)) break;
        }
      }
    }
    // some seeds may have changed, we write the updated seeds into seed object
    writeSeed(seeds);
    // let the proxy give the new document to client
    return 1;
  }

// send HTTP request to server to get the ads.obj object
// parse the respone from server and save the content of
// response into an object in ActiveCache
private static int getAdsObj()
  {
    StringBuffer reqStr = new StringBuffer();
    String curDoc, docName;
    int pos;
    int fd, newfd;
    boolean result;

    // construct the url of ads.obj, which is the url
    // of the document appended by .ads.obj

    // get the url of the current document
    curDoc = curdocurl();
    pos = curDoc.indexOf("http://");
    if (pos == -1) return -1;
    pos = curDoc.indexOf('/', pos+8);
    if (pos == -1) return -1;
    docName = curDoc.substring(pos);
    // construct the HTTP request, GET html_doc.ads.obj HTTP/1.0
    reqStr.append("GET ").append(docName)
      .append(".ads.obj").append(" HTTP/1.0\n\n");
    // send request to server getting the object
    fd = send_request_to_server(new String(reqStr));
    if (fd == -1) return -1;
    // see whether the GET request succeeded
    result = ParseResult(fd);
    if (!result) return -1;
    // filter the header part of the response
    filterHeader(fd);
    // create an object in ActiveCache for the ads.obj
    newfd = create("ads.obj", 00700);
    if (newfd == -1) return -1;
    int num;
    byte buf[];
    buf = new byte[1024];
    // save the content of the response into ads.obj
    while ((num = read(fd, buf, 1024)) > 0) {
      write(newfd, buf, num);
    }
    close(newfd);
    return 0;
  }

// check whether the status of the response is 200
// the response should begin with HTTP/1.x 200 OK
private static boolean ParseResult(int fd)
  {
    String line, result;
    int pos;
    line = readLine(fd);
    pos = line.indexOf(' ');
    if (pos == -1) return false;
    result = line.substring(pos+1, pos+4);
    if (result.equals("200"))
      return true;
    else
      return false;
  }

// filter the header of the HTTP response
private static int filterHeader(int fd)
  {
    String line;
    while (true) {
      line = readLine(fd);
      if (line.equals("\r\n")) return 1;
      if (line.equals("\n")) return 1;
      if (line.equals("")) return -1;
    }
  }

// initialize the values of the variables
private static void init(String htmls[][],
                              String position[],
                              double freqs[][],
                              long seeds[])
  {
    int i, j;
    for (i=0; i<MAXPOS; i++) {
      for (j=0; j<MAXOPT; j++) {
        htmls[i][j] = new String("");
      }
    }
    for (i=0; i<MAXPOS; i++) {
      position[i] = new String("");
    }

    for (i=0; i<MAXPOS; i++) {
      for (j=0; j<MAXOPT; j++) {
        freqs[i][j] = 0.0;
      }
    }
    for (i=0; i<MAXPOS; i++) {
      seeds[i] = 0;
    }
  }

// read the ads.obj object, which contains the
// positions, rotation banners, probabilities
// general format of the object is:
//    position 1
//    probability_1 roataion_banner_1
//    probability_2 rotation_banner_2
//    ... ...
//    probability_n rotation_banner_n
//    position_2
//    ... ...
//    position_n
private static int readAdsObj(String position[],
                              String htmls[][],
                              double freqs[][])
  {
    int fd;
    // open the object from the ActiveCache
    fd = open("ads.obj", 0);
    if (fd == -1) return -1;

    int i, j;
    String line, doubleStr, htmlStr;
    i = -1; j = 0;
    while (true) {
      // read one line from the object
      line = readLine(fd);
      // if nothing left to read, exit
      if (line.equals("")) break;
      if (line.startsWith("Position ")) {
        // if this line specifies one position, record the position
        i++; j=0;
        position[i] = new String(line.substring(0, line.length()-1));
      } else {
        // the file should begin with Position line
        if (i < 0) {
          close(fd);
          return -1;
        }
        int pos;
        // this line should specify a pair of probability
        // and rotation_banner separated by spaces
        // read till a space, this should be a probabilities
        for (pos=0; (pos<line.length()) && (line.charAt(pos)!=' ')
               && (line.charAt(pos)!='\t'); pos++);
        // if the line has no space, illegal format, ignore it
        if (pos >= line.length()) continue;
        // get the value of the probability, save it in freqs[i][j]
        doubleStr = line.substring(0, pos);
        freqs[i][j] = Double.valueOf(doubleStr).doubleValue();
        // read until a non-space character, where the rotation banner starts
        for (; (pos<line.length()) && ((line.charAt(pos)==' ')
               || (line.charAt(pos)=='\t')); pos++);
        // illegal format, ignore it
        if (pos >= line.length()) continue;
        // record the rotation banner in htmls[i][j]
        htmls[i][j] = line.substring(pos);
        j++;
      }
    }
    close(fd);
    return 0;
  }

// read the seed object from ActiveCache, if it is the
// first invocation of applet, the seed object does not
// exist yet, we use zeros for seed in this case
private static void readSeed(long seeds[])
  {
    boolean inCache;
    int fd;
    String line;
    inCache = is_in_cache("seed");
    // the seed object not in ActiveCache yet, ignore
    // it, we just use zeros as seeds
    if (!inCache) return;
    else {
      int i;
      // open the seed object for reading
      // each line of the seed object is just a value
      fd = open("seed", 0);
      if (fd == -1) return;
      for (i=0; i<MAXPOS; i++) {
        int start;
        // read one line, if no line left, exit
        line = readLine(fd);
        if (line.equals("")) break;
        start = 0;
        // read the value in this line, into seeds[i]
        while (Character.isDigit(line.charAt(start)))
          start ++;
        seeds[i] = Long.valueOf(line.substring(0, start)).longValue();
      }
    }
  }

// check whether this line contains a start pattern
// which is like <!--Begin Rotate Banner position i -->
// if true, return the position string, otherwise return
// empty string
private static String checkBeginBanner(String line)
  {
    int pos1, pos2, pos3;
    pos1 = line.indexOf("<!--");
    pos2 = line.indexOf("Begin Rotate Banner");
    pos3 = line.indexOf("-->");
    if ((pos1==-1) || (pos2==-1) || (pos3==-1))
      return new String("");
    if ((pos1<pos2) && (pos2<pos3)) {
      int start, end;
      start = pos2+19; end = pos3;
      while (!Character.isLetterOrDigit(line.charAt(start)))
        start ++;
      while (!Character.isLetterOrDigit(line.charAt(end)))
        end --;
      return line.substring(start, end+1);
    } else {
      return new String("");
    }
  }

// check whether this line contains an end pattern,
// which is like: <!-- End Rotate Banner position i -->
private static boolean checkEndBanner(String line)
  {
    int pos1, pos2, pos3;
    pos1 = line.indexOf("<!--");
    int pos1, pos2, pos3;
    pos1 = line.indexOf("<!--");
    pos2 = line.indexOf("End Rotate Banner");
    pos3 = line.indexOf("-->");
    if ((pos1==-1) || (pos2==-1) || (pos3==-1))
      return false;
    if ((pos1<pos2) && (pos2<pos3)) {
      return true;
    } else {
      return false;
    }
  }

// check whether a position specified in the document has been
// specified in the ads.obj object. The specified positions
// of ads.obj are in position[], PosStr is a position found
// in the html document
private static int findPosition(String position[], String PosStr)
  {
    int i;
    for (i=0; i<MAXPOS; i++) {
      if (position[i] == null) continue;
      if (PosStr.equals(position[i]))
        return i;
    }
    return -1;
  }

// make a choice based on the probabilities and seed for one position
// freqs[] is the probabilities of rotation banners of one position
// seed is the seed for this position
private static int makeChoice(double freqs[], long seed)
  {
    int i;
    double randNum, total;
    Random rand = new Random(seed);
    // generate a random number
    randNum = rand.nextDouble();
    total = 0;
    // see the number fall into which rotation banner
    // and make a choice
    for (i=0; i<MAXOPT; i++) {
      total += freqs[i];
      if (total > randNum) {
        return i;
      }
    }
    return 0;
  }

// the seed for the position is changed
private static void changeSeed(long seeds[], int pos)
  {
    seeds[pos] += 10000;
  }

// write the seeds into seed object, if this is the
// first invocation of applet, we need to create the seed
// object now
private static void writeSeed(long seeds[])
  {
    boolean inCache;
    int i, fd;
    byte buf[];
    inCache = is_in_cache("seed");
    if (inCache) {
      fd = open("seed", 2);
    } else {
      fd = create("seed", 00700);
    }
    if (fd == -1) {
      return;
    }
    for (i=0; i<MAXPOS; i++) {
      buf = Convert(Long.toString(seeds[i]));
      write(fd, buf, buf.length);
    }
    close(fd);
  }

// convert a String into a byte array
private static byte[] Convert(String str)
  {
    int i;
    byte buf[] = new byte[str.length()+1];
    for (i=0; i<str.length(); i++) {
      buf[i] = (byte)(str.charAt(i));
    }
    buf[i] = 10;
    return buf;
  }

// read one line from the file, fd is the file descriptor
private static String readLine(int fd)
  {
    StringBuffer strbuf = new StringBuffer(0);
    byte buf[] = new byte[1];
    while (read(fd, buf, 1)==1) {
      strbuf.append((char)buf[0]);
      if (buf[0] == '\n') break;
    }
    return new String(strbuf);
  }
}