#   Copyright 2012-2013 Eric Ptak - trouch.com
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.

from webiopi.decorators.rest import request, response
from webiopi.utils.types import M_JSON

class ADC():
    def __init__(self, channelCount, resolution, vref):
        self._analogCount = channelCount
        self._analogResolution = resolution
        self._analogMax = 2**resolution - 1
        self._analogRef = vref
    
    def __family__(self):
        return "ADC"

    def checkAnalogChannel(self, channel):
        if not 0 <= channel < self._analogCount:
            raise ValueError("Channel %d out of range [%d..%d]" % (channel, 0, self._analogCount-1))

    def checkAnalogValue(self, value):
        if not 0 <= value <= self._analogMax:
            raise ValueError("Value %d out of range [%d..%d]" % (value, 0, self._analogMax))
    
    @request("GET", "analog/count")
    @response("%d")
    def analogCount(self):
        return self._analogCount

    @request("GET", "analog/resolution")
    @response("%d")
    def analogResolution(self):
        return self._analogResolution
    
    @request("GET", "analog/max")
    @response("%d")
    def analogMaximum(self):
        return int(self._analogMax)
    
    @request("GET", "analog/vref")
    @response("%.2f")
    def analogReference(self):
        return self._analogRef
    
    def __analogRead__(self, channel, diff):
        raise NotImplementedError
    
    @request("GET", "analog/%(channel)d/integer")
    @response("%d")
    def analogRead(self, channel, diff=False):
        self.checkAnalogChannel(channel)
        return self.__analogRead__(channel, diff)
    
    @request("GET", "analog/%(channel)d/float")
    @response("%.2f")
    def analogReadFloat(self, channel, diff=False):
        return self.analogRead(channel, diff) / float(self._analogMax)
    
    @request("GET", "analog/%(channel)d/volt")
    @response("%.2f")
    def analogReadVolt(self, channel, diff=False):
        if self._analogRef == 0:
            raise NotImplementedError
        return self.analogReadFloat(channel, diff) * self._analogRef
    
    @request("GET", "analog/*/integer")
    @response(contentType=M_JSON)
    def analogReadAll(self):
        values = {}
        for i in range(self._analogCount):
            values[i] = self.analogRead(i)
        return values
            
    @request("GET", "analog/*/float")
    @response(contentType=M_JSON)
    def analogReadAllFloat(self):
        values = {}
        for i in range(self._analogCount):
            values[i] = float("%.2f" % self.analogReadFloat(i))
        return values
    
    @request("GET", "analog/*/volt")
    @response(contentType=M_JSON)
    def analogReadAllVolt(self):
        values = {}
        for i in range(self._analogCount):
            values[i] = float("%.2f" % self.analogReadVolt(i))
        return values
    
class DAC(ADC):
    def __init__(self, channelCount, resolution, vref):
        ADC.__init__(self, channelCount, resolution, vref)
    
    def __family__(self):
        return "DAC"
    
    def __analogWrite__(self, channel, value):
        raise NotImplementedError
    
    @request("POST", "analog/%(channel)d/integer/%(value)d")
    @response("%d")    
    def analogWrite(self, channel, value):
        self.checkAnalogChannel(channel)
        self.checkAnalogValue(value)
        self.__analogWrite__(channel, value)
        return self.analogRead(channel)
    
    @request("POST", "analog/%(channel)d/float/%(value)f")        
    @response("%.2f")    
    def analogWriteFloat(self, channel, value):
        self.analogWrite(channel, int(value * self._analogMax))
        return self.analogReadFloat(channel)
    
    @request("POST", "analog/%(channel)d/volt/%(value)f")        
    @response("%.2f")    
    def analogWriteVolt(self, channel, value):
        self.analogWriteFloat(channel, value /self._analogRef)
        return self.analogReadVolt(channel)
    

class PWM():
    def __init__(self, channelCount, resolution, frequency):
        self._pwmCount = channelCount
        self._pwmResolution = resolution
        self._pwmMax = 2**resolution - 1
        self.frequency = frequency
        self.period = 1.0/frequency
        
        # Futaba servos standard
        self.servo_neutral = 0.00152
        self.servo_travel_time = 0.0004
        self.servo_travel_angle = 45.0
        
        self.reverse = [False for i in range(channelCount)]
         
    def __family__(self):
        return "PWM"

    def checkPWMChannel(self, channel):
        if not 0 <= channel < self._pwmCount:
            raise ValueError("Channel %d out of range [%d..%d]" % (channel, 0, self._pwmCount-1))

    def checkPWMValue(self, value):
        if not 0 <= value <= self._pwmMax:
            raise ValueError("Value %d out of range [%d..%d]" % (value, 0, self._pwmMax))
    
    def __pwmRead__(self, channel):
        raise NotImplementedError
    
    def __pwmWrite__(self, channel, value):
        raise NotImplementedError
    
    @request("GET", "pwm/count")
    @response("%d")
    def pwmCount(self):
        return self._pwmCount

    @request("GET", "pwm/resolution")
    @response("%d")
    def pwmResolution(self):
        return self._pwmResolution
    
    @request("GET", "pwm/max")
    @response("%d")
    def pwmMaximum(self):
        return int(self._pwmMax)
    
    @request("GET", "pwm/%(channel)d/integer")
    @response("%d")
    def pwmRead(self, channel):
        self.checkPWMChannel(channel)
        return self.__pwmRead__(channel)
    
    @request("GET", "pwm/%(channel)d/float")
    @response("%.2f")
    def pwmReadFloat(self, channel):
        return self.pwmRead(channel) / float(self._pwmMax)
    
    @request("POST", "pwm/%(channel)d/integer/%(value)d")
    @response("%d")    
    def pwmWrite(self, channel, value):
        self.checkPWMChannel(channel)
        self.checkPWMValue(value)
        self.__pwmWrite__(channel, value)
        return self.pwmRead(channel)
    
    @request("POST", "pwm/%(channel)d/float/%(value)f")        
    @response("%.2f")    
    def pwmWriteFloat(self, channel, value):
        self.pwmWrite(channel, int(value * self._pwmMax))
        return self.pwmReadFloat(channel)
    
    def getReverse(self, channel):
        self.checkChannel(channel)
        return self.reverse[channel]
    
    def setReverse(self, channel, value):
        self.checkChannel(channel)
        self.reverse[channel] = value
        return value
    
    def RatioToAngle(self, value):
        f = value
        f *= self.period
        f -= self.servo_neutral
        f *= self.servo_travel_angle
        f /= self.servo_travel_time
        return f

    def AngleToRatio(self, value):
        f = value
        f *= self.servo_travel_time
        f /= self.servo_travel_angle
        f += self.servo_neutral
        f /= self.period
        return f
    
    @request("GET", "pwm/%(channel)d/angle")
    @response("%.2f")
    def pwmReadAngle(self, channel):
        f = self.pwmReadFloat(channel)
        f = self.RatioToAngle(f)
        if self.reverse[channel]:
            f = -f
        else:
            f = f
        return f
        
    @request("POST", "pwm/%(channel)d/angle/%(value)f")
    @response("%.2f")
    def pwmWriteAngle(self, channel, value):
        if self.reverse[channel]:
            f = -value
        else:
            f = value
        f = self.AngleToRatio(f)
        self.pwmWriteFloat(channel, f)
        return self.pwmReadAngle(channel)

    @request("GET", "pwm/*")
    @response(contentType=M_JSON)
    def pwmWildcard(self):
        values = {}
        for i in range(self._pwmCount):
            val = self.pwmReadFloat(i)
            values[i] = {}
            values[i]["float"] = float("%.2f" % val)
            values[i]["angle"] = float("%.2f" % self.RatioToAngle(val))
        return values
    
DRIVERS = {}
DRIVERS["ads1x1x"] = ["ADS1014", "ADS1015", "ADS1114", "ADS1115"]
DRIVERS["mcp3x0x"] = ["MCP3002", "MCP3004", "MCP3008", "MCP3204", "MCP3208"]
DRIVERS["mcp4725"] = ["MCP4725"]
DRIVERS["mcp48XX"] = ["MCP4802", "MCP4812", "MCP4822"]
DRIVERS["mcp492X"] = ["MCP4921", "MCP4922"]
DRIVERS["pca9685"] = ["PCA9685"]
DRIVERS["pcf8591"] = ["PCF8591"]

[(36, 'webiopi.decorators.rest.request', 'request', False, 'from webiopi.decorators.rest import request, response\n'), (37, 'webiopi.decorators.rest.response', 'response', False, 'from webiopi.decorators.rest import request, response\n'), (41, 'webiopi.decorators.rest.request', 'request', False, 'from webiopi.decorators.rest import request, response\n'), (42, 'webiopi.decorators.rest.response', 'response', False, 'from webiopi.decorators.rest import request, response\n'), (46, 'webiopi.decorators.rest.request', 'request', False, 'from webiopi.decorators.rest import request, response\n'), (47, 'webiopi.decorators.rest.response', 'response', False, 'from webiopi.decorators.rest import request, response\n'), (51, 'webiopi.decorators.rest.request', 'request', False, 'from webiopi.decorators.rest import request, response\n'), (52, 'webiopi.decorators.rest.response', 'response', False, 'from webiopi.decorators.rest import request, response\n'), (59, 'webiopi.decorators.rest.request', 'request', False, 'from webiopi.decorators.rest import request, response\n'), (60, 'webiopi.decorators.rest.response', 'response', False, 'from webiopi.decorators.rest import request, response\n'), (65, 'webiopi.decorators.rest.request', 'request', False, 'from webiopi.decorators.rest import request, response\n'), (66, 'webiopi.decorators.rest.response', 'response', False, 'from webiopi.decorators.rest import request, response\n'), (70, 'webiopi.decorators.rest.request', 'request', False, 'from webiopi.decorators.rest import request, response\n'), (71, 'webiopi.decorators.rest.response', 'response', False, 'from webiopi.decorators.rest import request, response\n'), (77, 'webiopi.decorators.rest.request', 'request', False, 'from webiopi.decorators.rest import request, response\n'), (78, 'webiopi.decorators.rest.response', 'response', False, 'from webiopi.decorators.rest import request, response\n'), (85, 'webiopi.decorators.rest.request', 'request', False, 'from webiopi.decorators.rest import request, response\n'), (86, 'webiopi.decorators.rest.response', 'response', False, 'from webiopi.decorators.rest import request, response\n'), (93, 'webiopi.decorators.rest.request', 'request', False, 'from webiopi.decorators.rest import request, response\n'), (94, 'webiopi.decorators.rest.response', 'response', False, 'from webiopi.decorators.rest import request, response\n'), (111, 'webiopi.decorators.rest.request', 'request', False, 'from webiopi.decorators.rest import request, response\n'), (112, 'webiopi.decorators.rest.response', 'response', False, 'from webiopi.decorators.rest import request, response\n'), (119, 'webiopi.decorators.rest.request', 'request', False, 'from webiopi.decorators.rest import request, response\n'), (120, 'webiopi.decorators.rest.response', 'response', False, 'from webiopi.decorators.rest import request, response\n'), (125, 'webiopi.decorators.rest.request', 'request', False, 'from webiopi.decorators.rest import request, response\n'), (126, 'webiopi.decorators.rest.response', 'response', False, 'from webiopi.decorators.rest import request, response\n'), (164, 'webiopi.decorators.rest.request', 'request', False, 'from webiopi.decorators.rest import request, response\n'), (165, 'webiopi.decorators.rest.response', 'response', False, 'from webiopi.decorators.rest import request, response\n'), (169, 'webiopi.decorators.rest.request', 'request', False, 'from webiopi.decorators.rest import request, response\n'), (170, 'webiopi.decorators.rest.response', 'response', False, 'from webiopi.decorators.rest import request, response\n'), (174, 'webiopi.decorators.rest.request', 'request', False, 'from webiopi.decorators.rest import request, response\n'), (175, 'webiopi.decorators.rest.response', 'response', False, 'from webiopi.decorators.rest import request, response\n'), (179, 'webiopi.decorators.rest.request', 'request', False, 'from webiopi.decorators.rest import request, response\n'), (180, 'webiopi.decorators.rest.response', 'response', False, 'from webiopi.decorators.rest import request, response\n'), (185, 'webiopi.decorators.rest.request', 'request', False, 'from webiopi.decorators.rest import request, response\n'), (186, 'webiopi.decorators.rest.response', 'response', False, 'from webiopi.decorators.rest import request, response\n'), (190, 'webiopi.decorators.rest.request', 'request', False, 'from webiopi.decorators.rest import request, response\n'), (191, 'webiopi.decorators.rest.response', 'response', False, 'from webiopi.decorators.rest import request, response\n'), (198, 'webiopi.decorators.rest.request', 'request', False, 'from webiopi.decorators.rest import request, response\n'), (199, 'webiopi.decorators.rest.response', 'response', False, 'from webiopi.decorators.rest import request, response\n'), (229, 'webiopi.decorators.rest.request', 'request', False, 'from webiopi.decorators.rest import request, response\n'), (230, 'webiopi.decorators.rest.response', 'response', False, 'from webiopi.decorators.rest import request, response\n'), (240, 'webiopi.decorators.rest.request', 'request', False, 'from webiopi.decorators.rest import request, response\n'), (241, 'webiopi.decorators.rest.response', 'response', False, 'from webiopi.decorators.rest import request, response\n'), (251, 'webiopi.decorators.rest.request', 'request', False, 'from webiopi.decorators.rest import request, response\n'), (252, 'webiopi.decorators.rest.response', 'response', False, 'from webiopi.decorators.rest import request, response\n')]