import socket
import os
import subprocess
import sys
import threading
import Queue
import errno
import json
import struct

class Driver:
  def __init__(self, port, use_stdin, use_air = False):
    self.port = port
    self.use_stdin = use_stdin
    self.use_air = use_air
    self.stdin = None
    self.conn = None

  def start(self):
    self.sock = socket.socket()
    self.sock.bind(("localhost", self.port))

    self.sock.settimeout(0.5)
    self.sock.listen(0)
    
    exporter_proc_args = [os.getcwd() + "/Exporter.exe"]
    if self.use_air:
      exporter_proc_args = [os.getcwd() + "/Exporter.app/Expoter.exe", str(self.port)]

    self.proc = subprocess.Popen(exporter_proc_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

    if self.use_stdin:
      self.stdin = Queue.Queue()
      reader = threading.Thread(target=self._get_stdin)
      reader.daemon = True
      reader.start()

    while self.conn is None and self.proc.returncode is None:
      try:
        (self.conn, _) = self.sock.accept()
      except socket.timeout, e:
        pass
      except e:
        print e
        self.sock.close()
        self.proc.wait()
        sys.exit(1)
      self.proc.poll()

    if self.conn:
      self.conn.setblocking(False)
    return self.conn != None

  def send_json(self, jsonObj):
    data = json.dumps(jsonObj)
    datalen = struct.pack(">H", len(data))
    self.conn.send(datalen)
    self.conn.send(data)

  def loop(self, msg_callback, stdin_callback = None):
    buf = ""
    quit = False
    while self.proc.returncode is None:
      try:
        d = self.conn.recv(4096)
        if len(d):
          buf += d
          if len(buf) >= 2:
            buf_len = struct.unpack_from(">H", buf, 0)[0]
            if len(buf) >= 2 + buf_len:
              msg = buf[2:2+buf_len]
              buf = buf[2+buf_len:]
              if not msg_callback(msg):
                quit = True
      except socket.error, e:
        if e.args[0] == errno.EWOULDBLOCK or e.args[0] == errno.EAGAIN:
          pass
        else:
          print e
          quit = True
      if not quit and self.use_stdin and self.stdin.qsize():
        line = self.stdin.get()
        if line == "":
          quit = True
        elif stdin_callback:
          if not stdin_callback(line):
            quit = True
      if quit:
        self.send_json({"command":"exit"})
        self.conn.close()
        self.sock.close()
        self.proc.kill()
        sys.exit(1)
      self.proc.poll()

    if self.conn:
      self.conn.close()
    self.sock.close()

  def _get_stdin(self):
    while True:
      line = sys.stdin.readline()
      self.stdin.put(line)
      if line == "":
        return
