# HG changeset patch
# User Patrick Lang <patrick.lang@hotmail.com>
# Date 1420151967 28800
#      Thu Jan 01 14:39:27 2015 -0800
# Node ID a54fca6875d18b5b7be644735e6c9ad7c42b4352
# Parent  1d6fb56e2220b39b4b23239a9edfb3afa965f4b1
[FT-60] Feature Request: Memory Bank Support #1945

diff -r 1d6fb56e2220 -r a54fca6875d1 chirp/ft60.py
--- a/chirp/ft60.py	Sun Dec 28 23:53:43 2014 -0800
+++ b/chirp/ft60.py	Thu Jan 01 14:39:27 2015 -0800
@@ -17,6 +17,7 @@
 from chirp import chirp_common, yaesu_clone, memmap, bitwise, directory
 from chirp import errors
 from textwrap import dedent
+from collections import defaultdict
 
 ACK = "\x06"
 
@@ -148,10 +149,94 @@
      unknown2:7;
 } names[1000];
 
+#seekto 0x69c8;
+struct {
+   u8 bitmap[128];
+} bank_channels[10];
+
 #seekto 0x6FC8;
 u8 checksum;
 """
 
+class FT60BankModel(chirp_common.MTOBankModel):
+    """Memory Bank Model for FT60"""
+    # Banks are numbered 1-10 on the radio
+    # Each memory can be in zero or more banks
+
+    def __init__(self, radio):
+        super(FT60BankModel, self).__init__(radio)
+        self.__b2m_cache = defaultdict(list)
+        self.__m2b_cache = defaultdict(list)
+
+    def __precache(self):
+        if self.__b2m_cache:
+            return
+
+        for bank in self.get_mappings():
+            self.__b2m_cache[bank.index] = self._get_bank_memories(bank)
+            for memnum in self.__b2m_cache[bank.index]:
+                self.__m2b_cache[memnum].append(bank.index)
+
+    def _get_bank_memories(self, bank):
+        # Returns array of memory indexes (1-indexed) contained in bank
+        memories = []
+        upper = self._radio.get_features().memory_bounds[1]
+        for i in range(0, upper+1):
+            _bitmap = self._radio._memobj.bank_channels[bank.index].bitmap[i/8]
+            ishft = i % 8 #7 - (i % 8)
+            if _bitmap & (1 << ishft):
+                memories.append(i + 1)
+        return memories
+
+    def get_num_mappings(self):
+        return 10 # equal to size of bank_channels in MEM_FORMAT
+
+    def get_mappings(self):
+        # mappings always exist for banks 1-10, even if empty.  Just return list of all banks
+        banks = []
+        for i in range(0, self.get_num_mappings()):
+            bank = chirp_common.Bank(self, "%i" % i, "BANK-%i" % (i + 1))
+            bank.index = i
+            banks.append(bank)
+        return banks
+
+    def add_memory_to_mapping(self, memory, bank):
+        self.__precache()
+
+        index = memory.number - 1
+        _bitmap = self._radio._memobj.bank_channels[bank.index]
+        ishft = index % 8
+        _bitmap.bitmap[index / 8] |= (1 << ishft)
+        self.__m2b_cache[memory.number].append(bank.index)
+        self.__b2m_cache[bank.index].append(memory.number)
+
+    def remove_memory_from_mapping(self, memory, bank):
+        self.__precache()
+
+        index = memory.number - 1
+        _bitmap = self._radio._memobj.bank_channels[bank.index]
+        ishft = index % 8
+        if not (_bitmap.bitmap[index / 8] & (1 << ishft)):
+            raise Exception("Memory {num} is " +
+                            "not in bank {bank}".format(num=memory.number,
+                                                           bank=bank))
+        _bitmap.bitmap[index / 8] &= ~(1 << ishft)
+        self.__b2m_cache[bank.index].remove(memory.number)
+        self.__m2b_cache[memory.number].remove(bank.index)
+
+    def get_mapping_memories(self, bank):
+        self.__precache()
+
+        return [self._radio.get_memory(n)
+                for n in self.__b2m_cache[bank.index]]
+
+    def get_memory_mappings(self, memory):
+        self.__precache()
+
+        _banks = self.get_mappings()
+        return [_banks[b] for b in self.__m2b_cache[memory.number]]
+
+
 DUPLEX = ["", "", "-", "+", "split"]
 TMODES = ["", "Tone", "TSQL", "TSQL-R", "DTCS"]
 POWER_LEVELS = [chirp_common.PowerLevel("High", watts=5.0),
@@ -206,10 +291,12 @@
         rf.valid_bands = [(108000000, 520000000), (700000000, 999990000)]
         rf.can_odd_split = True
         rf.has_ctone = False
-        rf.has_bank = False
+        rf.has_bank = True
         rf.has_dtcs_polarity = False
+	return rf
 
-        return rf
+    def get_bank_model(self):
+        return FT60BankModel(self)
 
     def _checksums(self):
         return [ yaesu_clone.YaesuChecksum(0x0000, 0x6FC7) ]
