'''This is the Android implementatoin of NFC Scanning using the
built in NFC adapter of some android phones.
'''

from kivy.app import App
from kivy.clock import Clock
#Detect which platform we are on
from kivy.utils import platform
if platform != 'android':
    raise ImportError
import threading

from electrum_gui.kivy.nfc_scanner import NFCBase
from jnius import autoclass, cast
from android.runnable import run_on_ui_thread
from android import activity

BUILDVERSION = autoclass('android.os.Build$VERSION').SDK_INT
NfcAdapter = autoclass('android.nfc.NfcAdapter')
PythonActivity = autoclass('org.kivy.android.PythonActivity')
JString = autoclass('java.lang.String')
Charset = autoclass('java.nio.charset.Charset')
locale = autoclass('java.util.Locale')
Intent = autoclass('android.content.Intent')
IntentFilter = autoclass('android.content.IntentFilter')
PendingIntent = autoclass('android.app.PendingIntent')
Ndef = autoclass('android.nfc.tech.Ndef')
NdefRecord = autoclass('android.nfc.NdefRecord')
NdefMessage = autoclass('android.nfc.NdefMessage')

app = None



class ScannerAndroid(NFCBase):
    ''' This is the class responsible for handling the interace with the
    Android NFC adapter. See Module Documentation for deatils.
    '''

    name = 'NFCAndroid'

    def nfc_init(self):
        ''' This is where we initialize NFC adapter.
        '''
        # Initialize NFC
        global app
        app = App.get_running_app()

        # Make sure we are listening to new intent 
        activity.bind(on_new_intent=self.on_new_intent)

        # Configure nfc
        self.j_context = context = PythonActivity.mActivity
        self.nfc_adapter = NfcAdapter.getDefaultAdapter(context)
        # Check if adapter exists
        if not self.nfc_adapter:
            return False
        
        # specify that we want our activity to remain on top whan a new intent
        # is fired
        self.nfc_pending_intent = PendingIntent.getActivity(context, 0,
            Intent(context, context.getClass()).addFlags(
                Intent.FLAG_ACTIVITY_SINGLE_TOP), 0)

        # Filter for different types of action, by default we enable all.
        # These are only for handling different NFC technologies when app is in foreground
        self.ndef_detected = IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED)
        #self.tech_detected = IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED)
        #self.tag_detected = IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED)

        # setup tag discovery for ourt tag type
        try:
            self.ndef_detected.addCategory(Intent.CATEGORY_DEFAULT)
            # setup the foreground dispatch to detect all mime types
            self.ndef_detected.addDataType('*/*')

            self.ndef_exchange_filters = [self.ndef_detected]
        except Exception as err:
            raise Exception(repr(err))
        return True

    def get_ndef_details(self, tag):
        ''' Get all the details from the tag.
        '''
        details = {}

        try:
            #print 'id'
            details['uid'] = ':'.join(['{:02x}'.format(bt & 0xff) for bt in tag.getId()])
            #print 'technologies'
            details['Technologies'] = tech_list = [tech.split('.')[-1] for tech in tag.getTechList()]
            #print 'get NDEF tag details'
            ndefTag = cast('android.nfc.tech.Ndef', Ndef.get(tag))
            #print 'tag size'
            details['MaxSize'] = ndefTag.getMaxSize()
            #details['usedSize'] = '0'
            #print 'is tag writable?'
            details['writable'] = ndefTag.isWritable()
            #print 'Data format'
            # Can be made readonly
            # get NDEF message details
            ndefMesg = ndefTag.getCachedNdefMessage()
            # get size of current records
            details['consumed'] = len(ndefMesg.toByteArray())
            #print 'tag type'
            details['Type'] = ndefTag.getType()

            # check if tag is empty
            if not ndefMesg:
                details['Message'] = None
                return details

            ndefrecords =  ndefMesg.getRecords()
            length = len(ndefrecords)
            #print 'length', length
            # will contain the NDEF record types
            recTypes = []
            for record in ndefrecords:
                recTypes.append({
                    'type': ''.join(map(unichr, record.getType())),
                    'payload': ''.join(map(unichr, record.getPayload()))
                    })

            details['recTypes'] = recTypes
        except Exception as err:
            print(str(err))

        return details

    def on_new_intent(self, intent):
        ''' This functions is called when the application receives a
        new intent, for the ones the application has registered previously,
        either in the manifest or in the foreground dispatch setup in the
        nfc_init function above. 
        '''

        action_list = (NfcAdapter.ACTION_NDEF_DISCOVERED,)
        # get TAG
        #tag = cast('android.nfc.Tag', intent.getParcelableExtra(NfcAdapter.EXTRA_TAG))

        #details = self.get_ndef_details(tag)

        if intent.getAction() not in action_list:
            print('unknow action, avoid.')
            return

        rawmsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)
        if not rawmsgs:
            return
        for message in rawmsgs:
            message = cast(NdefMessage, message)
            payload = message.getRecords()[0].getPayload()
            print('payload: {}'.format(''.join(map(chr, payload))))

    def nfc_disable(self):
        '''Disable app from handling tags.
        '''
        self.disable_foreground_dispatch()

    def nfc_enable(self):
        '''Enable app to handle tags when app in foreground.
        '''
        self.enable_foreground_dispatch()

    def create_AAR(self):
        '''Create the record responsible for linking our application to the tag.
        '''
        return NdefRecord.createApplicationRecord(JString("org.electrum.kivy"))

    def create_TNF_EXTERNAL(self, data):
        '''Create our actual payload record.
        '''
        if BUILDVERSION >= 14:
            domain = "org.electrum"
            stype = "externalType"
            extRecord = NdefRecord.createExternal(domain, stype, data)
        else:
            # Creating the NdefRecord manually:
            extRecord = NdefRecord(
                NdefRecord.TNF_EXTERNAL_TYPE,
                "org.electrum:externalType",
                '',
                data)
        return extRecord

    def create_ndef_message(self, *recs):
        ''' Create the Ndef message that will written to tag
        '''
        records = []
        for record in recs:
            if record:
                records.append(record)

        return NdefMessage(records)


    @run_on_ui_thread
    def disable_foreground_dispatch(self):
        '''Disable foreground dispatch when app is paused.
        '''
        self.nfc_adapter.disableForegroundDispatch(self.j_context)

    @run_on_ui_thread
    def enable_foreground_dispatch(self):
        '''Start listening for new tags
        '''
        self.nfc_adapter.enableForegroundDispatch(self.j_context,
                self.nfc_pending_intent, self.ndef_exchange_filters, self.ndef_tech_list)

    @run_on_ui_thread
    def _nfc_enable_ndef_exchange(self, data):
        # Enable p2p exchange
        # Create record
        ndef_record = NdefRecord(
                NdefRecord.TNF_MIME_MEDIA,
                'org.electrum.kivy', '', data)
        
        # Create message
        ndef_message = NdefMessage([ndef_record])

        # Enable ndef push
        self.nfc_adapter.enableForegroundNdefPush(self.j_context, ndef_message)

        # Enable dispatch
        self.nfc_adapter.enableForegroundDispatch(self.j_context,
                self.nfc_pending_intent, self.ndef_exchange_filters, [])

    @run_on_ui_thread
    def _nfc_disable_ndef_exchange(self):
        # Disable p2p exchange
        self.nfc_adapter.disableForegroundNdefPush(self.j_context)
        self.nfc_adapter.disableForegroundDispatch(self.j_context)

    def nfc_enable_exchange(self, data):
        '''Enable Ndef exchange for p2p
        '''
        self._nfc_enable_ndef_exchange()

    def nfc_disable_exchange(self):
        ''' Disable Ndef exchange for p2p
        '''
        self._nfc_disable_ndef_exchange()