#   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, 5, 36, 35), 'webiopi.decorators.rest.request', 'request', ({(36, 13, 36, 18): '"""GET"""', (36, 20, 36, 34): '"""analog/count"""'}, {}), "('GET', 'analog/count')", False, 'from webiopi.decorators.rest import request, response\n'), ((37, 5, 37, 19), 'webiopi.decorators.rest.response', 'response', ({(37, 14, 37, 18): '"""%d"""'}, {}), "('%d')", False, 'from webiopi.decorators.rest import request, response\n'), ((41, 5, 41, 40), 'webiopi.decorators.rest.request', 'request', ({(41, 13, 41, 18): '"""GET"""', (41, 20, 41, 39): '"""analog/resolution"""'}, {}), "('GET', 'analog/resolution')", False, 'from webiopi.decorators.rest import request, response\n'), ((42, 5, 42, 19), 'webiopi.decorators.rest.response', 'response', ({(42, 14, 42, 18): '"""%d"""'}, {}), "('%d')", False, 'from webiopi.decorators.rest import request, response\n'), ((46, 5, 46, 33), 'webiopi.decorators.rest.request', 'request', ({(46, 13, 46, 18): '"""GET"""', (46, 20, 46, 32): '"""analog/max"""'}, {}), "('GET', 'analog/max')", False, 'from webiopi.decorators.rest import request, response\n'), ((47, 5, 47, 19), 'webiopi.decorators.rest.response', 'response', ({(47, 14, 47, 18): '"""%d"""'}, {}), "('%d')", False, 'from webiopi.decorators.rest import request, response\n'), ((51, 5, 51, 34), 'webiopi.decorators.rest.request', 'request', ({(51, 13, 51, 18): '"""GET"""', (51, 20, 51, 33): '"""analog/vref"""'}, {}), "('GET', 'analog/vref')", False, 'from webiopi.decorators.rest import request, response\n'), ((52, 5, 52, 21), 'webiopi.decorators.rest.response', 'response', ({(52, 14, 52, 20): '"""%.2f"""'}, {}), "('%.2f')", False, 'from webiopi.decorators.rest import request, response\n'), ((59, 5, 59, 49), 'webiopi.decorators.rest.request', 'request', ({(59, 13, 59, 18): '"""GET"""', (59, 20, 59, 48): '"""analog/%(channel)d/integer"""'}, {}), "('GET', 'analog/%(channel)d/integer')", False, 'from webiopi.decorators.rest import request, response\n'), ((60, 5, 60, 19), 'webiopi.decorators.rest.response', 'response', ({(60, 14, 60, 18): '"""%d"""'}, {}), "('%d')", False, 'from webiopi.decorators.rest import request, response\n'), ((65, 5, 65, 47), 'webiopi.decorators.rest.request', 'request', ({(65, 13, 65, 18): '"""GET"""', (65, 20, 65, 46): '"""analog/%(channel)d/float"""'}, {}), "('GET', 'analog/%(channel)d/float')", False, 'from webiopi.decorators.rest import request, response\n'), ((66, 5, 66, 21), 'webiopi.decorators.rest.response', 'response', ({(66, 14, 66, 20): '"""%.2f"""'}, {}), "('%.2f')", False, 'from webiopi.decorators.rest import request, response\n'), ((70, 5, 70, 46), 'webiopi.decorators.rest.request', 'request', ({(70, 13, 70, 18): '"""GET"""', (70, 20, 70, 45): '"""analog/%(channel)d/volt"""'}, {}), "('GET', 'analog/%(channel)d/volt')", False, 'from webiopi.decorators.rest import request, response\n'), ((71, 5, 71, 21), 'webiopi.decorators.rest.response', 'response', ({(71, 14, 71, 20): '"""%.2f"""'}, {}), "('%.2f')", False, 'from webiopi.decorators.rest import request, response\n'), ((77, 5, 77, 39), 'webiopi.decorators.rest.request', 'request', ({(77, 13, 77, 18): '"""GET"""', (77, 20, 77, 38): '"""analog/*/integer"""'}, {}), "('GET', 'analog/*/integer')", False, 'from webiopi.decorators.rest import request, response\n'), ((78, 5, 78, 33), 'webiopi.decorators.rest.response', 'response', (), '', False, 'from webiopi.decorators.rest import request, response\n'), ((85, 5, 85, 37), 'webiopi.decorators.rest.request', 'request', ({(85, 13, 85, 18): '"""GET"""', (85, 20, 85, 36): '"""analog/*/float"""'}, {}), "('GET', 'analog/*/float')", False, 'from webiopi.decorators.rest import request, response\n'), ((86, 5, 86, 33), 'webiopi.decorators.rest.response', 'response', (), '', False, 'from webiopi.decorators.rest import request, response\n'), ((93, 5, 93, 36), 'webiopi.decorators.rest.request', 'request', ({(93, 13, 93, 18): '"""GET"""', (93, 20, 93, 35): '"""analog/*/volt"""'}, {}), "('GET', 'analog/*/volt')", False, 'from webiopi.decorators.rest import request, response\n'), ((94, 5, 94, 33), 'webiopi.decorators.rest.response', 'response', (), '', False, 'from webiopi.decorators.rest import request, response\n'), ((111, 5, 111, 60), 'webiopi.decorators.rest.request', 'request', ({(111, 13, 111, 19): '"""POST"""', (111, 21, 111, 59): '"""analog/%(channel)d/integer/%(value)d"""'}, {}), "('POST', 'analog/%(channel)d/integer/%(value)d')", False, 'from webiopi.decorators.rest import request, response\n'), ((112, 5, 112, 19), 'webiopi.decorators.rest.response', 'response', ({(112, 14, 112, 18): '"""%d"""'}, {}), "('%d')", False, 'from webiopi.decorators.rest import request, response\n'), ((119, 5, 119, 58), 'webiopi.decorators.rest.request', 'request', ({(119, 13, 119, 19): '"""POST"""', (119, 21, 119, 57): '"""analog/%(channel)d/float/%(value)f"""'}, {}), "('POST', 'analog/%(channel)d/float/%(value)f')", False, 'from webiopi.decorators.rest import request, response\n'), ((120, 5, 120, 21), 'webiopi.decorators.rest.response', 'response', ({(120, 14, 120, 20): '"""%.2f"""'}, {}), "('%.2f')", False, 'from webiopi.decorators.rest import request, response\n'), ((125, 5, 125, 57), 'webiopi.decorators.rest.request', 'request', ({(125, 13, 125, 19): '"""POST"""', (125, 21, 125, 56): '"""analog/%(channel)d/volt/%(value)f"""'}, {}), "('POST', 'analog/%(channel)d/volt/%(value)f')", False, 'from webiopi.decorators.rest import request, response\n'), ((126, 5, 126, 21), 'webiopi.decorators.rest.response', 'response', ({(126, 14, 126, 20): '"""%.2f"""'}, {}), "('%.2f')", False, 'from webiopi.decorators.rest import request, response\n'), ((164, 5, 164, 32), 'webiopi.decorators.rest.request', 'request', ({(164, 13, 164, 18): '"""GET"""', (164, 20, 164, 31): '"""pwm/count"""'}, {}), "('GET', 'pwm/count')", False, 'from webiopi.decorators.rest import request, response\n'), ((165, 5, 165, 19), 'webiopi.decorators.rest.response', 'response', ({(165, 14, 165, 18): '"""%d"""'}, {}), "('%d')", False, 'from webiopi.decorators.rest import request, response\n'), ((169, 5, 169, 37), 'webiopi.decorators.rest.request', 'request', ({(169, 13, 169, 18): '"""GET"""', (169, 20, 169, 36): '"""pwm/resolution"""'}, {}), "('GET', 'pwm/resolution')", False, 'from webiopi.decorators.rest import request, response\n'), ((170, 5, 170, 19), 'webiopi.decorators.rest.response', 'response', ({(170, 14, 170, 18): '"""%d"""'}, {}), "('%d')", False, 'from webiopi.decorators.rest import request, response\n'), ((174, 5, 174, 30), 'webiopi.decorators.rest.request', 'request', ({(174, 13, 174, 18): '"""GET"""', (174, 20, 174, 29): '"""pwm/max"""'}, {}), "('GET', 'pwm/max')", False, 'from webiopi.decorators.rest import request, response\n'), ((175, 5, 175, 19), 'webiopi.decorators.rest.response', 'response', ({(175, 14, 175, 18): '"""%d"""'}, {}), "('%d')", False, 'from webiopi.decorators.rest import request, response\n'), ((179, 5, 179, 46), 'webiopi.decorators.rest.request', 'request', ({(179, 13, 179, 18): '"""GET"""', (179, 20, 179, 45): '"""pwm/%(channel)d/integer"""'}, {}), "('GET', 'pwm/%(channel)d/integer')", False, 'from webiopi.decorators.rest import request, response\n'), ((180, 5, 180, 19), 'webiopi.decorators.rest.response', 'response', ({(180, 14, 180, 18): '"""%d"""'}, {}), "('%d')", False, 'from webiopi.decorators.rest import request, response\n'), ((185, 5, 185, 44), 'webiopi.decorators.rest.request', 'request', ({(185, 13, 185, 18): '"""GET"""', (185, 20, 185, 43): '"""pwm/%(channel)d/float"""'}, {}), "('GET', 'pwm/%(channel)d/float')", False, 'from webiopi.decorators.rest import request, response\n'), ((186, 5, 186, 21), 'webiopi.decorators.rest.response', 'response', ({(186, 14, 186, 20): '"""%.2f"""'}, {}), "('%.2f')", False, 'from webiopi.decorators.rest import request, response\n'), ((190, 5, 190, 57), 'webiopi.decorators.rest.request', 'request', ({(190, 13, 190, 19): '"""POST"""', (190, 21, 190, 56): '"""pwm/%(channel)d/integer/%(value)d"""'}, {}), "('POST', 'pwm/%(channel)d/integer/%(value)d')", False, 'from webiopi.decorators.rest import request, response\n'), ((191, 5, 191, 19), 'webiopi.decorators.rest.response', 'response', ({(191, 14, 191, 18): '"""%d"""'}, {}), "('%d')", False, 'from webiopi.decorators.rest import request, response\n'), ((198, 5, 198, 55), 'webiopi.decorators.rest.request', 'request', ({(198, 13, 198, 19): '"""POST"""', (198, 21, 198, 54): '"""pwm/%(channel)d/float/%(value)f"""'}, {}), "('POST', 'pwm/%(channel)d/float/%(value)f')", False, 'from webiopi.decorators.rest import request, response\n'), ((199, 5, 199, 21), 'webiopi.decorators.rest.response', 'response', ({(199, 14, 199, 20): '"""%.2f"""'}, {}), "('%.2f')", False, 'from webiopi.decorators.rest import request, response\n'), ((229, 5, 229, 44), 'webiopi.decorators.rest.request', 'request', ({(229, 13, 229, 18): '"""GET"""', (229, 20, 229, 43): '"""pwm/%(channel)d/angle"""'}, {}), "('GET', 'pwm/%(channel)d/angle')", False, 'from webiopi.decorators.rest import request, response\n'), ((230, 5, 230, 21), 'webiopi.decorators.rest.response', 'response', ({(230, 14, 230, 20): '"""%.2f"""'}, {}), "('%.2f')", False, 'from webiopi.decorators.rest import request, response\n'), ((240, 5, 240, 55), 'webiopi.decorators.rest.request', 'request', ({(240, 13, 240, 19): '"""POST"""', (240, 21, 240, 54): '"""pwm/%(channel)d/angle/%(value)f"""'}, {}), "('POST', 'pwm/%(channel)d/angle/%(value)f')", False, 'from webiopi.decorators.rest import request, response\n'), ((241, 5, 241, 21), 'webiopi.decorators.rest.response', 'response', ({(241, 14, 241, 20): '"""%.2f"""'}, {}), "('%.2f')", False, 'from webiopi.decorators.rest import request, response\n'), ((251, 5, 251, 28), 'webiopi.decorators.rest.request', 'request', ({(251, 13, 251, 18): '"""GET"""', (251, 20, 251, 27): '"""pwm/*"""'}, {}), "('GET', 'pwm/*')", False, 'from webiopi.decorators.rest import request, response\n'), ((252, 5, 252, 33), 'webiopi.decorators.rest.response', 'response', (), '', False, 'from webiopi.decorators.rest import request, response\n')]