# Copyright 2015 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from . import util

# Code for parsing the various Dalvik opcode formats
INSTRUCTION_FORMAT = util.keysToRanges({
    0x00: '10x',
    0x01: '12x',
    0x02: '22x',
    0x03: '32x',
    0x04: '12x',
    0x05: '22x',
    0x06: '32x',
    0x07: '12x',
    0x08: '22x',
    0x09: '32x',
    0x0a: '11x',
    0x0b: '11x',
    0x0c: '11x',
    0x0d: '11x',
    0x0e: '10x',
    0x0f: '11x',
    0x10: '11x',
    0x11: '11x',
    0x12: '11n',
    0x13: '21s',
    0x14: '31i',
    0x15: '21h',
    0x16: '21s',
    0x17: '31i',
    0x18: '51l',
    0x19: '21h',
    0x1a: '21c',
    0x1b: '31c',
    0x1c: '21c',
    0x1d: '11x',
    0x1e: '11x',
    0x1f: '21c',
    0x20: '22c',
    0x21: '12x',
    0x22: '21c',
    0x23: '22c',
    0x24: '35c',
    0x25: '3rc',
    0x26: '31t',
    0x27: '11x',
    0x28: '10t',
    0x29: '20t',
    0x2a: '30t',
    0x2b: '31t',
    0x2c: '31t',
    0x2d: '23x',
    0x32: '22t',
    0x38: '21t',
    0x3e: '10x',
    0x44: '23x',
    0x52: '22c',
    0x60: '21c',
    0x6e: '35c',
    0x73: '10x',
    0x74: '3rc',
    0x79: '10x',
    0x7b: '12x',
    0x90: '23x',
    0xb0: '12x',
    0xd0: '22s',
    0xd8: '22b',
    0xe3: '10x',
}, 256)

# parsing funcs
def p00op(w): return []
def pBAop(w): return [(w >> 8) & 0xF, w >> 12]
def pAAop(w): return [w >> 8]
def p00opAAAA(w, w2): return [w2]
def pAAopBBBB(w, w2): return [w >> 8, w2]
def pAAopCCBB(w, w2): return [w >> 8, w2 & 0xFF, w2 >> 8]
def pBAopCCCC(w, w2): return [(w >> 8) & 0xF, w >> 12, w2]
def p00opAAAAAAAA(w, w2, w3): return [w2 ^ (w3 << 16)]
def p00opAAAABBBB(w, w2, w3): return [w2, w3]
def pAAopBBBBBBBB(w, w2, w3): return [w >> 8, w2 ^ (w3 << 16)]

def pAGopBBBBFEDC(w, w2, w3):
    a = w >> 12
    c, d, e, f = (w3) & 0xF, (w3 >> 4) & 0xF, (w3 >> 8) & 0xF, (w3 >> 12) & 0xF
    g = (w >> 8) & 0xF
    return [w2, [c, d, e, f, g][:a]]

def pAAopBBBBCCCC(w, w2, w3):
    a = w >> 8
    return [w2, range(w3, w3+a)]

def pAAopBBBBBBBBBBBBBBBB(w, w2, w3, w4, w5):
    b = w2 ^ (w3 << 16) ^ (w4 << 32) ^ (w5 << 48)
    return [w >> 8, b]

_FUNC = {
    '10x': p00op,
    '12x': pBAop,
    '11n': pBAop,
    '11x': pAAop,
    '10t': pAAop,
    '20t': p00opAAAA,
    '22x': pAAopBBBB,
    '21t': pAAopBBBB,
    '21s': pAAopBBBB,
    '21h': pAAopBBBB,
    '21c': pAAopBBBB,
    '23x': pAAopCCBB,
    '22b': pAAopCCBB,
    '22t': pBAopCCCC,
    '22s': pBAopCCCC,
    '22c': pBAopCCCC,
    '30t': p00opAAAAAAAA,
    '32x': p00opAAAABBBB,
    '31i': pAAopBBBBBBBB,
    '31t': pAAopBBBBBBBB,
    '31c': pAAopBBBBBBBB,
    '35c': pAGopBBBBFEDC,
    '3rc': pAAopBBBBCCCC,
    '51l': pAAopBBBBBBBBBBBBBBBB,
}

def sign(x, bits):
    if x >= (1 << (bits-1)):
        x -= 1 << bits
    return x

def decode(shorts, pos, opcode):
    fmt = INSTRUCTION_FORMAT[opcode]
    size = int(fmt[0])
    results = _FUNC[fmt](*shorts[pos:pos+size])
    # Check if we need to sign extend
    if fmt[2] == 'n':
        results[-1] = sign(results[-1], 4)
    elif fmt[2] == 'b' or (fmt[2] == 't' and size == 1):
        results[-1] = sign(results[-1], 8)
    elif fmt[2] == 's' or (fmt[2] == 't' and size == 2):
        results[-1] = sign(results[-1], 16)
    elif fmt[2] == 't' and size == 3:
        results[-1] = sign(results[-1], 32)

    # Hats depend on actual size expected, so we rely on opcode as a hack
    if fmt[2] == 'h':
        assert opcode == 0x15 or opcode == 0x19
        results[-1] = results[-1] << (16 if opcode == 0x15 else 48)

    # Convert code offsets to actual code position
    if fmt[2] == 't':
        results[-1] += pos
    return pos + size, results
