## CONFIG
# change this to your external ip address for your server
#(needs to be external to allow tor routing)
from config import *
import os,sys; sys.path.append(os.path.abspath(os.path.join(os.path.abspath(os.path.join(os.path.dirname(__file__),'..')),'..')))
from comrad import *

import logging
logger=logging.getLogger(__name__)

# monkeypatching the things that asyncio needs
import subprocess
subprocess.PIPE = -1  # noqa
subprocess.STDOUT = -2  # noqa
subprocess.DEVNULL = -3  # noqa


import asyncio
import os
os.environ['KIVY_EVENTLOOP'] = 'asyncio'
# loop = asyncio.get_event_loop()
# loop.set_debug(True)

# prefer experimental kivy if possible
# sys.path.insert(0,os.path.join(PATH_COMRAD_LIB,'KivyMD'))
import kivymd
# print(kivymd.__file__)
# exit()


# imports
from kivy.uix.screenmanager import *
from kivymd.app import *
from kivymd.uix.button import *
from kivymd.uix.toolbar import *
from kivymd.uix.screen import *
from kivymd.uix.dialog import *
from kivy.lang import *
from kivy.uix.boxlayout import *
from kivymd.theming import *
from kivy.properties import *
from kivymd.uix.label import *
from kivy.uix.widget import *
from kivymd.uix.list import *
from kivymd.uix.card import *
from kivymd.uix.boxlayout import *
from kivy.uix.gridlayout import *
from kivy.metrics import *
from kivy.properties import *   
from kivymd.uix.list import * #MDList, ILeftBody, IRightBody, ThreeLineAvatarListItem, TwoLineAvatarListItem, BaseListItem, ImageLeftWidget
from kivy.uix.image import *
import requests,json
from kivy.storage.jsonstore import *
from kivy.core.window import *
from kivy.core.text import *
import shutil,sys
from kivy.uix.image import *
import sys
sys.path.append("..") # Adds higher directory to python modules path.

from kivy.event import *
import threading,asyncio,sys
from requests.exceptions import ReadTimeout

# raise Exception(str(Window.size))
Window.size = WINDOW_SIZE
# Window.fullscreen = True #'auto'

# with open('log.txt','w') as of:
#     of.write('### LOG ###\n')
def rgb(r,g,b,a=1):
    return (r/255,g/255,b/255,a)

class MyLayout(MDBoxLayout):
    scr_mngr = ObjectProperty(None)
    post_id = ObjectProperty()

    def log(self,*x,**y): return self.app.log(*x,**y)

    @property
    def app(self):
        if not hasattr(self,'_app'):
            from kivy.app import App
            self._app = App.get_running_app()
        return self._app

    def rgb(self,r,g,b,a=1):
        return rgb(r,g,b,a=a)

    def change_screen(self, screen, *args):
        self.scr_mngr.current = screen
        self.app.screen_hist.append(self.app.screen)
        self.app.screen = self.screen = screen

        # toolbar
        toolbar=self.ids.toolbar
        action_items = toolbar.ids.right_actions.children
        for item in action_items:
            # this the screen?
            #self.log('ITEM!!',item, item.icon)
            if item.icon == SCREEN_TO_ICON[screen]:
                item.text_color=rgb(*COLOR_ACCENT)
            else:
                item.text_color=rgb(*COLOR_ICON)
        pass



    def change_screen_from_uri(self,uri,*args):
        self.app.uri=uri
        screen_name = route(uri)
        self.app.screen = screen_name
        self.app.log(f'routing to {screen_name}')
        self.scr_mngr.current = screen_name
    
    def view_post(self,post_id):
        self.post_id=post_id
        self.change_screen('view')

    # def refresh(self,*x,**yy):
    #     async def go():    
    #         if not hasattr(self.app,'is_logged_in') or not self.app.is_logged_in or not hasattr(self.app,'comrad') or not self.app.comrad:
    #             self.change_screen('login')
    #             self.app.log('changing screen???')
    #             return None


    #         logger.info(f'REFRESH: {self.app.is_logged_in}, {self.app.comrad.name}')
    #         self.app.log('<--',x,yy)
    #         if not hasattr(self.app,'map') or not self.app.map:
    #             from comrad.app.screens.map import MapWidget
    #             self.app.map=MapWidget()
    #         self.app.map.open()
    #         await self.app.comrad.get_updates()
    #         self.app.map.dismiss()
    #         self.app.map=None
    #     asyncio.create_task(go())




from comrad.app.screens.dialog import MDDialog2




class ProgressPopup(MDDialog2): pass


class MessagePopup(MDDialog2):
    pass
    # def __init__(self,*x,**y):
    #     super().__init__(*x,**y)
    #     self.ok_to_continue=False
    
    # def on_dismiss(self):
    #     if not self.ok_to_continue:
    #         self.ok_to_continue=True
    #         logger.info('ouch!')
        



    pass
class MessagePopupCard(MDDialog2):
    def __init__(self,*x,**y):
        y['color_bg']=rgb(*COLOR_BG,a=0.5)
        y['type']='custom'
        y['overlay_color']=rgb(*COLOR_BG,a=0)  #rgb(*COLOR_BG)
        self.color_bg=rgb(*COLOR_BG)
        super().__init__(*x,**y)
        self.ok_to_continue=False
        self.dismissable=False
    
    def on_dismiss(self):
       if not self.dismissable:
           return True 
    
    def on_touch_down(self,touch):
        self.ok_to_continue=True
        logger.info('oof!')
        self.dismiss()
        # if hasattr(self,'msg_dialog'):
            # logger.info(str(self.msg_dialog))


class TextInputPopupCard(MDDialog2):
    def say(self,x=None):
        self.ok_to_continue=True
        self.response=self.field.text
        return self.response


    def __init__(self,msg,password=False,input_name='',comrad_name='',*x,**y):
        self.ok_to_continue=False
        self.response=None
        title=msg
        from comrad.app.screens.login.login import UsernameField,PasswordField,UsernameLayout,UsernameLabel

        class TextInputPopupCardField(UsernameField):
            def on_text_validate(self):
                self.has_had_text = True
                self._set_text_len_error()

                # custom
                self.card.say()

        self.layout=MDBoxLayout()
        self.layout.orientation='vertical'
        self.layout.cols=1
        self.layout.size_hint=('333sp','222sp')
        # self.layout.md_bg_color=(1,1,0,1)
        self.layout.adaptive_height=True
        self.layout.height=self.layout.minimum_height
        self.layout.spacing='0sp'
        self.layout.padding='0sp'

        # self.layout.size=('333sp','333sp')



        self.field_layout=UsernameLayout()
        # self.field = PasswordField() if password else UsernameField()
        self.field = TextInputPopupCardField()
        self.field.card=self
        self.field.password=True
        self.field.line_color_focus=rgb(*COLOR_TEXT)
        self.field.line_color_normal=rgb(*COLOR_TEXT)
        self.field.font_name=FONT_PATH
        self.field.font_size='20sp'
        self.field.pos_hint={'center_y':0.5}

        


        self.field_label = UsernameLabel(text='password:' if password else input_name)
        self.field_label.font_name=FONT_PATH
        self.field_label.font_size='20sp'
        
        if title:
            self.title_label = UsernameLabel(text=title)
            self.title_label.halign='center'
            self.title_label.font_size='20sp'
            self.title_label.pos_hint={'center_x':0.5}
            self.title_label.font_name=FONT_PATH
            #self.field_layout.add_widget(self.title_label)
            self.layout.add_widget(self.title_label)
            

        
        self.field_layout.add_widget(self.field_label)
        self.field_layout.add_widget(self.field)
        self.layout.add_widget(self.field_layout)
        # do dialog's intro
        super().__init__(
            type='custom',
            text=msg,
            content_cls=self.layout,
            buttons=[
                MDFlatButton(
                    text="cancel",
                    text_color=rgb(*COLOR_TEXT),
                    md_bg_color = (0,0,0,1),
                    theme_text_color='Custom',
                    on_release=self.dismiss,
                    font_name=FONT_PATH
                ),
                MDFlatButton(
                    text="enter",
                    text_color=rgb(*COLOR_TEXT),
                    md_bg_color = (0,0,0,1),
                    theme_text_color='Custom',
                    on_release=self.say,
                    font_name=FONT_PATH
                ),
            ],
            color_bg = rgb(*COLOR_CARD)
        )
        self.ids.text.text_color=rgb(*COLOR_TEXT)
        self.ids.text.font_name=FONT_PATH
        self.size=('400sp','400sp')
        self.adaptive_height=True
        self.ids.text.font_size='28sp'
        for widget in self.ids.button_box.children:
            widget.font_size='18sp'

    # wait and show
    async def open(self,maxwait=666,pulse=0.1):
        super().open()
        await asyncio.sleep(pulse)
        waited=0
        while not self.ok_to_continue:
            await asyncio.sleep(pulse)
            waited+=pulse
            if waited>maxwait: break
            # logger.info(f'waiting for {waited} seconds... {self.ok_to_continue} {self.response}')
        return self.response














class BooleanInputPopupCard(MDDialog2):
    def say_yes(self,x):
        # logger.info('say_yes got:',str(x))
        self.ok_to_continue=True
        self.response=True
        self.dismiss()
        return self.response
    def say_no(self,x):
        # logger.info('say_no got:',str(x))
        self.ok_to_continue=True
        self.response=False
        self.dismiss()
        return self.response
    
    def __init__(self,msg,*x,**y):
        self.ok_to_continue=False
        self.response=None
        

        # do dialog's intro
        super().__init__(
            text=msg,
            buttons=[
                MDFlatButton(
                    text="no",
                    text_color=rgb(*COLOR_TEXT),
                    md_bg_color = (0,0,0,1),
                    theme_text_color='Custom',
                    on_release=self.say_no,
                    font_name=FONT_PATH
                ),
                MDFlatButton(
                    text="yes",
                    text_color=rgb(*COLOR_TEXT),
                    md_bg_color = (0,0,0,1),
                    theme_text_color='Custom',
                    on_release=self.say_yes,
                    font_name=FONT_PATH
                ),
            ],
            color_bg = rgb(*COLOR_CARD)
        )

        self.ids.text.text_color=rgb(*COLOR_TEXT)
        self.ids.text.font_name=FONT_PATH
        self.ids.text.font_size='22sp'

        for widget in self.ids.button_box.children:
            widget.font_size='22sp'


    # wait and show
    async def open(self,maxwait=666,pulse=0.1):
        super().open()
        await asyncio.sleep(pulse)
        waited=0
        while not self.ok_to_continue:
            await asyncio.sleep(pulse)
            waited+=pulse
            if waited>maxwait: break
            # logger.info(f'waiting for {waited} seconds... {self.ok_to_continue} {self.response}')
        return self.response


class MyBoxLayout(MDBoxLayout): pass
class MyLabel(MDLabel): pass

class MyToolbar(MDToolbar):
    action_icon_color = ListProperty()

    def update_action_bar(self, action_bar, action_bar_items):
        action_bar.clear_widgets()
        new_width = 0
        for item in action_bar_items:
            new_width += dp(48)
            action_bar.add_widget(
                MDIconButton(
                    icon=item[0],
                    on_release=item[1],
                    opposite_colors=True,
                    text_color=(self.specific_text_color if not self.action_icon_color else self.action_icon_color),
                    theme_text_color="Custom",
                )
            )
        action_bar.width = new_width

    def update_action_bar_text_colors(self, instance, value):
        for child in self.ids["left_actions"].children:
            if not self.action_icon_color:
                child.text_color = self.specific_text_color
            else:
                child.text_color = self.action_icon_color

        for child in self.ids["right_actions"].children:
            if not self.action_icon_color:
                child.text_color = self.specific_text_color
            else:
                child.text_color = self.action_icon_color




def draw_background(widget, img_fn='assets/bg.png'):
    from kivy.core.image import Image as CoreImage
    from kivy.graphics import Color, Rectangle 
    widget.canvas.before.clear()
    with widget.canvas.before:
        Color(.4, .4, .4, 1)
        texture = CoreImage(img_fn).texture
        texture.wrap = 'repeat'
        nx = float(widget.width) / texture.width
        ny = float(widget.height) / texture.height
        Rectangle(pos=widget.pos, size=widget.size, texture=texture,
                  tex_coords=(0, 0, nx, 0, nx, ny, 0, ny))








#### LOOPER



def route(uri):
    if not '/' in uri: return None
    prefix=uri.split('/')[1] #,channel,rest = uri.split('/',3)

    mapd = {
        'inbox':'feed',
        'outbox':'feed',
        'login':'login',
    }
    return mapd.get(prefix,None)



# DEFAULT_SCREEN = route(DEFAULT_URI)

class MainApp(MDApp, Logger):
    title = 'Comrad'
    logged_in=False
    login_expiry = 60 * 60 * 24 * 7  # once a week
    texture = ObjectProperty()
    uri='/do/login'
    screen='login'
    screen_hist=[]
    screen_names = [
        'feed',
        'messages',
        'post',
        'profile',
        # 'refresh',
        # 'login'
    ]

    def go_back(self):
        self.change_screen(
            self.screen_hist.pop() if self.screen_hist else 'feed'
        )

    def _on_keyboard_down(self, instance, keyboard, keycode, text, modifiers):
        # self.log(f'keyboard:{keyboard}\nkeycode: {keycode}\ntext: {text}\ninstance: {instance}\nmodifiers:\n{modifiers}\n\n')
        
        ## arrows
        # ctrl left
        modifiers=set(modifiers)
        
        # enter? continue
        if keycode==40:
            if hasattr(self,'msg_dialog') and self.msg_dialog:
                self.msg_dialog.ok_to_continue=True

        # left
        if keycode == 80 and 'ctrl' in modifiers:
            # go back
            self.go_back()
        # down
        elif keycode==81 and 'ctrl' in modifiers:
            screen_index=self.screen_names.index(self.screen)
            new_screen = self.screen_names[screen_index - 1]
            
            self.change_screen(new_screen)
        # up
        elif keycode==82 and 'ctrl' in modifiers:
            screen_index=self.screen_names.index(self.screen)
            try:
                new_screen = self.screen_names[screen_index +1]
            except IndexError:
                new_screen = self.screen_names[0]

            self.change_screen(new_screen)
        
        
        ## keys
        elif text=='f' and 'ctrl' in modifiers:
            self.change_screen('feed')
        elif text=='m' and 'ctrl' in modifiers:
            self.change_screen('messages')
        elif text=='c' and 'ctrl' in modifiers:
            self.change_screen('post')
        elif text=='p' and 'ctrl' in modifiers:
            self.change_screen('profile')
        elif text=='r' and 'ctrl' in modifiers:
            self.change_screen('refresh')
        elif text=='e' and 'ctrl' in modifiers:
            self.change_screen('login')
        


    def rgb(self,*_): return rgb(*_)

    def change_screen(self, screen, *args):
        self.root.change_screen(screen,*args)

    def get_username(self): return self._name

    @property
    def crypt(self):
        if not hasattr(self,'_crypt'):
            from comrad.backend.crypt import Crypt
            self._crypt = Crypt(
                fn=PATH_CRYPT_CA_DATA,
                encrypt_values=False,
            )
        return self._crypt

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.event_loop_worker = None
        self.loop=asyncio.get_event_loop()
        self.comrad=None
        self._name=''
        Window.bind(on_key_down=self._on_keyboard_down)


    def build(self):
        # bind 
        global app,root
        app = self
        
        # load root
        self.root = root = Builder.load_file('root.kv')
        draw_background(self.root)
        
        # edit logo
        toolbar=root.ids.toolbar
        toolbar.md_bg_color = root.rgb(*COLOR_TOOLBAR)
        toolbar.action_icon_color=root.rgb(*COLOR_ICON)
        logo=toolbar.ids.label_title
        logo.font_name='assets/Strengthen.ttf'
        logo.font_size='58dp'
        logo.pos_hint={'center_y':0.43}
        logo.text_color=root.rgb(*COLOR_LOGO)
        
        action_items = toolbar.ids.right_actions.children


        

        # build the walker
        self.walker=MazeWalker(callbacks=self.callbacks)
        self.torpy_logger = logging.getLogger('torpy')
        self.torpy_logger.propagate=False
        self.torpy_logger.addHandler(self.walker)
        import ipinfo
        ipinfo_access_token = '90df1baf7c373a'
        self.ipinfo_handler = ipinfo.getHandler(ipinfo_access_token)

        from comrad.app.screens.map import MapWidget
        self.map = MapWidget()
        
        self.change_screen(self.screen)

        for item in action_items:
            if item.icon == SCREEN_TO_ICON['login']:
                item.text_color=rgb(*COLOR_ACCENT)
            else:
                item.text_color=rgb(*COLOR_ICON)
        

        return self.root

    # def boot(self,username):
    #     commie = Comrad(username)
    #     if self.exists_locally_as_contact()

    def open_map(self):
        if self.map is None:
            from comrad.app.screens.map import MapWidget
            self.map = MapWidget()
            self.map.open()
    
    def close_map(self):
        if self.map is not None:
            self.map.dismiss()
            self.map=None

    @property
    def callbacks(self):
        return {
            'torpy_guard_node_connect':self.callback_on_hop,
            'torpy_extend_circuit':self.callback_on_hop,
        }
    
    async def callback_on_hop(self,rtr):
        if not hasattr(self,'hops'): self.hops=[]
        if not hasattr(self,'map') or not self.map:
            from comrad.app.screens.map import MapWidget
            self.map=MapWidget()
        if not self.map.opened:
            self.map.open()
            # self.map.draw()

        try:
            deets = self.ipinfo_handler.getDetails(rtr.ip)
            self.hops.append((rtr,deets))
            lat,long=tuple(float(_) for _ in deets.loc.split(','))
            flag=f'{deets.city}, {deets.country_name} ({rtr.nickname})'
            
            self.map.add_point(lat,long,flag)
            self.map.draw()
            # await asyncio.sleep(2)
            # logger.info('CALLBACK ON HOP: ' + flag)
        except ReadTimeout as e:
            self.log('!! read time out:',e)
            return

    def load_store(self):
        if not self.store.exists('user'): return
        userd=self.store.get('user')
        if not userd: return

        self.username = userd.get('username','')




    def clear_widget_tree(self,widget_type,widget=None):
        if not widget: widget=self.root
        for widg in widget.children:
            if hasattr(widg,'children') and widg.children:
                self.clear_widget_tree(widget_type,widget=widg)
            
            self.log(widg,type(widg),widget_type,issubclass(type(widg),widget_type))
            if issubclass(type(widg),widget_type):
                self.remove_widget(widg)


    def view_profile(self,username):
        self.username=username
        self.change_screen('profile')
        # self.username=self.comrad.name


















    


    async def upload(self,filename,file_id=None):
        self.log('uploading filename:',filename)
        rdata=await self.api.upload(filename,file_id=file_id)
        self.log('upload result:',rdata)
        if rdata is not None:
            rdata['success']='File uploaded'
            return rdata
        return {'error':'Upload failed'}
        
    async def download(self,file_id,output_fn=None):
        self.log('downloading:',file_id)
        file_dat = await self.api.download(file_id)
        self.log('file_dat =',file_dat)
        if not output_fn:
            file_id=file_dat['id']
            file_ext=file_dat['ext']
            output_fn=os.path.join('cache',file_id[:3]+'/'+file_id[3:]+'.'+file_ext)
        
        output_dir=os.path.dirname(output_fn)
        if not os.path.exists(output_dir): os.makedirs(output_dir)

        with open(output_fn,'wb') as of:
            for data_piece in file_dat['parts_data']:
                if data_piece is not None:
                    of.write(data_piece)
    
    async def post(self, content='', file_id=None, file_ext=None, anonymous=False,channel='world'):
        #timestamp=time.time()
        jsond={}
        #jsond['timestamp']=
        if content: jsond['content']=str(content)
        if file_id: jsond['file_id']=str(file_id)
        if file_ext: jsond['file_ext']=str(file_ext)
        if channel and channel[0]=='@': channel=channel[1:]
        self.log(f'''app.post(
            content={content},
            file_id={file_id},
            file_ext={file_ext},
            anonymous={anonymous},
            channel={channel},
            [username={self.username}]'''
        )
        if not anonymous and self.username:
            jsond['author']=self.username
        
        #jsond['channel']=channel
        self.log('posting:',jsond)
        res=await self.api.post(jsond,channel = channel)
        if 'success' in res:
            self.root.change_screen('feed')
            return {'post_id':res['post_id']}
                    

    @property
    def keys(self):
        contacts_obj = self.comrad.contacts()
        contacts = [p.name for p in contacts_obj]
        return contacts

    async def get_post(self,post_id):
        return self.comrad.read_post()

    def get_posts(self,uri=b'/inbox/world'):
        return self.comrad.posts()
        

    async def get_channel_posts(self,channel,prefix='inbox'):
        # am I allowed to?
        if not channel in self.keys:
            self.log('!! tsk tsk dont be nosy')
            return
        return await self.get_posts(uri='/'+os.path.join(prefix,channel))

    async def get_channel_inbox(self,channel):
        return await self.get_channel_posts(channel=channel,prefix='inbox')
    
    async def get_channel_outbox(self,channel):
        return await self.get_channel_posts(channel=channel,prefix='outbox')

    async def get_my_posts(self):
        return await self.comrad.posts()



    ### SYNCHRONOUS?
    def app_func(self):
        '''This will run both methods asynchronously and then block until they
        are finished
        '''
        # self.other_task = asyncio.ensure_future(self.waste_time_freely())
        # self.other_task = asyncio.ensure_future(self.api.connect_forever())

        async def run_wrapper():
            # we don't actually need to set asyncio as the lib because it is
            # the default, but it doesn't hurt to be explicit
            await self.async_run() #async_lib='asyncio')
            print('App done')
            # self.other_task.cancel()

        # return asyncio.gather(run_wrapper(), self.other_task)
        asyncio.run(run_wrapper())



    def open_dialog(self,msg):
        if not hasattr(self,'dialog') or not self.dialog:
            self.dialog = ProgressPopup()
        
        # raise Exception(self.dialog, msg)
        self.dialog.text=msg
        self.dialog.open()
        #stop

    async def get_input(self,msg,comrad_name='Telephone',get_pass=False,yesno=False,**y):
        from comrad.app.screens.feed.feed import PostCardInputPopup
        if hasattr(self,'msg_dialog') and self.msg_dialog: self.msg_dialog.dismiss()
            
            # and hasattr(self.msg_dialog,'card') and self.msg_dialog.card:
        #     self.msg_dialog0=self.msg_dialog
        #     self.msg_dialog0.dismiss()
        #     self.msg_dialog0=None
        
        if yesno:
            self.msg_dialog = BooleanInputPopupCard(msg,comrad_name=comrad_name,**y)
            
        else:
            self.msg_dialog = TextInputPopupCard(msg,password=get_pass,comrad_name=comrad_name,**y)

        response = await self.msg_dialog.open()
        logger.info(f'get_input got user response {response}')
        if self.msg_dialog: 
            self.msg_dialog.dismiss()
            self.root.remove_widget(self.msg_dialog)
        self.msg_dialog=None
        # async def task():
        #     await asyncio.sleep(1)
        #     self.msg_dialog.dismiss()
        #     self.msg_dialog=None
        # asyncio.create_task(task())
        return response

    async def ring_ring(self,*x,commie=None,**y):
        if not commie: commie=self.comrad
        from comrad.app.screens.map import MapWidget
        self.map=MapWidget()
        self.map.open()
        resp_msg_d = await commie.ring_ring(*x,**y)
        logger.info('done with ring_ring! ! !')
        self.map.dismiss()
        self.map=None
        return resp_msg_d

    
    async def get_updates(self,*x,commie=None,**y):
        if not commie: commie=self.comrad
        from comrad.app.screens.map import MapWidget
        self.map=MapWidget()
        self.map.open()
        await commie.get_updates(*x,**y)
        logger.info('done with get_updates! ! !')
        self.map.dismiss()
        self.map=None
        

        
    async def stat(self,msg,comrad_name='Telephone',pause=False,get_pass=False,**y):
        from comrad.app.screens.feed.feed import PostCard,PostCardPopup
        # if hasattr(self,'msg_dialog') and self.msg_dialog:# and hasattr(self.msg_dialog,'card') and self.msg_dialog.card:
        #     self.msg_dialog0=self.msg_dialog
        #     self.msg_dialog0.dismiss()
        #     self.msg_dialog0.clear_widgets()


        self.msg_dialog = MessagePopupCard()
        # self.root.add_widget(self.msg_dialog)
        # self.msg_dialog.ids.msg_label.text=msg
        nm = '?' if not self.comrad else self.comrad.name
        self.msg_dialog.card = postcard = PostCardPopup({
            'author':comrad_name,
            'author_prefix':'Comrad @',
            'to_name':nm,
            'content':msg,
            'timestamp':time.time(),
            'author_label_font_size':'18sp',
            **y
        },
        msg_dialog=self.msg_dialog)
        postcard.font_size='16sp'
        postcard.size_hint=(None,None)
        postcard.size=('600sp','600sp')
        postcard.ok_to_continue=False

        self.msg_dialog.add_widget(postcard)

        self.msg_dialog.open(animation=False)

        # if hasattr(self,'msg_dialog0'):
            # self.root.remove_widget(self.msg_dialog0)
            
        await asyncio.sleep(0.1)
        while not self.msg_dialog.ok_to_continue:
            await asyncio.sleep(0.1)
            # logger.info(str(postcard), postcard.ok_to_continue,'??')
        self.msg_dialog.dismissable=True
        self.msg_dialog.dismiss()
        self.msg_dialog.remove_widget(postcard)
        # self.root.remove_widget(self.msg_dialog)
        # self.root.clear_widgets()
        self.msg_dialog.card = postcard = self.msg_dialog = None
        await asyncio.sleep(0.1)
        return {'success':True, 'status':'Delivered popup message'}
        

    def open_msg_dialog(self,msg):
        # from screens.post.post import MessagePopup,ProgressPopup
        if not hasattr(self,'msg_dialog') or not self.msg_dialog:
            self.msg_dialog = MessagePopup()
            self.msg_dialog.ids.msg_label.text=msg
            self.msg_dialog.open()

    def close_dialog(self):
        if hasattr(self,'dialog'):
            self.dialog.dismiss()

    def close_msg_dialog(self):
        if hasattr(self,'msg_dialog'):
            self.msg_dialog.remove_widget(self.msg_dialog.card)
            self.msg_dialog.dismiss()
            self.remove_widget(self.msg_dialog)


    async def prompt_addcontact(self,post_data,screen=None,post_id=None,post_card=None):
        meet_name = post_data.get('meet_name')
        meet_uri = post_data.get('meet').decode()    
                
        yn=await self.get_input(f"Exchange public keys with {meet_name}? It will allow you and @{meet_name} to read and write encrypted messages to one another.",yesno=True)


        if screen:
            num_slides=len(screen.carousel.slides)
            i=screen.carousel.index
            self.log('changing slide??',num_slides,i)
            screen.carousel.index=(i+1 if i<num_slides else 0)
            if post_card is not None:
                screen.carousel.remove_widget(post_card)    
        
        if yn:
            fnfn = self.comrad.save_uri_as_qrcode(
                uri_id=meet_uri,
                name=meet_name
            )
            await self.stat(f'''Saved {meet_name}'s public key:\n{meet_uri}.''',img_src=fnfn)
            await self.stat('Now returning the invitation...')
            self.open_map()
            res = await self.comrad.meet_async(meet_name,returning=True)
            self.close_map()

            if res.get('success'):
                await self.stat('Invitation successfully sent.')
            else:
                await self.stat(res.get('status'))
            

        #delete this msg
        if post_id:
           self.comrad.delete_post(post_id)
        
        


    async def prompt_meet(self,meet_name,screen=None):
        yn=await self.get_input(f"Exchange public keys with {meet_name}? It will allow you and @{meet_name} to read and write encrypted messages to one another.",yesno=True)

        if yn:
            other_data = {}
            path_avatar=os.path.join(PATH_AVATARS,self.comrad.name+'.png')

            if os.path.exists(path_avatar):
                yn_avatar = await self.get_input(f'Send along your avatar? Otherwise, you will appear to {meet_name} as the default avatar.',yesno=True)

                if yn_avatar:
                    with open(path_avatar,'rb') as f:
                        other_data['img_avatar']=f.read()
                
                # extra_msg = await self.get_input(f'Include a short message to {meet_name}? (optional)')
                # other_data['extra_msg']=extra_msg.strip()
            await self.stat('Sending the invitation...')
            self.open_map()
            res = await self.comrad.meet_async(
                meet_name,
                returning=True,
                other_data=other_data
            )
            self.close_map()

            if res.get('success'):
                await self.stat('Invitation successfully sent.')
            else:
                await self.stat(res.get('status'))







if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    try:
        loop.run_until_complete(MainApp().app_func())
    except AssertionError:
        print('\n\nGoodbye.\n')
        pass
    loop.close()




# def main():
#     # start_logger()
#     App = MainApp()
#     App.run()


# if __name__ == '__main__':
#     # loop = asyncio.get_event_loop()
#     # asyncio.set_event_loop(loop)
#     # loop.run_until_complete(main())
#     # loop.close()
#     main()
