#  -*- coding: utf-8 -*-
# *****************************************************************************
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Module authors:
#   Markus Zolliker <markus.zolliker@psi.ch>
# *****************************************************************************
"""vector field"""

from frappy.core import Drivable, Done, BUSY, IDLE, WARN, ERROR
from frappy.errors import BadValueError
from frappy_psi.vector import Vector


DECREASE = 1
INCREASE = 2


class VectorField(Vector, Drivable):
    _state = None

    def doPoll(self):
        """periodically called method"""
        try:
            if self._starting:
                # first decrease components
                driving = False
                for target, component in zip(self.target, self.components):
                    if target * component.value < 0:
                        # change sign: drive to zero first
                        target = 0
                    if abs(target) < abs(component.target):
                        if target != component.target:
                            component.write_target(target)
                        if component.isDriving():
                            driving = True
                if driving:
                    return
                # now we can go to the final targets
                for target, component in zip(self.target, self.components):
                     component.write_target(target)
                self._starting = False
            else:
                for component in self.components:
                    if component.isDriving():
                        return
                self.setFastPoll(False)
        finally:
            super().doPoll()

    def merge_status(self):
        names = [c.name for c in self.components if c.status[0] >= ERROR]
        if names:
            return ERROR, 'error in %s' % ', '.join(names)
        names = [c.name for c in self.components if c.isDriving()]
        if self._state:
            # self.log.info('merge %r', [c.status for c in self.components])
            if names:
                direction = 'down ' if self._state == DECREASE else ''
                return BUSY, 'ramping %s%s' % (direction, ', '.join(names))
            if self.status[0] == BUSY:
                return self.status
            return BUSY, 'driving'
        if names:
            return WARN, 'moving %s directly' % ', '.join(names)
        names = [c.name for c in self.components if c.status[0] >= WARN]
        if names:
            return WARN, 'warnings in %s' % ', '.join(names)
        return IDLE, ''

    def write_target(self, value):
        """initiate target change"""
        # first make sure target is valid
        for target, component in zip(self.target, self.components):
            # check against limits if individual components
            component.check_limits(target)
        if sum(v * v for v in value) > 1:
            raise BadValueError('norm of vector too high')
        self.log.info('decrease')
        self.setFastPoll(True)
        self.target = value
        self._state = DECREASE
        self.doPoll()
        self.log.info('done write_target %r', value)
        return Done
