package com.greenphosphor.glasshouse;

import javax.servlet.http.*;
import javax.servlet.*;
import java.util.*;
import java.io.*;
import java.sql.*;
import java.net.*;
import java.util.zip.*;
import java.util.logging.*;

public class CICPProxy extends HttpServlet {

  private static Hashtable layerTable = new Hashtable();
  private Logger log;
  public static final int CHUNK_SIZE = 12;
  public static long layerExpireMillis = 3600000;

  public void init () throws ServletException {
    log = Logger.getLogger("cicp");
  }

  protected void doGet ( HttpServletRequest req, HttpServletResponse res ) throws ServletException, IOException {
    try {
      res.setContentType("text/html");
      PrintWriter pw = res.getWriter();
      String type = req.getParameter("type");
      int layer = Integer.parseInt(req.getParameter("layer"));
      if ( type.equals("msg") )
        processMsg(req, pw, layer);
      else if ( type.equals("chunk") )
        printChunk(pw, (LayerData)layerTable.get(String.valueOf(layer)));
      else if ( type.equals("cell") )
        processCell(req, pw, layer);
      pw.close();
    }
    catch ( SQLException se ) {
      throw new ServletException("error: " + se, se);
    }
    catch ( IOException ie ) {
      throw new ServletException("error: " + ie, ie);
    }
  }

  private void processMsg ( HttpServletRequest req, PrintWriter pw, int layer ) throws IOException, SQLException {
    String msg = req.getParameter("msg");
    log.info("received from world: " + msg);

    String rline = null;
    try {
      Socket s = new Socket("glasshouse.greenphosphor.com", 4445);
      PrintWriter pwg = new PrintWriter(new OutputStreamWriter(s.getOutputStream()));
      BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
      pwg.println(msg);
      pwg.flush();
      rline = br.readLine();
      System.out.println("received from gateway: " + rline);
      pwg.close();
      br.close(); 
    }
    catch ( IOException ie ) {
      System.out.println("exception processing msg: " + ie);
    }

    if ( !msg.startsWith("init") ) {
      String[] split = msg.split(",");
      int sourceLayer = Integer.parseInt(split[0]);
      if ( split[2].equals("-3") )
        layerTable.remove(String.valueOf(sourceLayer));
    }

    if ( rline != null && rline.startsWith("create") )
      loadCells(rline, pw);
  }

  private void processCell ( HttpServletRequest req, PrintWriter pw, int layer ) {
    LayerData ld = (LayerData)layerTable.get(String.valueOf(layer));
    float yPos = Float.parseFloat(req.getParameter("yPos"));
    float xPos = Float.parseFloat(req.getParameter("xPos"));
    float zPos = Float.parseFloat(req.getParameter("zPos"));

    String key = String.valueOf(Math.round(xPos * 100)) + "," +
                 String.valueOf(Math.round(yPos * 100)) + "," +
                 String.valueOf(Math.round(zPos * 100));
    //log.info("key: " + key);
    CellInfo val = ld.cells.get(key);
    if ( val != null )
      pw.println(val.props);
    else
      log.info("null cell for key <" + key + ">");
    LinkedList<String> ll = ld.menuCache.get(val.menuKey);
    if ( ll == null )
      log.info("null menu for menuKey <" + val.menuKey + ">");
    ListIterator<String> li = ll.listIterator();
    while ( li.hasNext() ) {
      String l = li.next();
      pw.println(l);
    }
  }

  private void loadCells ( String rline, PrintWriter pw ) throws IOException {
    log.info(rline);
    String[] split = rline.split(",");
    int layer = Integer.parseInt(split[7]);
    double startX = Double.parseDouble(split[1]);
    double startY = Double.parseDouble(split[3]);
    double startZ = Double.parseDouble(split[2]);

    removeExpiredLayers();

    LayerData ld = new LayerData(layer, startX, startY, startZ);
    layerTable.put(String.valueOf(layer), ld);

    URL url = new URL(split[8]);
    URLConnection conn = url.openConnection();
    BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
    String line;
    boolean doMenus = true;
    String menuKey = null;
    LinkedList<String>menuList = null;
    while ( (line = br.readLine()) != null ) {
      if ( doMenus && line.equals("nodes") ) {
        doMenus = false;
        continue;
      }

      String[] lsplit = line.split(",");
      if ( doMenus ) {
        if ( lsplit[0].equals("menu") ) {
          menuKey = lsplit[1];
          menuList = new LinkedList<String>();
        }
        else if ( lsplit[0].equals("endmenu") )
          ld.menuCache.put(menuKey, menuList);
        else
          menuList.add(line);
      }
      else if ( !lsplit[0].equals("filament") && !lsplit[0].equals("create") ) {
        float[] dims = new float[3];
        String lineAbs = makeAbsolute(lsplit, dims, ld);
        String key2 = String.valueOf(dims[0]) + "," + String.valueOf(dims[1]) + "," + String.valueOf(dims[2]);
        String keyRound = String.valueOf(Math.round(dims[0] * 100)) + "," +
                          String.valueOf(Math.round(dims[1] * 100)) + "," +
                          String.valueOf(Math.round(dims[2] * 100));
        ld.cells.put(keyRound, new CellInfo(lineAbs, lsplit[18]));
        ld.cellList.add(key2);
      }
    }
    br.close();

    pw.println(String.valueOf(ld.layer));
    printChunk(pw, ld);
  }

  private void removeExpiredLayers(){
      Enumeration e = layerTable.keys();
      while(e.hasMoreElements()){
          String tempKey = (String) e.nextElement();
          LayerData tld = (LayerData) layerTable.get(tempKey);
          if(System.currentTimeMillis()-tld.timeMillis > layerExpireMillis){
              layerTable.remove(tempKey);
              log.info("Removed expired layer #: "+tld.layer);
          }
      }
  }

  private void printChunk ( PrintWriter pw, LayerData ld ) throws IOException {
    int i = ld.curChunk;
    while ( ld.curChunk < i + CHUNK_SIZE && ld.curChunk < ld.cellList.size() ) {
      String s = ld.cellList.get(ld.curChunk);
      pw.println(s);
      ld.curChunk++;
    }
    if ( ld.curChunk >= ld.cellList.size() )
      pw.println("done");
    else
      pw.println("more");
  }

  public static String slEncode ( String str ) {
    String encStr = str;
    try {
      encStr = URLEncoder.encode(str, "UTF-8");
    }
    catch ( UnsupportedEncodingException e ) {
      System.out.println("error in slEncode: " + e);
    }
    return encStr.replace("+", "%20");
  }

  private static String stripQuotes ( String str ) {
    return str.substring(1, str.length() - 1);
  }

  private String makeAbsolute ( String[] lsplit, float[] dims, LayerData ld ) {
    double relx = Double.parseDouble(lsplit[1]);
    double relz = Double.parseDouble(lsplit[2]);
    double rely = Double.parseDouble(lsplit[3]);
    StringBuffer sb = new StringBuffer(lsplit[0]);
    dims[0] = (float)(relx + ld.startX);
    dims[1] = (float)(rely + ld.startY);
    dims[2] = (float)(relz + ld.startZ);
    sb.append("," + String.valueOf(dims[0]));
    sb.append("," + String.valueOf(dims[1]));
    sb.append("," + String.valueOf(dims[2]));
    int startCopy = 4;
    if ( lsplit[0].equals("lin") ) {
      double x2 = Double.parseDouble(lsplit[4]);
      double z2 = Double.parseDouble(lsplit[5]);
      double y2 = Double.parseDouble(lsplit[6]);
      float x2f = (float)(x2 + ld.startX);
      float y2f = (float)(y2 + ld.startY);
      float z2f = (float)(z2 + ld.startZ);
      sb.append("," + String.valueOf(x2f));
      sb.append("," + String.valueOf(y2f));
      sb.append("," + String.valueOf(z2f));
      startCopy = 7;
    }
    for ( int i = startCopy; i < lsplit.length; i++ )
      sb.append("," + lsplit[i]);
    return sb.toString();
  }
}
