#
# See the LICENSE file
#

import datetime
import struct

import cartridge
import material

#
# CartridgeManager is used to create, encrypt and decrypt Stratasys cartridge
#
# Typical structure on the EEPROM
#        offset : len
#        0x00   : 0x08 - Canister serial number (double) (part of the key, written *on* the canister as S/N)
#        0x08   : 0x08 - Material type (double)
#        0x10   : 0x14 - Manufacturing lot (string)
#        0x24   : 0x02 - Version? (must be 1)
#        0x28   : 0x08 - Manufacturing date (date yymmddhhmmss)
#        0x30   : 0x08 - Use date (date yymmddhhmmss)
#        0x38   : 0x08 - Initial material quantity (double)
#        0x40   : 0x02 - Plain content CRC (uint16)
#        0x46   : 0x02 - Crypted content CRC (uint16)
#        0x48   : 0x08 - Key (unencrypted, 8 bytes)
#        0x50   : 0x02 - Key CRC (unencrypted, uint16)
#        0x58   : 0x08 - Current material quantity (double)
#        0x60   : 0x02 - Current material quantity crypted CRC (unencrypted, uint16)
#        0x62   : 0x02 - Current material quantity CRC (unencrypted, uint16)
#       ~~~~~~~~~~~~~
#       14 0x00: 0x48 - crypted/plaintext (start, len)
#       15 0x58: 0x10 - unknown, looks like DEX IV, but why?
#       16 0x48: 0x10 - ^

class Manager:
    def __init__(self, crypto, checksum):
        self.crypto = crypto
        self.checksum = checksum

    #
    # Encode a cartridge object into a data that can be burn onto a cartridge
    #
    def encode(self, machine_number, eeprom_uid, cartridge):
        cartridge_packed = self.pack(cartridge)
        cartridge_crypted = self.encrypt(machine_number, eeprom_uid, cartridge_packed)
        return cartridge_crypted

    #
    # Decode a eeprom to a cartridge object
    #
    def decode(self, machine_number, eeprom_uid, cartridge_crypted):
        cartridge_packed = self.decrypt(machine_number, eeprom_uid, cartridge_crypted)
        cartridge = self.unpack(cartridge_packed)
        return cartridge

    #
    # Pack a cartridge into a format suitable to be encrypted then burn
    # onto the cartridge EEPROM
    #
    def pack(self, cartridge):
        eeprom = bytearray(0x71)

        # serial number
        struct.pack_into("<d", eeprom, 0x0, cartridge.serial_number)
        # material id
        struct.pack_into("<d", eeprom, 0x08, material.get_id_from_name(cartridge.material_name))
        # manufacturing lot
        struct.pack_into("<20s", eeprom, 0x10, cartridge.manufacturing_lot)
        # version (not sure)
        struct.pack_into("<H", eeprom, 0x24, cartridge.version)
        # manufacturing date
        struct.pack_into("<HBBBBH", eeprom, 0x28,
                cartridge.manufacturing_date.year - 1900,
                cartridge.manufacturing_date.month,
                cartridge.manufacturing_date.day,
                cartridge.manufacturing_date.hour,
                cartridge.manufacturing_date.minute,
                cartridge.manufacturing_date.second)
        # last use date
        struct.pack_into("<HBBBBH", eeprom, 0x30,
                cartridge.use_date.year - 1900,
                cartridge.use_date.month,
                cartridge.use_date.day,