#BEGIN_LEGAL
#
#Copyright (c) 2017 Intel Corporation
#
#  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.
#  
#END_LEGAL

import re
import ild_nt
import mbuild
import ild_codegen
import operand_storage
import genutil
import ildutil
import ild_info

_easz_token = 'EASZ'
_easz_binding_pattern = re.compile(r'EASZ=(?P<rhs>[0-9]+)')

#FIXME: we can get default NT by looking at the spine
_easz_default_nt = 'ASZ_NONTERM'

_easz_lookup_def_str = 'XED_ILD_EASZ_LOOKUP'

_easz_defines = {
                 'XED_ILD_EASZ_0' : 0,
                 'XED_ILD_EASZ_1' : 1,
                 'XED_ILD_EASZ_2' : 2,
                 'XED_ILD_EASZ_3' : 3,
                 }
#set up LOOKUP define to be the biggest defined value
_easz_defines[_easz_lookup_def_str] = len(_easz_defines)

#reverted _eosz_defines
_easz_defines_reverse = dict((v,k) for k, v in _easz_defines.items())

_easz_c_fn = 'xed-ild-easz.c'
_easz_header_fn = 'xed-ild-easz.h'

def get_getter_fn(ptrn_list):
    if len(ptrn_list) == 0:
        l1_fn = '(%s)0' % (ildutil.ild_getter_typename)
    first = ptrn_list[0]
    for cur in ptrn_list[1:]:
        if first.easz_nt_seq != cur.easz_nt_seq:
            #conflict in easz resolution functions.. should not happen
            return None 
    return ild_codegen.get_derived_op_getter_fn(first.easz_nt_seq, _easz_token)

def is_easz_conflict(info_list):
    """
    Return True/False if info list conflicts
    on EASZ resolution function (EOSZ NT sequence). 
    """
    first_info = info_list[0]
    for cur_info in info_list[1:]:
        if first_info.easz_nt_seq != cur_info.easz_nt_seq:
            return True
    return False

def gen_getter_fn_lookup(agi, united_lookup, easz_dict): # NOT USED
    """Compute L1(conflict resolution) functions list and easz 
    lookup tables dict.
    @param agi: all generators info
    
    @param united_lookup: the 2D lookup by map-opcode to info objects list.
    united_lookup['0x0']['0x78'] == [ild_info1, ild_info2, ... ]
    @type united_lookup: 
    {string(insn_map) : {string(opcode): [ild_info.ild_info_t]} }
    
    
    """
    l1_lookup = {}
    for insn_map in united_lookup.get_maps():
        l1_lookup[insn_map] = {}
        for opcode in range(0, 256):
            info_list = united_lookup.get_info_list(insn_map, hex(opcode))
            #get only info objects with minimum priority
            info_list = ild_info.get_min_prio_list(info_list)
            is_conflict = False
            if len(info_list) > 1:
                is_conflict = is_easz_conflict(info_list)
            
            if is_conflict:
                l1_fn = None
            #if map-opcode pair is undefined the lookup function ptr is NULL
            #this will happen for opcodes like 0F in 0F map - totally illegal
            #opcodes, that should never be looked up in runtime.
            elif len(info_list) == 0:
                l1_fn = '(%s)0' % (ildutil.l1_ptr_typename)
            else:
                #there are no conflicts, we can use the eosz_nt_seq
                #function
                info = info_list[0]
                l1_fn = ild_nt.get_lufn(info.easz_nt_seq, _easz_token)
            l1_lookup[insn_map][hex(opcode)] = l1_fn
    return l1_lookup


#returns a list of names of EASZ-binding NTs in the pattern
def get_easz_nt_seq(ptrn_wrds, easz_nts):
    return ild_nt.get_nt_seq(ptrn_wrds, easz_nts, 
                             implied_nt=_easz_default_nt)


#returns a list of all sequences of EOSZ setting NTs in patterns
#each sequence is a list of strings (NT names)
def get_all_easz_seq(united_lookup):
    all_seq = set()
    for info in united_lookup.get_all_infos():
        #lists are unhaashable, hence we have to use tuples instead
        all_seq.add(tuple(info.easz_nt_seq))
    #convert back to lists, in order not to surprise user
    return_list = []
    for nt_tuple in all_seq:
        return_list.append(list(nt_tuple))
    return return_list

#Parameters: agi - all generator info object
#returns a list of names of NTs that bind EASZ operand
def get_easz_binding_nts(agi):
    return ild_nt.get_setting_nts(agi, _easz_token)

def get_target_opname():
    return _easz_token

def get_ntseq_header_fn():
    return _easz_header_fn

#dumps the xed_ild_easz.c file that defines all lookup functions
#for EASZ resolution
#FIXME: should dump header file too
def work(agi, united_lookup, easz_nts, ild_gendir, debug):
    
    #dump lookup tables for each NT
    #just for debugging
    nt_arrays = []
    for nt_name in easz_nts:
        array = ild_nt.gen_nt_lookup(agi, nt_name, 'EASZ')
        if not array:
            return
        nt_arrays.append(array)
    ild_nt.dump_lu_arrays(agi, nt_arrays, 'ild_easz_debug.txt',
                          'ild_easz_debug_header.txt')
        
    all_easz_seq = get_all_easz_seq(united_lookup)
    debug.write('EASZ SEQS: %s\n' % all_easz_seq)

    nt_seq_arrays = {}
    for nt_seq in all_easz_seq:
        array = ild_nt.gen_nt_seq_lookup(agi, nt_seq, _easz_token)
        if not array:
            return
        nt_seq_arrays[tuple(nt_seq)] = array
    #init function calls all single init functions for the created tables
    init_f = ild_nt.gen_init_function(list(nt_seq_arrays.values()),
                                       'xed_ild_easz_init')
    ild_nt.dump_lu_arrays(agi, list(nt_seq_arrays.values()), _easz_c_fn, 
                          mbuild.join('include-private', _easz_header_fn),
                          init_f)
    getter_fos = []
    for names in list(nt_seq_arrays.keys()):
        arr = nt_seq_arrays[names]
        getter_fo = ild_codegen.gen_derived_operand_getter(agi, _easz_token,
                                                           arr, list(names))
        getter_fos.append(getter_fo)
    
    headers = [ildutil.ild_private_header,
               _easz_header_fn, 
               operand_storage.get_operand_accessors_fn()]
    ild_codegen.dump_flist_2_header(agi, 'xed-ild-easz-getters.h',
                                    headers, 
                                    getter_fos)
    
    #getter_lookup = gen_getter_fn_lookup(agi, united_lookup, nt_seq_arrays)
    
    return nt_seq_arrays

