import numpy as np

import json
import os
import serial
import subprocess
import threading
import time

from humidifier import Humidifier, HumidifierV2
from humidifier_v3 import HumidifierV3
from controller import Controller

import utils
from utils import start_process, send_update

SERIAL_PATH = "/dev/ttyACM0"
SERIAL_BAUD = 115200

SAMPLE_PERIOD = 0.5
DECIMATION_RATE = 1

try:
  is_mock = os.environ['MOCK']
except KeyError:
  is_mock = False

print("controller start")
start_process()

if is_mock:
  import mock_serial
  s = mock_serial.MockSerial()
else:
  s = serial.Serial(SERIAL_PATH, SERIAL_BAUD, timeout = 0.3)
  print("pausing for bootloader...")
  time.sleep(10)
  print("pause done")

def reset_serial():
  global s
  if not is_mock:
    s.close()
    s = serial.Serial(SERIAL_PATH, SERIAL_BAUD, timeout = 0.3)
  time.sleep(10)

humidifier = HumidifierV3()
controller = Controller([humidifier])

exiting = False
# run thread to process data from process's stdout
def stdout_loop():
  global process, controller, humidifier, humidifier2
  while not exiting:
    msg = utils.process.stdout.readline()
    if len(msg) <= 1:
      continue
    print("got message ", msg)
    try:
      msg_js = json.loads(msg)
      if "query_params" in msg_js:
        if msg_js["query_params"]:
          send_update({"params": {
            "target_lower": controller.target_lower,
            "target_upper": controller.target_upper,
            "feedforward_coeff": controller.feedforward_coeff,
            "manual_timeout": controller.manual_timeout,
            "manual_duration_s": controller.manual_duration,
            "manual_mode": 1.0 if controller.manual_mode else 0.0,
            "manual_hum_on": 1.0 if controller.manual_on else 0.0,

            "off_threshold_volts": humidifier.off_threshold,
            "on_threshold_volts": humidifier.on_threshold,
            "toggle_cooldown": humidifier.toggle_cooldown
            }
          })
      elif "set_params" in msg_js:
        if type(msg_js["set_params"]) is dict:
          set_params = msg_js["set_params"]
          if "name" in set_params and "value" in set_params:
            name, value = set_params["name"], set_params["value"]
            if type(value) is float:
              if name == "target_lower":
                controller.target_lower = value
              elif name == "target_upper":
                controller.target_upper = value
              elif name == "feedforward_coeff":
                controller.feedforward_coeff = value
              elif name == "manual_timeout":
                controller.manual_timeout = value
              elif name == "manual_duration_s":
                controller.manual_duration = value
              elif name == "off_threshold_volts":
                humidifier.off_threshold = value
              elif name == "on_threshold_volts":
                humidifier.on_threshold = value
              elif name == "toggle_cooldown":
                humidifier.toggle_cooldown = value
              elif name == "off_threshold_volts2":
                humidifier2.off_threshold = value
              elif name == "on_threshold_volts2":
                humidifier2.on_threshold = value
              elif name == "toggle_cooldown2":
                humidifier2.toggle_cooldown = value
      elif "manual_mode" in msg_js:
        controller.manual_mode = msg_js["manual_mode"]
        if controller.manual_mode:
          controller.manual_timeout = time.time() + controller.manual_duration
      elif "manual_mode_on" in msg_js:
        controller.manual_on = msg_js["manual_mode_on"]
    except json.JSONDecodeError as e:
      print("received bad json ", msg)
stdout_thread = threading.Thread(target=stdout_loop)
stdout_thread.start()


frame_num = 0
last_sample = 0
try:
  while True:
    now = time.time()
    if now - last_sample < SAMPLE_PERIOD:
      time.sleep(SAMPLE_PERIOD - (now - last_sample) + 0.001)
      continue
    last_sample = now

    #print("write s")
    s.write(b"s")
    s.flush()
    resp = s.read(120)
    #print("read", resp)
    if len(resp) == 0:
      reset_serial()
      time.sleep(5)
      continue
    parts = resp.split(b",")
    humidity = float(parts[0])
    temp = float(parts[1])
    volts = float(parts[2])
    #volts2 = float(parts[3])
    #print(parts)

    try:
      humidifier.update(volts)
      controller.update(s, humidity)

      if frame_num == 0:
        #print(humidity, temp, volts)
        update = {
            "data": {
              "time": int(now*1000),
              "temp": temp,
              "hum": humidity,
              "hv": volts,
              "hv2": 1 if humidifier.on else 0,
            }
        }
        send_update(update)
        #print("sending update {}".format(update))
      frame_num = (frame_num + 1) % DECIMATION_RATE
    except Exception as e:
      print("pipe errored out, restarting: ", e)
      # restart the process I guess
      exiting = True
      utils.process.kill()
      time.sleep(0.1)
      stdout_thread.join()
      exiting = False

      start_process()

      stdout_thread = threading.Thread(target=stdout_loop)
      stdout_thread.start()

finally:
  # kill ssh connection
  exiting = True
  utils.process.kill()
  stdout_thread.join()