class Action(QObject):
    """! @brief
    Binding between the card reader and the database.
    @author CASAS Jacky
    @date 22.06.14
    @version 1.0
    """
    ## Signal used to display the action status on the UI
    status = Signal(str)
    ## Signal used to display the last transactions on the UI
    transactionsLoaded = Signal(list)

    def __init__(self, pifacecontrol):
        """! @brief Initialize the instance and get the two collections of the database.
        @param self the Action instance
        """
        QObject.__init__(self)
        ## contains a link to the database collection *user*
        self.users = DataBase().user
        ## contains a link to the database collection *transaction*
        self.transactions = DataBase().transaction

        self.piFace = pifacecontrol
        self.piFace.activateButtonListener()

    def transaction(self, deviceId, amount):
        """! @brief Method to make a transaction (withdraw an amount from an account).
        @param self the Action instance
        @param deviceId identifier of the device (smartcard or smartphone)
        @param amount amount to withdraw
        """
        canWithdraw = False
        user = self.users.find_one({"devices.uid": deviceId})
        for device in user['devices']:
            if device['uid'] == deviceId:
                if device['status'] == STA_DEVICE_ACTIVE:
                    if amount > 0:
                        if user['balance'] >= amount:    
                            canWithdraw = True
                        else:
                            self.status.emit('Please recharge your account, you don\'t have enough money in it.')
                    else:
                        self.status.emit('The amount must be greater than 0.')
                elif device['status'] == STA_DEVICE_LOST:
                    self.status.emit('Impossible transaction : Lost device.')
                elif device['status'] == STA_DEVICE_STOLEN:
                    self.status.emit('Impossible transaction : Stolen device.')
                elif device['status'] == STA_DEVICE_DELETED:
                    self.status.emit('Impossible transaction : Deleted device.')
                else:
                    self.status.emit('Impossible transaction : Device status unknown.')

                if canWithdraw:
                    # create transaction
                    userId = user['uid']
                    dispenserId = DISPENSER_ID
                    transactionDate = datetime.now()

                    transaction = {"userId":userId, "deviceId":deviceId, "dispenserId":dispenserId, "transactionType":WITHDRAWAL, "amount":amount, "transactionDate":transactionDate}
                    self.transactions.insert(transaction)

                    # debit credit in account
                    query = {"uid": user['uid'] }
                    update = {"$inc": { "balance": -amount}}
                    self.users.update(query, update)

                    self.status.emit('You withdraw ' + `amount` + ' CHF. You have now ' + `user['balance'] - amount` + ' CHF on your account.')
                    self.piFace.actionValidated()
                else:
                    self.piFace.actionDenied()
                break

    def recharge(self, deviceId, amount):
        """! @brief Method to make a recharge (put an amount in an account).
        @param self the Action instance
        @param deviceId identifier of the device (smartcard or smartphone)
        @param amount amount to recharge
        """
        canRecharge = False
        user = self.users.find_one({"devices.uid": deviceId})
        for device in user['devices']:
            if device['uid'] == deviceId:
                if device['status'] == STA_DEVICE_ACTIVE:
                    if amount > 0:  
                        canRecharge = True
                    else:
                        self.status.emit('The amount must be greater than 0.')
                elif device['status'] == STA_DEVICE_LOST:
                    self.status.emit('Impossible recharge : Lost device.')
                elif device['status'] == STA_DEVICE_STOLEN:
                    self.status.emit('Impossible recharge : Stolen device.')
                elif device['status'] == STA_DEVICE_DELETED:
                    self.status.emit('Impossible recharge : Deleted device.')
                else:
                    self.status.emit('Impossible recharge : Device status unknown.')

                if canRecharge:
                    # create transaction
                    userId = user['uid']
                    dispenserId = DISPENSER_ID
                    transactionDate = datetime.now()

                    transaction = {"userId":userId, "deviceId":deviceId, "dispenserId":dispenserId, "transactionType":RECHARGE, "amount":amount, "transactionDate":transactionDate}
                    self.transactions.insert(transaction)

                    # recharge account
                    query = {"uid": user['uid'] }
                    update = {"$inc": { "balance": amount}}
                    self.users.update(query, update)

                    self.status.emit('Recharge of ' + `amount` + ' CHF. You have now ' + `user['balance'] + amount` + ' CHF on your account.')
                    self.piFace.actionValidated()
                else:
                    self.piFace.actionDenied()
                break
            
    def getAccount(self, deviceId):
        """! @brief Method to get an account in a JSON format.
        @param self the Action instance
        @param deviceId identifier of the device (smartcard or smartphone)
        """
        user = self.users.find_one({"devices.uid": deviceId})
        if user is None:
            return None
        else:
            return user

    def getLastTransactions(self, deviceId):
        """! @brief Method to get the last 10 transactions.
        @param self the Action instance
        @param deviceId identifier of the device (smartcard or smartphone)
        """
        user = self.users.find_one({"devices.uid": deviceId})
        transactions = self.transactions.find(
                {'userId': user['uid']}, 
                {'_id': 0, 'amount': 1, 'transactionType': 1, 'transactionDate': 1, 'deviceId': 1, 'dispenserId': 1}
            ).limit(10).sort("transactionDate", -1)
        trs = []
        for transaction in transactions:
            tr = []
            tr.append(transaction['transactionDate'].strftime("%a %e %b %Y, %H:%M:%S"))
            if transaction['transactionType'] == RECHARGE:
                tr.append('+' + `transaction['amount']`)
            elif transaction['transactionType'] == WITHDRAWAL:
                tr.append('-' + `transaction['amount']`)
            tr.append('CHF')
            trs.append(tr)
        self.transactionsLoaded.emit(trs)

    def addUser(self, username, name=None, surname=None):
        """! @brief Method to create a new user account.
        @param self the Action instance
        @param username unique username
        @param name name of the user, optional
        @param surname surname of the user, optional
        """
        if not username:
            self.status.emit('Please enter at least a username.')
        else:
            userAlreadyRegistered = self.users.find_one({'username':username})
            if userAlreadyRegistered is None:
                uid = str(uuid4())
                balance = 0
                registrationDate = datetime.now()
                statement = STA_USER_ACTIVE
                devices = []

                if name is None:
                    if surname is None:
                        user = {"uid":uid, "username":username, "balance":balance, "registrationDate":registrationDate, "statement":statement, "devices":devices}
                    else:
                        user = {"uid":uid, "username":username, "surname":surname, "balance":balance, "registrationDate":registrationDate, "statement":statement, "devices":devices}
                else:
                    if surname is None:
                        user = {"uid":uid, "username":username, "name":name, "balance":balance, "registrationDate":registrationDate, "statement":statement, "devices":devices}
                    else:
                        user = {"uid":uid, "username":username, "name":name, "surname":surname, "balance":balance, "registrationDate":registrationDate, "statement":statement, "devices":devices}

                self.users.insert(user)
                self.status.emit('User successfully added to the database.')
                self.piFace.actionValidated()
            else:
                self.status.emit('User already registered.')

    def addDevice(self, username, deviceId, ATR):
        """! @brief Method to add a device to an account.
        @param self the Action instance
        @param username username of the account owner
        @param deviceId identifier of the device (smartcard or smartphone)
        @param ATR ATR of the card
        """
        if deviceId is None or ATR is None:
            self.status.emit('Please place a card in front of the reader.')
        else:
            if not username:
                self.status.emit('Please enter a username.')
            else:
                userWithDevice = self.users.find_one({"devices.uid": deviceId})
                if userWithDevice is None:
                    user = self.users.find_one({"username": username})

                    if user is None:
                        self.status.emit('This username doesn\'t exist.')
                    else:
                        status = STA_DEVICE_ACTIVE
                        activationDate = datetime.now()
                        category = 'smartcard'

                        # create the new device
                        newDevice = {"uid":deviceId, "status":status, "activationDate":activationDate, "ATR":ATR, "category":category}
                        # add the new device to the list
                        user['devices'].append(newDevice)
                        # remove the _id from user (required to make a $set)
                        del user['_id']
                        
                        # update user
                        query = {"uid": user['uid'] }
                        update = { "$set": user }
                        self.users.update(query, update)

                        self.status.emit('Device added to the user ' + user['name'] + ' ' + user['surname'] + '.')
                        self.piFace.actionValidated()
                else:
                    self.status.emit('This device already belongs to someone.')