#!/usr/bin/env python
#Copyright (c) 2011, Sam Gambrell
#Licensed under the Simplified BSD License.
#NOTE: This code has not been thoroughly tested and may not function as advertised.
#   Please report and findings to the author so that they may be addressed in a stable release.

from . import csafe_dic
import logging

def __int2bytes(numbytes, integer):
    if not 0 <= integer <= 2 ** (8 * numbytes):
        logging.warning("Integer is outside the allowable range",UserWarning)

    byte = []
    for k in range(numbytes):
        calcbyte = (integer >> (8 * k)) & 0xFF
        byte.append(calcbyte)

    return byte

def __bytes2int(raw_bytes):
    num_bytes = len(raw_bytes)
    integer = 0

    for k in range(num_bytes):
        integer = (raw_bytes[k] << (8 * k)) | integer

    return integer

def __bytes2ascii(raw_bytes):
    word = ""
    for letter in raw_bytes:
        word += chr(letter)

    return word

#for sending
def write(arguments):

    #priming variables
    i = 0
    message = []
    wrapper = 0
    wrapped = []
    maxresponse = 3 #start & stop flag & status

    #loop through all arguments
    while i < len(arguments):

        arg = arguments[i]
        cmdprop = csafe_dic.cmds[arg]
        command = []

        #load variables if command is a Long Command
        if len(cmdprop[1]) != 0:
            for varbytes in cmdprop[1]:
                i += 1
                intvalue = arguments[i]
                value = __int2bytes(varbytes, intvalue)
                command.extend(value)

            #data byte count
            cmdbytes = len(command)
            command.insert(0, cmdbytes)

        #add command id
        command.insert(0, cmdprop[0])

        #closes wrapper if required
        if len(wrapped) > 0 and (len(cmdprop) < 3 or cmdprop[2] != wrapper):
            wrapped.insert(0, len(wrapped)) #data byte count for wrapper
            wrapped.insert(0, wrapper) #wrapper command id
            message.extend(wrapped) #adds wrapper to message
            wrapped = []
            wrapper = 0

        #create or extend wrapper
        if len(cmdprop) == 3: #checks if command needs a wrapper
            if wrapper == cmdprop[2]: #checks if currently in the same wrapper
                wrapped.extend(command)
            else: #creating a new wrapper
                wrapped = command
                wrapper = cmdprop[2]
                maxresponse += 2

            command = [] #clear command to prevent it from getting into message

        #max message length
        cmdid = cmdprop[0] | (wrapper << 8)
        #double return to account for stuffing
        maxresponse += abs(sum(csafe_dic.resp[cmdid][1])) * 2 + 1

        #add completed command to final message
        message.extend(command)

        i += 1

    #closes wrapper if message ended on it
    if len(wrapped) > 0:
        wrapped.insert(0, len(wrapped)) #data byte count for wrapper
        wrapped.insert(0, wrapper) #wrapper command id
        message.extend(wrapped) #adds wrapper to message

    #prime variables
    checksum = 0x0
    j = 0

    #checksum and byte stuffing
    while j < len(message):
        #calculate checksum
        checksum = checksum ^ message[j]

        #byte stuffing
        if 0xF0 <= message[j] <= 0xF3:
            message.insert(j, csafe_dic.Byte_Stuffing_Flag)
            j += 1
            message[j] = message[j] & 0x3

        j += 1

    #add checksum to end of message
    message.append(checksum)

    #start & stop frames
    message.insert(0, csafe_dic.Standard_Frame_Start_Flag)
    message.append(csafe_dic.Stop_Frame_Flag)

    #check for frame size (96 bytes)
    if len(message) > 96:
        logging.warning("Message is too long: " + len(message),UserWarning)

    #report IDs
    maxmessage = max(len(message) + 1, maxresponse)

    if maxmessage <= 21:
        message.insert(0, 0x01)
        message += [0] * (21 - len(message))
    elif maxmessage <= 63:
        message.insert(0, 0x04)
        message += [0] * (63 - len(message))
    elif (len(message) + 1) <= 121:
        message.insert(0, 0x02)
        message += [0] * (121 - len(message))
        if maxresponse > 121:
            logging.warning("Response may be too long to receive.  Max possible length " + str(maxresponse),UserWarning)
    else: