1# -*- coding: windows-1252 -*-
2
3from struct import unpack, pack
4from . import BIFFRecords
5from .compat import xrange
6
7class StrCell(object):
8    __slots__ = ["rowx", "colx", "xf_idx", "sst_idx"]
9
10    def __init__(self, rowx, colx, xf_idx, sst_idx):
11        self.rowx = rowx
12        self.colx = colx
13        self.xf_idx = xf_idx
14        self.sst_idx = sst_idx
15
16    def get_biff_data(self):
17        # return BIFFRecords.LabelSSTRecord(self.rowx, self.colx, self.xf_idx, self.sst_idx).get()
18        return pack('<5HL', 0x00FD, 10, self.rowx, self.colx, self.xf_idx, self.sst_idx)
19
20class BlankCell(object):
21    __slots__ = ["rowx", "colx", "xf_idx"]
22
23    def __init__(self, rowx, colx, xf_idx):
24        self.rowx = rowx
25        self.colx = colx
26        self.xf_idx = xf_idx
27
28    def get_biff_data(self):
29        # return BIFFRecords.BlankRecord(self.rowx, self.colx, self.xf_idx).get()
30        return pack('<5H', 0x0201, 6, self.rowx, self.colx, self.xf_idx)
31
32class MulBlankCell(object):
33    __slots__ = ["rowx", "colx1", "colx2", "xf_idx"]
34
35    def __init__(self, rowx, colx1, colx2, xf_idx):
36        self.rowx = rowx
37        self.colx1 = colx1
38        self.colx2 = colx2
39        self.xf_idx = xf_idx
40
41    def get_biff_data(self):
42        return BIFFRecords.MulBlankRecord(self.rowx,
43            self.colx1, self.colx2, self.xf_idx).get()
44
45class NumberCell(object):
46    __slots__ = ["rowx", "colx", "xf_idx", "number"]
47
48    def __init__(self, rowx, colx, xf_idx, number):
49        self.rowx = rowx
50        self.colx = colx
51        self.xf_idx = xf_idx
52        self.number = float(number)
53
54    def get_encoded_data(self):
55        rk_encoded = 0
56        num = self.number
57
58        # The four possible kinds of RK encoding are *not* mutually exclusive.
59        # The 30-bit integer variety picks up the most.
60        # In the code below, the four varieties are checked in descending order
61        # of bangs per buck, or not at all.
62        # SJM 2007-10-01
63
64        if -0x20000000 <= num < 0x20000000: # fits in 30-bit *signed* int
65            inum = int(num)
66            if inum == num: # survives round-trip
67                # print "30-bit integer RK", inum, hex(inum)
68                rk_encoded = 2 | (inum << 2)
69                return 1, rk_encoded
70
71        temp = num * 100
72
73        if -0x20000000 <= temp < 0x20000000:
74            # That was step 1: the coded value will fit in
75            # a 30-bit signed integer.
76            itemp = int(round(temp, 0))
77            # That was step 2: "itemp" is the best candidate coded value.
78            # Now for step 3: simulate the decoding,
79            # to check for round-trip correctness.
80            if itemp / 100.0 == num:
81                # print "30-bit integer RK*100", itemp, hex(itemp)
82                rk_encoded = 3 | (itemp << 2)
83                return 1, rk_encoded
84
85        if 0: # Cost of extra pack+unpack not justified by tiny yield.
86            packed = pack('<d', num)
87            w01, w23 = unpack('<2i', packed)
88            if not w01 and not(w23 & 3):
89                # 34 lsb are 0
90                # print "float RK", w23, hex(w23)
91                return 1, w23
92
93            packed100 = pack('<d', temp)
94            w01, w23 = unpack('<2i', packed100)
95            if not w01 and not(w23 & 3):
96                # 34 lsb are 0
97                # print "float RK*100", w23, hex(w23)
98                return 1, w23 | 1
99
100        #print "Number"
101        #print
102        return 0, pack('<5Hd', 0x0203, 14, self.rowx, self.colx, self.xf_idx, num)
103
104    def get_biff_data(self):
105        isRK, value = self.get_encoded_data()
106        if isRK:
107            return pack('<5Hi', 0x27E, 10, self.rowx, self.colx, self.xf_idx, value)
108        return value # NUMBER record already packed
109
110class BooleanCell(object):
111    __slots__ = ["rowx", "colx", "xf_idx", "number"]
112
113    def __init__(self, rowx, colx, xf_idx, number):
114        self.rowx = rowx
115        self.colx = colx
116        self.xf_idx = xf_idx
117        self.number = number
118
119    def get_biff_data(self):
120        return BIFFRecords.BoolErrRecord(self.rowx,
121            self.colx, self.xf_idx, self.number, 0).get()
122
123error_code_map = {
124    0x00:  0, # Intersection of two cell ranges is empty
125    0x07:  7, # Division by zero
126    0x0F: 15, # Wrong type of operand
127    0x17: 23, # Illegal or deleted cell reference
128    0x1D: 29, # Wrong function or range name
129    0x24: 36, # Value range overflow
130    0x2A: 42, # Argument or function not available
131    '#NULL!' :  0, # Intersection of two cell ranges is empty
132    '#DIV/0!':  7, # Division by zero
133    '#VALUE!': 36, # Wrong type of operand
134    '#REF!'  : 23, # Illegal or deleted cell reference
135    '#NAME?' : 29, # Wrong function or range name
136    '#NUM!'  : 36, # Value range overflow
137    '#N/A!'  : 42, # Argument or function not available
138}
139
140class ErrorCell(object):
141    __slots__ = ["rowx", "colx", "xf_idx", "number"]
142
143    def __init__(self, rowx, colx, xf_idx, error_string_or_code):
144        self.rowx = rowx
145        self.colx = colx
146        self.xf_idx = xf_idx
147        try:
148            self.number = error_code_map[error_string_or_code]
149        except KeyError:
150            raise Exception('Illegal error value (%r)' % error_string_or_code)
151
152    def get_biff_data(self):
153        return BIFFRecords.BoolErrRecord(self.rowx,
154            self.colx, self.xf_idx, self.number, 1).get()
155
156class FormulaCell(object):
157    __slots__ = ["rowx", "colx", "xf_idx", "frmla", "calc_flags"]
158
159    def __init__(self, rowx, colx, xf_idx, frmla, calc_flags=0):
160        self.rowx = rowx
161        self.colx = colx
162        self.xf_idx = xf_idx
163        self.frmla = frmla
164        self.calc_flags = calc_flags
165
166    def get_biff_data(self):
167        return BIFFRecords.FormulaRecord(self.rowx,
168            self.colx, self.xf_idx, self.frmla.rpn(), self.calc_flags).get()
169
170# module-level function for *internal* use by the Row module
171
172def _get_cells_biff_data_mul(rowx, cell_items):
173    # Return the BIFF data for all cell records in the row.
174    # Adjacent BLANK|RK records are combined into MUL(BLANK|RK) records.
175    pieces = []
176    nitems = len(cell_items)
177    i = 0
178    while i < nitems:
179        icolx, icell = cell_items[i]
180        if isinstance(icell, NumberCell):
181            isRK, value = icell.get_encoded_data()
182            if not isRK:
183                pieces.append(value) # pre-packed NUMBER record
184                i += 1
185                continue
186            muldata = [(value, icell.xf_idx)]
187            target = NumberCell
188        elif isinstance(icell, BlankCell):
189            muldata = [icell.xf_idx]
190            target = BlankCell
191        else:
192            pieces.append(icell.get_biff_data())
193            i += 1
194            continue
195        lastcolx = icolx
196        j = i
197        packed_record = ''
198        for j in xrange(i+1, nitems):
199            jcolx, jcell = cell_items[j]
200            if jcolx != lastcolx + 1:
201                nexti = j
202                break
203            if not isinstance(jcell, target):
204                nexti = j
205                break
206            if target == NumberCell:
207                isRK, value = jcell.get_encoded_data()
208                if not isRK:
209                    packed_record = value
210                    nexti = j + 1
211                    break
212                muldata.append((value, jcell.xf_idx))
213            else:
214                muldata.append(jcell.xf_idx)
215            lastcolx = jcolx
216        else:
217            nexti = j + 1
218        if target == NumberCell:
219            if lastcolx == icolx:
220                # RK record
221                value, xf_idx = muldata[0]
222                pieces.append(pack('<5Hi', 0x027E, 10, rowx, icolx, xf_idx, value))
223            else:
224                # MULRK record
225                nc = lastcolx - icolx + 1
226                pieces.append(pack('<4H', 0x00BD, 6 * nc + 6, rowx, icolx))
227                pieces.append(b''.join(pack('<Hi', xf_idx, value) for value, xf_idx in muldata))
228                pieces.append(pack('<H', lastcolx))
229        else:
230            if lastcolx == icolx:
231                # BLANK record
232                xf_idx = muldata[0]
233                pieces.append(pack('<5H', 0x0201, 6, rowx, icolx, xf_idx))
234            else:
235                # MULBLANK record
236                nc = lastcolx - icolx + 1
237                pieces.append(pack('<4H', 0x00BE, 2 * nc + 6, rowx, icolx))
238                pieces.append(b''.join(pack('<H', xf_idx) for xf_idx in muldata))
239                pieces.append(pack('<H', lastcolx))
240        if packed_record:
241            pieces.append(packed_record)
242        i = nexti
243    return b''.join(pieces)
244
245