#!/bin/python3

# Sends pre-recorded footage to the server

import time
import websockets
import asyncio
import os
import functools

from utils import writeInFormat, readInFormat

apitoken = "secret_token 321"
deviceName = "fake001"
url = "wss://savox.protopaja.aalto.fi/api/websockets"
headers = {
  "apitoken": apitoken,
  "devicename": deviceName,
  "type": "device"
}

#2592x1944
w_hd = 640 #2592 #640
h_hd = 480 #1944 #480

#256x384
w_thermal = 256
h_thermal = 192 #192, 384
pictureDelay = 0.0

def loadDirs(dir):
  files = os.listdir(dir)
  files = [dir+"/"+f+"/" for f in files if not os.path.isfile(dir+'/'+f)]
  return files

def loadFolder(dir):
  files = os.listdir(dir)
  files = [f for f in files if os.path.isfile(dir+'/'+f)]
  return files

def readFootageInDir(dir):
  footageFiles = loadFolder(dir)
  imageFiles = [x for x in filter(lambda x: x.endswith(".jpg"), footageFiles)]
  thermFiles = [x for x in filter(lambda x: x.startswith("thermal_"), imageFiles)]
  hdFiles = [x for x in filter(lambda x: x.startswith("hd_"), imageFiles)]
  gpsFiles = [x for x in filter(lambda x: x.startswith("gps_"), footageFiles)]

  hdData = [[f, float(f[f.index('_')+1 : f.rindex('.')])] for f in hdFiles]
  thermData = [[f, float(f[f.index('_')+1 : f.rindex('.')])] for f in thermFiles]
  gpsData = [[f, float(f[f.index('_')+1 : f.rindex('.')])] for f in gpsFiles]

  hd_ts = [x[1] for x in hdData]
  therm_ts = [x[1] for x in thermData]
  gps_ts = [x[1] for x in gpsData]

  mint_hd, maxt_hd = min(hd_ts), max(hd_ts)
  mint_therm, maxt_therm = min(therm_ts), max(therm_ts)
  mint_gps, maxt_gps = min(gps_ts), max(gps_ts)

  min_ts, max_ts = max(mint_hd, mint_therm, mint_gps), min(maxt_hd, maxt_therm, maxt_gps)

  loop_time = max_ts - min_ts

  return hdData, thermData, gpsData, min_ts, max_ts, loop_time

directories = loadDirs("./fake_camera_footage")

def findClosestLessThan(ts, dataArr):
  return functools.reduce(lambda prev, curr: curr if curr[1] < ts and prev[1] < curr[1] else prev, [[dataArr[0][0], 0.0]] + dataArr)

fakeCameraCount = 0

class FakeCamera:
  def __init__(self, dir):
    self.hdData, self.thermData, self.gpsData, self.min_ts, self.max_ts, self.loop_time = readFootageInDir(dir)
    self.start_time = time.time()
    self.dir = dir
    global fakeCameraCount
    self.deviceName = "Camera " + str(fakeCameraCount + 1)
    fakeCameraCount += 1

  def getCurrentTimestamp(self):
    t = time.time()
    return (t - self.start_time) % self.loop_time + self.min_ts

  def readCameraHDBuffer(self):
    ts = self.getCurrentTimestamp()
    filename = self.dir + findClosestLessThan(ts, self.hdData)[0]
    with open(filename, 'rb') as f:
      data = f.read()
    return data

  def readCameraThermalBuffer(self):
    ts = self.getCurrentTimestamp()
    filename = self.dir + findClosestLessThan(ts, self.thermData)[0]
    with open(filename, 'rb') as f:
      data = f.read()
    return data

  def readGps(self):
    ts = self.getCurrentTimestamp()
    filename = self.dir + findClosestLessThan(ts, self.gpsData)[0]
    with open(filename, 'r') as f:
      data = f.read()
    return data.split(" ")

async def sendData():
  global fakeCameraCount
  fakeCameraCount = 0
  fakeCameras = [FakeCamera(dir) for dir in directories]

  while True:
    try:
      async with websockets.connect(url, extra_headers = headers, ping_timeout=1, close_timeout=1) as ws:
        print("Connected to server, sending data...")
        while True:
          for fakeCamera in fakeCameras:
            therm_data = fakeCamera.readCameraThermalBuffer()
            hd_data = fakeCamera.readCameraHDBuffer()
            gps_data = fakeCamera.readGps()

            data = bytearray()
            data += writeInFormat("devicename", bytes(fakeCamera.deviceName, "utf-8"))
            data += writeInFormat("therm_data", therm_data)
            data += writeInFormat("hd_data", hd_data)
            data += writeInFormat("therm_w", w_thermal.to_bytes(4, 'big'))
            data += writeInFormat("therm_h", h_thermal.to_bytes(4, 'big'))
            data += writeInFormat("hd_w", w_hd.to_bytes(4, 'big'))
            data += writeInFormat("hd_h", h_hd.to_bytes(4, 'big'))
            data += writeInFormat("gps_lat", bytes(gps_data[0], 'utf-8'))
            data += writeInFormat("gps_long", bytes(gps_data[1], 'utf-8'))

            try:
              await ws.send(data)
            except:
              print("Could not send data")

            await asyncio.wait_for(ws.recv(), 0.2)
          time.sleep(pictureDelay)
    except websockets.ConnectionClosed as e:
      print("Connection closed, trying to reconnect...")
      continue

if __name__ == '__main__':
  while True:
    try:
      loop = asyncio.get_event_loop()
      loop.run_until_complete(sendData())
      loop.run_forever()
    except KeyboardInterrupt:
      break
    except Exception as e:
      print("Exception:", e)
      print("Attempting to reconnect in 1 second.")
      time.sleep(1)
