#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# System modules
import requests
from astropy.time import Time
from urllib.parse import urlencode

# Custom modules
from utils.logger import log
from devices import check

class Camera:
    def __init__(self, url):
        self.url = url
        self.addr = self.url
        self.timeout = 3
        self.error = []
        
    @check.request_errors
    def get(self, method, params=[]):
        res = requests.get(f"{self.addr}/{method}.cgi",
                           params="&".join(params),
                           timeout=self.timeout)
        #res.raise_for_status()
        value = res.text.split("\r\n")
        if not value[-1]:
            value = value[:-1]
        if len(value) == 1:
            value = value[0]

        try:
            int(value)
            return int(value)
        except:
            try:
                float(value)
                return float(value)
            except:
                return value

    @check.request_errors
    def put(self, method, params={}):
        res = requests.get(f"{self.addr}/{method}.cgi",
                           params=urlencode(params),
                           timeout=self.timeout)

        res.raise_for_status()
        text = res.text.split("\r\n")
        if not text[-1]:
            text = text[:-1]
        if len(text) == 1:
            text = text[0]
        return text


    def start(self, duration, frametype, datetime=Time.now().isot):
        params = {"Duration":duration,
                  "FrameType":frametype,
                  "DateTime":datetime}
        self.put("ImagerStartExposure", params=params)

    def abort(self):
        self.put("ImagerAbortExposure")

    def download(self):
        res = requests.get(f"{self.addr}/Imager.FIT")
        with open('temp.fits', 'wb') as f:
            f.write(res.content)
        #return fits.ImageHDU.fromstring(res.content)

    def full_frame(self):
        maxx,maxy = self.max_range
        self.xrange = [0, maxx]
        self.yrange = [0, maxy]
        self.xystart = [0,0]
        self.xyend = [maxx, maxy]
        return {"xrange":self.xrange, "yrange":self.yrange}

    def half_frame(self):
        maxx,maxy = self.max_range
        self.xrange = [maxx//4, maxx//4*3]
        self.yrange =  [maxy//4, maxy//4*3]
        self.xystart = [maxx//4, maxy//4]
        self.xyend = [maxx//4*3, maxy//4*3]
        return {"xrange":self.xrange, "yrange":self.yrange}

    def small_frame(self):
        maxx,maxy = self.max_range
        self.xrange = [maxx//20*19, maxx//20*21]
        self.yrange =  [maxy//20*19, maxy//20*21]
        self.xystart = [maxx//20*19, maxy//20*19]
        self.xyend = [maxx//20*21, maxy//20*21]
        return {"xrange":self.xrange, "yrange":self.yrange}

    @property
    def version(self):
        res = self.get("VersionNumbers")
        return res

    @property
    def description(self):
        res = self.get("Description")
        return res

    @property
    def state(self):
        res = self.get("ImagerState")
        return res

    @property
    def ready(self):
        res = self.get("ImagerImageReady")
        return res

    @property
    def all(self):
        params = ["AmbientTemperature",
                  "CCDTemperatureSetpoint",
                  "CCDTemperature",
                  "CoolerState",
                  "CoolerPower",
                  "CameraXSize",
                  "CameraYSize",
                  "BinX", "BinY",
                  "StartX", "StartY",
                  "NumX", "NumY"]
        res = self.get("ImagerGetSettings", params=params)
        
        all_dict = dict(zip(params,res))

        return all_dict

    @property
    def ambient(self):
        params = ["AmbientTemperature"]
        res = self.get("ImagerGetSettings", params=params)
        return res

    @property
    def max_range(self):
        params = ["CameraXSize", "CameraYSize"]
        res = self.get("ImagerGetSettings", params=params)
        res = list(map(int, res))
        binning = self.binning
        return [r//b for r,b in zip(res, binning)]

    @property
    def is_moving(self):
        res = self.get("FilterState")
        self._is_moving = res
        return self._is_moving

    @property
    def filter(self):
        params = ["CurrentFilter"] # , "CurrentFilterName"
        res = self.get("GetFilterSetting", params=params)
        self._filter = res
        return self._filter

    @filter.setter
    def filter(self, n):
        params =  {"NewPosition": n}
        res = self.put("ChangeFilter", params=params)
        self._filter = self.filter

    @property
    def binning(self):
        params = ["BinX", "BinY"]
        res = self.get("ImagerGetSettings", params=params)
        self.binning = list(map(int, res))
        return self._binning

    @binning.setter
    def binning(self, b):
        params =  {"BinX":b[0], "BinY":b[1]}
        res = self.put("ImagerSetSettings", params=params)
        self._binning = b

    @property
    def setpoint(self):
        params = ["CCDTemperatureSetpoint"]
        res = self.get("ImagerGetSettings", params=params)
        return res

    @property
    def temperature(self):
        params = ["CCDTemperature"]
        res = self.get("ImagerGetSettings", params=params)
        return res

    @temperature.setter
    def temperature(self, t):
        params =  {"CCDTemperatureSetpoint": t}
        res = self.put("ImagerSetSettings", params=params)
        return self.setpoint

    @property
    def cooler(self):
        params = ["CoolerState"]
        res = self.get("ImagerGetSettings", params=params)
        self.cooler = True if res else False
        return self._cooler

    @cooler.setter
    def cooler(self, b):
        s = "1" if b else "0"
        params =  {"CoolerState":s}
        res = self.put("ImagerSetSettings", params=params)
        self._cooler = b

    @property
    def xrange(self):
        params = ["StartX", "NumX", "BinX"] #start=20, width=100 (binning=1)
        res = self.get("ImagerGetSettings", params=params)    
        startx,numx,binx = list(map(int, res))
        #binx = self.binning[0] # binning is 2
        x_start = startx // binx
        x_end =  numx // binx + x_start
        self.xrange = [x_start, x_end] # start=10, end=60
        return self._xrange

    @xrange.setter
    def xrange(self, b): # start=10, end=60
        binx = self.binning[0] # binning is 2
        startx = b[0]*binx # start: 10*2=20
        numx =  b[1]*binx - startx # width: 60*2-startx=100
        params =  {"StartX":startx, "NumX":numx}
        res = self.put("ImagerSetSettings", params=params)
        self._xrange = b

    @property
    def yrange(self):
        params = ["StartY", "NumY", "BinY"] #start=20, height=100 (binning=1)
        res = self.get("ImagerGetSettings", params=params)    
        starty,numy,biny = list(map(int, res))
        #biny = self.binning[1] # binning is 2
        y_start = starty // biny
        y_end =  numy // biny + y_start
        self.yrange = [y_start, y_end] # start=10, end=60
        return self._yrange

    @yrange.setter
    def yrange(self, b): # start=10, end=60
        biny = self.binning[1] # binning is 2
        starty = b[0]*biny # start: 10*2=20
        numy =  b[1]*biny - starty # height: 60*2-starty=100
        params =  {"StartY":starty, "NumY":numy}
        res = self.put("ImagerSetSettings", params=params)
        self._yrange = b

    @property
    def xystart(self):
        params = ["StartX", "StartY", "BinX", "BinY"]
        res = self.get("ImagerGetSettings", params=params)    
        startx,starty,binx,biny = list(map(int, res))
        x_start = startx // binx
        y_start = starty // biny
        self.xystart = [x_start, y_start]
        return self._xystart

    @xystart.setter
    def xystart(self, b):
        params = ["StartX", "StartY", "NumX", "NumY", "BinX", "BinY"]
        res = self.get("ImagerGetSettings", params=params)    
        startx,starty,numx,numy,binx,biny = list(map(int, res))
        numx = numx + startx - b[0] * binx
        numy = numy + starty - b[1] * biny
        startx = int(b[0]) * binx
        starty = int(b[1]) * biny
        params = {"StartX":startx, "StartY":starty, "NumX":numx, "NumY":numy}
        res = self.put("ImagerSetSettings", params=params)
        self._xystart = b

    @property
    def xyend(self):
        params = ["StartX", "StartY", "NumX", "NumY", "BinX", "BinY"]
        res = self.get("ImagerGetSettings", params=params)    
        startx,starty,numx,numy,binx,biny = list(map(int, res))
        x_end = (startx + numx) // binx
        y_end = (starty + numy) // biny
        return [x_end, y_end]

    @xyend.setter
    def xyend(self, b):
        params = ["StartX", "StartY", "BinX", "BinY"]
        res = self.get("ImagerGetSettings", params=params)
        startx,starty,binx,biny = list(map(int, res))
        numx =  int(b[0]) * binx - startx
        numy =  int(b[1]) * biny - starty
        params = {"NumX":numx, "NumY":numy}
        res = self.put("ImagerSetSettings", params=params)
        self._xyend = b
                  
    @property
    def center(self):
        params = ["CameraXSize", "CameraYSize", "BinX", "BinY"]
        res = self.get("ImagerGetSettings", params=params)
        camx,camy,binx,biny = list(map(int, res))
        centx = camx//binx//2
        centy = camy//biny//2
        return [centx, centy]
