# *****************************************************************************
# 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:
#   Anik Stark <anik.stark@psi.ch>
# *****************************************************************************

import time
from frappy.core import Parameter, Writable, Attached, EnumType, FloatRange, \
    IDLE, WARN


STATES = {'manual': 0,
          'high_pressure': 1,
          'circulating': 2,
          'warmup': 3,
          }


class JTCCR(Writable):

    compressor = Attached()
    ccrcompressor = Attached()

    value = Parameter('current state', datatype=EnumType(STATES), default=0)
    target = Parameter('target state', datatype=EnumType(STATES), default=0)
    #p1min = Property('lower limit to switch to high pressure mode', dataype=FloatRange(unit='bar'), default=1.8)
    p1max = Parameter('limit to switch to circulating mode', datatype=FloatRange(unit='bar'),
                      readonly=False, default=2.2)
    p2min = Parameter('lower limit to turn compressor off', datatype=FloatRange(unit='bar'),
                      readonly=False, default=0.12)
    p2max = Parameter('upper limit to turn compressor on', datatype=FloatRange(unit='bar'),
                      readonly=False, default=0.8)
    #p2lim = Property('do not start compressor if p2 is below this value', datatype=FloatRange(unit='bar'), default=0.15)
    pdifmax = Parameter('max pressure difference of compressor', datatype=FloatRange(unit='bar'),
                        readonly=False, default=5.0)
    pdifmargin = Parameter('safety margin for pressure difference of compressor',
                           datatype=FloatRange(unit='bar'),
                           readonly=False, default=1.0)
    p3margin = Parameter('start compressor when p3 is below pressreg setpoint plus this value',
                        datatype=FloatRange(unit='bar'),
                         readonly=False, default=0.01)
    p3reg = Parameter('pressure regulation setpoint', datatype=FloatRange(unit='bar'), default=4.0)
    plow = Parameter('pressure below 5K', datatype=FloatRange(unit='bar'),
                     readonly=False, default=4.0)

    valves_high_pressure = {
        'close': 'v3 v4 v5 v6 v7 v8 v10',
        'open': 'v1 v2 v9 jtpump',  # vm
    }
    valves_circulating = {
        'close': 'v3 v4 v5 v6 v7 v9 v10',
        'open': 'v1 v2 v8',  # vm
    }
    valves_warmup = {
        'close': 'v6 v7 v8 v9 v10',
        'open': 'v1 v2 v3 v4 v5',  # vm
    }
    valves_security= {
        'open': '',
        'close': 'v1 v9'
    }
    valves_overpressure = {
        'open': 'v10',
        'close': ''
    }

    _start_ccr = None

    def write_target(self, target):
        if self.value != target:
            self.log.info('set mode %r', target)
            self.set_mode(target)
        return target

    def set_mode(self, state):
        if state == 'high_pressure':
            self.p3reg = 12
            self.handle_valves(**self.valves_high_pressure)
        elif state == 'circulating':
            self.p3reg = self.plow
            self.handle_valves(**self.valves_circulating)
        elif state == 'warmup':
            self.handle_valves(**self.valves_warmup)
            self.ccrcompressor(False)
        elif state != 'manual':
            self.log.error('unknown state %r', state)
        self.value = state

    def security_settings(self):
        self.compressor.write_target(False)
        self.handle_valves(**self.valves_security)
        self.set_mode('manual')

    def handle_valves(self, close=(), open=()):
        """set given valves. raises ImpossibleError, when checks fails"""
        self.log.info('handle_valves %r %r', close, open)
        self._valves_to_wait_for = {}
        self._valves_failed = {True: [], False: []}
        for flag, valves in enumerate([close, open]):
            for vname in valves.split():
                valve = self.secNode.modules[vname]
                self.log.info('set valve %s to %r', vname, flag)
                valve.write_target(flag)
        # TODO: do we need to wait for motor valve?

    def doPoll(self):
        p1 = self.secNode.modules['p1'].read_value()
        p2 = self.secNode.modules['p2'].read_value()
        p3 = self.secNode.modules['p3'].read_value()
        p4 = self.secNode.modules['p4'].read_value()
        compressor_state = self.compressor.read_value()
        if self.value == 'manual':
            return self.set_mode('manual')
        if p3 >= p2 + self.pdifmax + self.pdifmargin:
            # overpressure protection
            self.security_settings()
            self.status = WARN, 'overpressure: He recovery output closed?'
            return
        if p2 < self.p2min and p3 < self.p3reg and self.value != 'high_pressure':
            # underpressure protection
            self.security_settings()
            self.status = WARN, 'underpressure: not enough He'
            return
        if self.value == 'circulating':
            if p3 < self.p3reg and not compressor_state:
                self.compressor.write_target(True)
            elif (p3 - p2) > self.pdifmax and compressor_state:
                self.compressor.write_target(False)
            # TODO: do we need to skip overpressure protection for one time?
        if self.value == 'high_pressure':
            if not self.ccrcompressor.read_value():
                if p4 > self.p3reg * 0.99:
                    if self._start_ccr is None:
                        self._start_ccr = time.time() + 900  # 15 min
                        self.status = IDLE, 'flush a little before cooling'
                    elif time.time() > self._start_ccr:
                        self.ccrcompressor.write_target(True)
                        self._start_ccr = None
                        self.status = IDLE, 'cool down'
            elif p1 > self.p1max:
                self.set_mode('circulating')
            else:
                self.status = IDLE, 'cool down'
        if p2 > self.p2max and not compressor_state:
            self.compressor.write_target(True)
        elif p2 < self.p2min and compressor_state:
            self.compressor.write_target(False)
        if (p3 - p2) >= self.pdifmax + 0.1:
            self.handle_valves(**self.valves_overpressure)
            self.status = WARN, 'release to recovery'
        elif self.secNode.modules['v10'].read_value():
            self.secNode.modules['v10'].write_target(False)
            self.status = IDLE, 'release finished'
