from __future__ import division
import serial

try:
    from config import plotter_port, plotter_baudrate, plotter_timeout, plotter_center_at_origin
except ImportError:
    plotter_port = lambda: '/dev/ttyUSB0'
    plotter_baudrate = lambda: 9600
    plotter_timeout = lambda: 15
    plotter_center_at_origin = lambda: False


class Plotter:

    def __init__(self, boundaries=None):
        self.boundaries = boundaries
        self.p0incenter = plotter_center_at_origin()
        if not boundaries:
            s = self.getoutput(b'OW;')
            print(s)
            if not s:
                self.ser = None
            else:
                self.boundaries = tuple(int(x) for x in "".join(s).split(","))

    def getoutput(self, outstr):
        try:
            self.ser = serial.Serial(
                plotter_port(),
                baudrate=plotter_baudrate(),
                timeout=plotter_timeout()
            )
            print('try to get Status')
            if not self.ser:
                print('Plotter not available')
                return None
            self.ser.write(outstr)
            print('device busy')
            s = []
            while True:
                x = self.ser.read()
                if x == b"\x0d" or not x:
                    break
                s.append(x)
            return b''.join(s).decode()
        except OSError:
            return None

    @property
    def xmin(self):
        return self.boundaries[0]

    @property
    def ymin(self):
        return self.boundaries[1]

    @property
    def xmax(self):
        return self.boundaries[2]

    @property
    def ymax(self):
        return self.boundaries[3]

    @property
    def center(self):
        return (self.xmin + self.xmax) / 2, (self.ymin + self.ymax) / 2

    def write(self, programm):
        self.ser.write(str(programm).encode())

    @property
    def winsize(self):
        return self.xmax - self.xmin, self.ymax - self.ymin

    def oob(self, prog):
        return (prog.xmax > self.xmax or prog.xmin < self.xmin or
                prog.ymin < self.ymin or prog.ymax > self.ymax)

    def full(self, prog):
        """Scale program to fit plotter bounds."""
        arg = min(self.winsize[0] / prog.winsize[0], self.winsize[1] / prog.winsize[1])
        return prog * arg

    def plot(self, prog):
        if self.ready and (not self.oob(prog)):
            self.write(prog)
        if self.oob(prog):
            print('programm out of bound')
        if not self.ready:
            print('device not ready')

    def centralize(self, prog):
        return prog - prog.center if self.p0incenter else prog - (prog.xmin, prog.ymin)

    @property
    def ready(self):
        return bool(self.getoutput('OS;'))
