package nxm.vnc.libvnc.tightvnc;

import java.awt.*;
import java.io.*;

/** This class is overrides VncCanvas to allow the thread to be stopped without System.exit().
    @version $Id: NxmCanvas.java,v 1.3 2013/03/08 14:05:12 ntn Exp $
 */
class NxmCanvas extends VncCanvas {
  /** NOTE: this should be changed if the structure/fields of this class changes. */
  private static final long serialVersionUID = 2013030800332L;

  NxmViewer nxmviewer;

  NxmCanvas(NxmViewer v) throws IOException {
    super(v);
    nxmviewer = v;
  }

  /** This is a duplicate of processNormalProtocol from VncCanvas
      except it checks viewer.isShutdown() instead of looping forever
   */
  public void processNormalProtocol() throws Exception {

    // Start/stop session recording if necessary.
    viewer.checkRecordingStatus();

    rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth, rfb.framebufferHeight, false);

    //
    // main dispatch loop
    //
    while (!nxmviewer.isShutdown()) {

      // Read message type from the server.
      int msgType = rfb.readServerMessageType();

      // Process the message depending on its type.
      switch (msgType) {
        case RfbProto.FramebufferUpdate:
          rfb.readFramebufferUpdate();

          boolean cursorPosReceived = false;

          for (int i = 0; i < rfb.updateNRects; i++) {
            rfb.readFramebufferUpdateRectHdr();
            int rx = rfb.updateRectX, ry = rfb.updateRectY;
            int rw = rfb.updateRectW, rh = rfb.updateRectH;

            if (rfb.updateRectEncoding == RfbProto.EncodingLastRect)
              break;

            if (rfb.updateRectEncoding == RfbProto.EncodingNewFBSize) {
              rfb.setFramebufferSize(rw, rh);
              updateFramebufferSize();
              break;
            }

            if (rfb.updateRectEncoding == RfbProto.EncodingXCursor ||
                rfb.updateRectEncoding == RfbProto.EncodingRichCursor) {
              handleCursorShapeUpdate(rfb.updateRectEncoding, rx, ry, rw, rh);
              continue;
            }

            if (rfb.updateRectEncoding == RfbProto.EncodingPointerPos) {
              softCursorMove(rx, ry);
              cursorPosReceived = true;
              continue;
            }

            switch (rfb.updateRectEncoding) {
              case RfbProto.EncodingRaw:
                handleRawRect(rx, ry, rw, rh);
                break;
              case RfbProto.EncodingCopyRect:
                handleCopyRect(rx, ry, rw, rh);
                break;
              case RfbProto.EncodingRRE:
                handleRRERect(rx, ry, rw, rh);
                break;
              case RfbProto.EncodingCoRRE:
                handleCoRRERect(rx, ry, rw, rh);
                break;
              case RfbProto.EncodingHextile:
                handleHextileRect(rx, ry, rw, rh);
                break;
              case RfbProto.EncodingZlib:
                handleZlibRect(rx, ry, rw, rh);
                break;
              case RfbProto.EncodingTight:
                handleTightRect(rx, ry, rw, rh);
                break;
              default:
                throw new Exception("Unknown RFB rectangle encoding " + rfb.updateRectEncoding);
            }
          }

          boolean fullUpdateNeeded = false;

          // Start/stop session recording if necessary. Request full
          // update if a new session file was opened.
          if (viewer.checkRecordingStatus())
            fullUpdateNeeded = true;

          // Defer framebuffer update request if necessary. But wake up
          // immediately on keyboard or mouse event. Also, don't sleep
          // if there is some data to receive, or if the last update
          // included a PointerPos message.
          if (viewer.deferUpdateRequests > 0 &&
              rfb.is.available() == 0 && !cursorPosReceived && !nxmviewer.isShutdown()) {
            synchronized(rfb) {
              try {
                rfb.wait(viewer.deferUpdateRequests);
              } catch (InterruptedException e) {
              }
            }
          }

          // Before requesting framebuffer update, check if the pixel
          // format should be changed. If it should, request full update
          // instead of an incremental one.
          if (viewer.options.eightBitColors != (bytesPixel == 1)) {
            setPixelFormat();
            fullUpdateNeeded = true;
          }

          rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth, rfb.framebufferHeight, !fullUpdateNeeded);
          break;

        case RfbProto.SetColourMapEntries:
          throw new Exception("Can't handle SetColourMapEntries message");

        case RfbProto.Bell:
          Toolkit.getDefaultToolkit().beep();
          break;

        case RfbProto.ServerCutText:
          String s = rfb.readServerCutText();
          viewer.clipboard.setCutText(s);
          break;

        default:
          throw new Exception("Unknown RFB message type " + msgType);
      }
    }
  }

}
