# -*- coding: utf8 -*-
# Общественное достояние, 2023, Алексей Безбородов (Alexei Bezborodov) <AlexeiBv+mirocod_platform_bot@narod.ru> 

# Модуль для редактирования и просмотра таблицы в БД

from bot_sys import keyboard, user_access, bd_table, bot_bd, bot_subscribes
from bot_modules import access_utils, mod_simple_message
from template import simple_message, bd_item, bd_item_select, bd_item_view, bd_item_delete, bd_item_add, bd_item_edit

from aiogram.dispatcher import FSMContext
from aiogram.dispatcher.filters.state import State, StatesGroup

from enum import Enum
from enum import auto

def EditButton(a_BDTableDestiny):
    return 'edit ' + str(a_BDTableDestiny)

def EnumButton(a_EnumItem):
    return 'enum ' + str(a_EnumItem)

def EditMessage(a_BDTableDestiny):
    return 'edit ' + str(a_BDTableDestiny)

def CreateMessage(a_BDTableDestiny):
    return 'create ' + str(a_BDTableDestiny)

def EnumMessageForView(a_EnumItem):
    return 'enum ' + str(a_EnumItem)

def NotificationMessage(a_EnumItem):
    return 'notification ' + str(a_EnumItem)

def SubscribeMessage(a_EnumItem):
    return 'subscribe ' + str(a_EnumItem)

class ButtonNames(Enum):
    LIST = auto() 
    ADD = auto() 
    EDIT = auto() 
    DEL = auto() 

class Messages(Enum):
    SELECT = auto() 
    ERROR_FIND = auto() 
    OPEN = auto() 
    SUCCESS_CREATE = auto() 
    START_EDIT = auto() 
    SELECT_TO_EDIT = auto() 
    SUCCESS_EDIT = auto() 
    SELECT_TO_DELETE = auto() 
    SUCCESS_DELETE = auto() 

def GetCurItem(a_Bot, a_TableName, a_KeyName, a_KeyValue):
    items = bd_item.GetBDItemsTemplate(a_Bot, a_TableName, a_KeyName)(a_KeyValue)
    if len(items) == 1:
        return items[0]
    return None

create_fsm_create_cmd = '''
class FSMCreate{a_ModName}(StatesGroup):
{items}

fsm = FSMCreate{a_ModName}
'''

def MakeFSMForAdd(a_ModName, a_Fields):
    cmd = create_fsm_create_cmd.replace("{a_ModName}", a_ModName)
    items = ""
    for i in range(len(a_Fields)):
        items += f"\titem{i} = State()\n"
    cmd = cmd.replace("{items}", items)
    _locals = locals()
    exec(cmd, globals(), _locals)
    return _locals['fsm']


edit_fsm_cmd = '''
class FSMEdit{a_ModName}_{a_FieldName}_Item(StatesGroup):
    item_field = State()
    
fsm = FSMEdit{a_ModName}_{a_FieldName}_Item
'''

def MakeFSMForEdit(a_ModName, a_FieldName):
    cmd = edit_fsm_cmd.replace("{a_ModName}", a_ModName).replace("{a_FieldName}", a_FieldName)
    _locals = locals()
    exec(cmd, globals(), _locals)
    return _locals['fsm']

class TableOperateModule(mod_simple_message.SimpleMessageModule):
    def __init__(self, a_Table, a_Messages, a_Buttons, a_ParentModName, a_ChildModName, a_InitAccess, a_DefInitAccess, a_ChildModuleNameList, a_EditModuleNameList, a_Bot, a_ModuleAgregator, a_BotMessages, a_BotButtons, a_BotSubscribes, a_Log):
        super().__init__(a_Messages, a_Buttons, a_InitAccess, a_DefInitAccess, a_ChildModuleNameList, a_Bot, a_ModuleAgregator, a_BotMessages, a_BotButtons, a_BotSubscribes, a_Log)
        self.m_Table = a_Table
        self.m_EditModuleNameList = a_EditModuleNameList
        self.m_ChildModName = a_ChildModName
        self.m_ParentModName = a_ParentModName
        self.m_SelectPrefix = ''

        def GetEditKeyboardButtons(a_Message, a_UserGroups):
            return self.GetEditKeyboardButtons(a_Message, a_UserGroups)
        self.m_GetEditKeyboardButtonsFunc = GetEditKeyboardButtons

        def GetButtonNameAndKeyValueAndAccess(a_Item):
            return self.GetButtonNameAndKeyValueAndAccess(a_Item)
        self.m_GetButtonNameAndKeyValueAndAccessFunc = GetButtonNameAndKeyValueAndAccess

        async def PreDelete(a_CallbackQuery, a_Item, a_None, table_name = self.m_Table.GetName()):
            return await self.PreDelete(a_CallbackQuery, a_Item, a_None)
        self.m_PreDeleteFunc = PreDelete

        async def PostDelete(a_CallbackQuery, a_ItemID):
            return await self.PostDelete(a_CallbackQuery, a_ItemID)
        self.m_PostDeleteFunc = PostDelete

        async def AddBDItemFunc(a_ItemData, a_UserID):
            return await self.AddBDItemFunc(a_ItemData, a_UserID)
        self.m_AddBDItemFunc = AddBDItemFunc
 
    def GetInitBDCommands(self):
        return  [
            self.m_Table.GetInitTableRequest(),
            ] + super().GetInitBDCommands()

    def GetStartButtons(self, a_Message, a_UserGroups):
        return [
            [ButtonNames.LIST, user_access.AccessMode.VIEW],
            [ButtonNames.ADD, user_access.AccessMode.ADD],
            [ButtonNames.DEL, user_access.AccessMode.DELETE],
            [ButtonNames.EDIT, user_access.AccessMode.EDIT],
        ]

    def GetStartKeyboardButtons(self, a_Message, a_UserGroups):
        mod_buttons = super().GetStartKeyboardButtons(a_Message, a_UserGroups)
        cur_buttons = []
        for b in self.GetStartButtons(a_Message, a_UserGroups):
            bn = b[0]
            ba = b[1]
            cur_buttons += [keyboard.ButtonWithAccess(self.GetButton(bn), ba, self.GetAccess())]
        return mod_buttons + keyboard.MakeButtons(self.m_Bot, cur_buttons, a_UserGroups)

    def GetAccessForEditKeyboardButtons(self, a_Field):
        if a_Field.m_Destiny == bd_table.TableFieldDestiny.ACCESS or a_Field.m_Destiny == bd_table.TableFieldDestiny.DEFAULT_ACCESS:
            return user_access.AccessMode.ACCEES_EDIT
        return user_access.AccessMode.EDIT

    def GetEditKeyboardButtons(self, a_Message, a_UserGroups):
        mod_buttons = keyboard.MakeButtons(self.m_Bot, self.GetButtons(self.m_EditModuleNameList), a_UserGroups)
        cur_buttons = []
        for f in self.m_Table.GetFields():
            access = self.GetAccessForEditKeyboardButtons(f)
            cur_buttons += [keyboard.ButtonWithAccess(self.GetButton(EditButton(f.m_Destiny)), access, self.GetAccess()),]
        return mod_buttons + keyboard.MakeButtons(self.m_Bot, cur_buttons, a_UserGroups)

    def GetViewItemInlineKeyboardTemplate(self, a_ItemID):
        def GetViewItemInlineKeyboard(a_Message, a_UserGroups):
            return self.GetViewItemInlineKeyboard(a_Message, a_UserGroups, a_ItemID)
        return GetViewItemInlineKeyboard

    def GetSelectPrefix(self):
        return self.m_SelectPrefix

    def GetViewItemInlineKeyboard(self, a_Message, a_UserGroups, a_ItemID):
        if not self.m_ChildModName:
            return None
        child_mod = self.GetModule(self.m_ChildModName)
        cur_buttons = [
                keyboard.InlineButtonWithAccess(child_mod.GetButton(ButtonNames.LIST), child_mod.GetSelectPrefix(), a_ItemID, self.GetAccess(), user_access.AccessMode.VIEW),
                ]
        return keyboard.MakeInlineKeyboardButtons(self.m_Bot, cur_buttons, a_UserGroups)

    def GetButtonNameAndKeyValueAndAccess(self, a_Item):
        key_name_id = self.GetKeyFieldID()
        name_field_id = self.m_Table.GetFieldIDByDestiny(bd_table.TableFieldDestiny.NAME)
        access_field_id = self.m_Table.GetFieldIDByDestiny(bd_table.TableFieldDestiny.ACCESS)
        assert key_name_id != None
        assert name_field_id != None
        assert access_field_id != None
        return \
                a_Item[name_field_id],\
                a_Item[key_name_id],\
                a_Item[access_field_id]

    def UpdateMessage(self, a_Msg, a_Lang, a_Item, a_EnablePhoto = False):
        a_Msg.UpdateDesc(self.m_Table.ReplaceAllFieldTags(a_Msg.GetDesc(), a_Item))
        photos = []
        field_id = 0
        for f in self.m_Table.GetFields():
            if f.m_Type == bd_table.TableFieldType.ENUM:
                for s in f.m_Enum:
                    msg = self.GetMessage(EnumMessageForView(s))
                    if msg:
                        a_Msg.UpdateDesc(a_Msg.GetDesc().replace(str(s), str(msg.GetMessageForLang(a_Lang).StaticCopy())))
            elif f.m_Type == bd_table.TableFieldType.PHOTO:
                photos += [str(a_Item[field_id])]
            field_id += 1
        if a_EnablePhoto:
            a_Msg.UpdatePhotoID(','.join(photos))
        return a_Msg

    def UpdateMessageByDict(self, a_Msg, a_Lang, a_ItemDict, a_EnablePhoto = False):
        a_Msg.UpdateDesc(self.m_Table.ReplaceAllFieldTagsByDict(a_Msg.GetDesc(), a_ItemDict))
        photos = []
        for f in self.m_Table.GetFields():
            if f.m_Type == bd_table.TableFieldType.ENUM:
                for s in f.m_Enum:
                    msg = self.GetMessage(EnumMessageForView(s))
                    if msg:
                        a_Msg.UpdateDesc(a_Msg.GetDesc().replace(str(s), str(msg.GetMessageForLang(a_Lang).StaticCopy())))
            elif f.m_Type == bd_table.TableFieldType.PHOTO:
                if f.m_Name in a_ItemDict:
                    photos += [str(a_ItemDict[f.m_Name])]
        if a_EnablePhoto:
            a_Msg.UpdatePhotoID(','.join(photos))
        return a_Msg

    def ShowMessageTemplate(self, a_Message, Inline_keyboard_template_func = None, a_EnablePhoto = False):
        async def ShowMessage(a_CallbackQuery, a_Item, a_ItemDict, table_name = self.m_Table.GetName()):
            msg = a_Message.StaticCopy()
            # TODO: добавить поддержку языков в a_MessageName
            Inline_keyboard_func = None
            item_access = None
            if a_ItemDict:
                lang = str(a_CallbackQuery.from_user.language_code)
                msg = msg.GetMessageForLang(lang).StaticCopy()
                msg = self.UpdateMessageByDict(msg, lang, a_ItemDict, a_EnablePhoto = a_EnablePhoto)

            if a_Item and self.m_Table.GetName() == table_name:
                if len(a_Item) < self.m_Table.GetFieldsCount() - 1: # Для проектов это нужно. Там на 1 меньше поле. TODO разделить отправку сообщений item_access и Inline_keyboard_func
                    return simple_message.WorkFuncResult(self.GetMessage(Messages.ERROR_FIND))
                elif len(a_Item) == self.m_Table.GetFieldsCount():
                    lang = str(a_CallbackQuery.from_user.language_code)
                    msg = msg.GetMessageForLang(lang).StaticCopy()
                    msg = self.UpdateMessage(msg, lang, a_Item, a_EnablePhoto = a_EnablePhoto)
                item_access = a_Item[self.m_Table.GetFieldIDByDestiny(bd_table.TableFieldDestiny.ACCESS)]
                if Inline_keyboard_template_func:
                    Inline_keyboard_func = Inline_keyboard_template_func(a_Item[self.GetKeyFieldID()])

            return simple_message.WorkFuncResult(msg, item_access = item_access, Inline_keyboard_func = Inline_keyboard_func)
        return ShowMessage

    async def PreDelete(self, a_CallbackQuery, a_Item, a_None):
        if len(a_Item) < self.m_Table.GetFieldsCount():
            return simple_message.WorkFuncResult(self.GetMessage(Messages.ERROR_FIND))
        access = a_Item[self.m_Table.GetFieldIDByDestiny(bd_table.TableFieldDestiny.ACCESS)]
        return simple_message.WorkFuncResult(self.GetMessage(Messages.SUCCESS_DELETE), None, item_access = access)

    async def PostDelete(self, a_CallbackQuery, a_ItemID):
        user_id = a_CallbackQuery.from_user.id
        self.m_Log.Success(f'Задача №{a_ItemID} была удалена пользователем {user_id}.')
        #TODO: удалить вложенные 
        self.OnChange()

        subscribe_type = bot_subscribes.SubscribeType.ANY_ITEM_DEL
        item_id = -1
        await self.SendSubscribe(subscribe_type, item_id, user_id)

        subscribe_type = bot_subscribes.SubscribeType.ITEM_DEL
        item_id = a_ItemID
        await self.SendSubscribe(subscribe_type, item_id, user_id)

        table_name = self.m_Table.GetName()
        key_name = self.GetKeyFieldName()

        cur_item = GetCurItem(self.m_Bot, table_name, key_name, a_ItemID)
        print(cur_item)

        parent_id_field_index = self.m_Table.GetFieldIDByDestiny(bd_table.TableFieldDestiny.PARENT_ID)
        if parent_id_field_index and cur_item and cur_item[parent_id_field_index]:
            subscribe_type = bot_subscribes.SubscribeType.ANY_ITEM_DEL_WITH_PARENT
            item_id = cur_item[parent_id_field_index]
            await self.SendSubscribe(subscribe_type, item_id, user_id)

        return simple_message.WorkFuncResult(self.GetMessage(Messages.SUCCESS_DELETE))

    async def AddBDItemFunc(self, a_ItemData, a_UserID):
        table_name = self.m_Table.GetName()
        name_field = self.m_Table.GetFieldNameByDestiny(bd_table.TableFieldDestiny.NAME)
        photo_field = self.m_Table.GetFieldNameByDestiny(bd_table.TableFieldDestiny.PHOTO)
        desc_field = self.m_Table.GetFieldNameByDestiny(bd_table.TableFieldDestiny.DESC)
        access_field = self.m_Table.GetFieldNameByDestiny(bd_table.TableFieldDestiny.ACCESS)
        create_datetime_field = self.m_Table.GetFieldNameByDestiny(bd_table.TableFieldDestiny.CREATE_DATE)
        parent_id_field = self.m_Table.GetFieldNameByDestiny(bd_table.TableFieldDestiny.PARENT_ID)

        def_access = access_utils.GetItemDefaultAccessForModule(self.m_Bot, self.GetName())

        fields = []
        values = []
        param = ()
        for f in self.m_Table.GetFields():
            d = f.m_Destiny
            n = f.m_Name
            if d == bd_table.TableFieldDestiny.KEY:
                continue
            elif d == bd_table.TableFieldDestiny.CREATE_DATE:
                fields += [n]
                values += [bot_bd.GetBDDateTimeNow()]
            elif d == bd_table.TableFieldDestiny.ACCESS:
                fields += [n]
                values += ['?']
                param += (def_access + f";{a_UserID}=+", )
            else:
                fields += [n]
                values += ['?']
                param += (a_ItemData[n], )

        request = f'INSERT INTO {table_name}({",".join(fields)}) VALUES({",".join(values)})'
        print('request', request, param)
        res, error = self.m_Bot.SQLRequest(request, commit = True, return_error = True, param = param)

        self.OnChange()
        if error:
            self.m_Log.Error(f'Пользователь {a_UserID}. Ошибка добавления записи в таблицу {request} {param}.')
        else:
            self.m_Log.Success(f'Пользователь {a_UserID}. Добавлена запись в таблицу {request} {param}.')

        subscribe_type = bot_subscribes.SubscribeType.ANY_ITEM_ADD
        item_id = -1
        await self.SendSubscribe(subscribe_type, item_id, a_UserID)

        if parent_id_field and a_ItemData[parent_id_field]:
            subscribe_type = bot_subscribes.SubscribeType.ANY_ITEM_ADD_WITH_PARENT
            item_id = a_ItemData[parent_id_field]
            await self.SendSubscribe(subscribe_type, item_id, a_UserID)

        return res, error

    async def SendSubscribe(self, a_Type, a_ItemID, a_OwnerUserID):
        for s_user_id in self.m_BotSubscribes.GetUserIDs(self.GetName(), a_Type, a_ItemID = a_ItemID):
            a_BotMessage = self.GetMessage(SubscribeMessage(a_Type))
            if not a_BotMessage:
                continue
            if s_user_id == a_OwnerUserID:
                continue
            a_BotMessage = a_BotMessage.StaticCopy()
            a_BotMessage.UpdateDesc(a_BotMessage.GetDesc().replace("#item_id", str(a_ItemID)))
            a_GetButtonsFunc = None
            a_GetInlineButtonsFunc = None
            a_UserID = s_user_id
            a_Message = None
            user_groups = None
            parse_mode = None
            try:
                await simple_message.SendMessage(self.m_Bot, a_BotMessage, a_GetButtonsFunc, a_GetInlineButtonsFunc, a_UserID, a_Message, user_groups, parse_mode=parse_mode)
            except:
                return

    def SelectSourceTemplate(self, a_PrevPrefix, a_ButtonName):
        parent_id_field = self.m_Table.GetFieldNameByDestiny(bd_table.TableFieldDestiny.PARENT_ID)
        return bd_item_select.DBItemSelectSource(self.m_Bot, self.m_Table.GetName(), parent_id_field, a_PrevPrefix, a_ButtonName)

    def RegisterSelect(self, a_ButtonName, access_mode, only_parent = False):
        a_Prefix = None
        if self.m_ParentModName:
            parent_mod = self.GetModule(self.m_ParentModName)
            a_Prefix = parent_mod.RegisterSelect(a_ButtonName, access_mode, only_parent = False)

        if not only_parent:
            a_Prefix =  bd_item_select.SelectRegisterHandlers(self.m_Bot, \
                        self.SelectSourceTemplate(a_Prefix, a_ButtonName), \
                        self.m_GetButtonNameAndKeyValueAndAccessFunc, \
                        self.GetMessage(Messages.SELECT), \
                        self.m_GetAccessFunc,\
                        access_mode = access_mode\
                        )

        return a_Prefix

    def AdditionalKeyboardForEditTemplate(self, a_Field, user_access = user_access.AccessMode.EDIT):
        if a_Field.m_Type == bd_table.TableFieldType.ENUM:
            def KeyboardButtons(a_Message, a_UserGroups):
                cur_buttons = []
                for s in a_Field.m_Enum:
                    cur_buttons += [keyboard.ButtonWithAccess(self.GetButton(EnumButton(s)), user_access, self.GetAccess()),]
                return keyboard.MakeButtons(self.m_Bot, cur_buttons, a_UserGroups)
            return KeyboardButtons
        return None

    def PostProccessingForFieldForEditTemplate(self, a_Field):
        if a_Field.m_Type == bd_table.TableFieldType.ENUM:
            def PostProccessing(a_Message):
                for s in a_Field.m_Enum:
                    if a_Message == str(self.GetButton(EnumButton(s))):
                        return str(s)
                return a_Message
            return PostProccessing
        return None

    async def OnChangeField(self, a_Field, a_ItemID, a_ItemData, a_EditUserID):
        subscribe_type = bot_subscribes.SubscribeType.ANY_ITEM_EDIT
        item_id = -1
        await self.SendSubscribe(subscribe_type, item_id, a_EditUserID)

        subscribe_type = bot_subscribes.SubscribeType.ITEM_EDIT
        item_id = a_ItemID
        await self.SendSubscribe(subscribe_type, item_id, a_EditUserID)


        table_name = self.m_Table.GetName()
        key_name = self.GetKeyFieldName()

        cur_item = GetCurItem(self.m_Bot, table_name, key_name, a_ItemID)
        print(cur_item)

        parent_id_field_index = self.m_Table.GetFieldIDByDestiny(bd_table.TableFieldDestiny.PARENT_ID)
        if parent_id_field_index and cur_item and cur_item[parent_id_field_index]:
            subscribe_type = bot_subscribes.SubscribeType.ANY_ITEM_EDIT_WITH_PARENT
            item_id = cur_item[parent_id_field_index]
            await self.SendSubscribe(subscribe_type, item_id, a_EditUserID)

    def RegisterEdit(self, a_Field, a_AccessMode = user_access.AccessMode.EDIT):
            a_ButtonName = self.GetButton(EditButton(a_Field.m_Destiny))
            a_EditMessage = self.GetMessage(EditMessage(a_Field.m_Destiny))
            a_FieldName = a_Field.m_Name

            a_FieldType = bd_item.FieldType.text
            if a_Field.m_Type == bd_table.TableFieldType.PHOTO:
                a_FieldType = bd_item.FieldType.photo

            if not a_ButtonName or not a_EditMessage:
                return

            async def OnChange(a_ItemID, a_ItemData, a_EditUserID):
                await self.OnChangeField(a_Field, a_ItemID, a_ItemData, a_EditUserID)
                return self.OnChange()

            table_name = self.m_Table.GetName()
            key_name = self.GetKeyFieldName()
            edit_keyboard_func = self.m_GetEditKeyboardButtonsFunc
            GetButtonNameAndKeyValueAndAccess = self.m_GetButtonNameAndKeyValueAndAccessFunc
            GetAccess = self.m_GetAccessFunc

            a_Prefix = self.RegisterSelect(a_ButtonName, user_access.AccessMode.VIEW, only_parent = True)

            bd_item_edit.EditBDItemRegisterHandlers(self.m_Bot, \
                self.SelectSourceTemplate(a_Prefix, a_ButtonName), \
                MakeFSMForEdit(self.GetName(), a_FieldName), \
                self.GetMessage(Messages.SELECT_TO_EDIT), \
                self.ShowMessageTemplate(a_EditMessage), \
                self.ShowMessageTemplate(self.GetMessage(Messages.SUCCESS_EDIT)), \
                table_name, \
                key_name, \
                a_FieldName, \
                self.PostProccessingForFieldForEditTemplate(a_Field),\
                GetButtonNameAndKeyValueAndAccess, \
                GetAccess, \
                self.AdditionalKeyboardForEditTemplate(a_Field),\
                edit_keyboard_func, \
                OnChange,\
                access_mode = a_AccessMode, \
                field_type = a_FieldType\
                )

    def GetAddFields(self):
        add_destiny = (
            bd_table.TableFieldDestiny.NAME,
            bd_table.TableFieldDestiny.DESC,
            bd_table.TableFieldDestiny.PHOTO,
            bd_table.TableFieldDestiny.SUBSCRIBE_TYPE,
            bd_table.TableFieldDestiny.ITEM_ID,
            bd_table.TableFieldDestiny.USER_NAME,
            bd_table.TableFieldDestiny.USER_FAMILY_NAME,
            bd_table.TableFieldDestiny.USER_MIDDLE_NAME,
            bd_table.TableFieldDestiny.USER_BIRTHDAY,
            bd_table.TableFieldDestiny.USER_ADDRESS,
            bd_table.TableFieldDestiny.USER_CONTACTS,
            bd_table.TableFieldDestiny.USER_CONFIRM,
        )
        fields = []
        for f in self.m_Table.GetFields():
            if f.m_Destiny in add_destiny:
                fields += [f]
        return fields

    def AddBDItemRegisterHandlers(self, a_StartCheckFunc, a_AddBDItemFunc, a_ParentPrefix, a_ParentTableName : str, a_ParentKeyFieldName, a_GetButtonNameAndKeyValueAndAccessFunc, a_AccessFunc, a_ButtonFunc, access_mode = user_access.AccessMode.ADD):
        fields = self.GetAddFields()
        if len(fields) == 0:
            return
        fsm = MakeFSMForAdd(self.GetName(), fields)

        keyboard_cancel = bd_item.GetCancelKeyboardButtonsTemplate(self.m_Bot, a_AccessFunc, access_mode)
        keyboard_skip_and_cancel = bd_item.GetSkipAndCancelKeyboardButtonsTemplate(self.m_Bot, a_AccessFunc, access_mode)
        reg_func = self.m_Bot.RegisterMessageHandler

        if a_ParentTableName:
            reg_func = self.m_Bot.RegisterCallbackHandler

        def GetFieldType(a_Field):
            if a_Field.m_Type == bd_table.TableFieldType.PHOTO:
                return bd_item.FieldType.photo
            return bd_item.FieldType.text

        def GetContentTypes(a_Field):
            if a_Field.m_Type == bd_table.TableFieldType.PHOTO:
                return ['photo', 'text']
            return ['text']

        def GetKeyboard(a_Field):
            if a_Field.m_Type == bd_table.TableFieldType.PHOTO:
                return keyboard_skip_and_cancel
            return keyboard_cancel

        f_id = 0
        f = fields[f_id]
        keyboard = bd_item.MixKeyboardFuncTemplate(self.AdditionalKeyboardForEditTemplate(f, user_access.AccessMode.ADD), GetKeyboard(f))
        reg_func(bd_item_add.StartAddBDItemTemplate(self.m_Bot, fsm, getattr(fsm, f'item{f_id}'), self.ShowMessageTemplate(self.GetMessage(CreateMessage(f.m_Destiny))), a_ParentTableName, a_ParentKeyFieldName, a_ParentPrefix, a_AccessFunc, keyboard, a_ButtonFunc, access_mode), a_StartCheckFunc)

        for i in range(len(fields) - 1):
            f = fields[i]
            next_f = fields[i + 1]
            keyboard = bd_item.MixKeyboardFuncTemplate(self.AdditionalKeyboardForEditTemplate(next_f, user_access.AccessMode.ADD), GetKeyboard(next_f))
            self.m_Bot.RegisterMessageHandler(bd_item_add.NextAddBDItemTemplate(self.m_Bot, fsm, None, a_ParentTableName, a_ParentKeyFieldName, f.m_Name, self.ShowMessageTemplate(self.GetMessage(CreateMessage(next_f.m_Destiny))), None, a_AccessFunc, keyboard, a_ButtonFunc, access_mode, field_type = GetFieldType(f)), content_types = GetContentTypes(f), state = getattr(fsm, f'item{i}'))

        f_id = len(fields) - 1
        f = fields[f_id]
        self.m_Bot.RegisterMessageHandler(bd_item_add.FinishAddBDItemTemplate(self.m_Bot, fsm, a_AddBDItemFunc, a_ParentTableName, a_ParentKeyFieldName, f.m_Name, self.ShowMessageTemplate(self.GetMessage(Messages.SUCCESS_CREATE)), None, a_AccessFunc, a_ButtonFunc, access_mode, field_type = GetFieldType(f)), content_types = GetContentTypes(f), state = getattr(fsm, f'item{f_id}'))

    def GetKeyFieldDestiny(self):
        return bd_table.TableFieldDestiny.KEY

    def GetKeyFieldName(self):
        return self.m_Table.GetFieldNameByDestiny(self.GetKeyFieldDestiny())

    def GetKeyFieldID(self):
        return self.m_Table.GetFieldIDByDestiny(self.GetKeyFieldDestiny())

    def RegisterHandlers(self):
        super().RegisterHandlers()
        table_name = self.m_Table.GetName()
        key_name = self.GetKeyFieldName()
        name_field = self.m_Table.GetFieldNameByDestiny(bd_table.TableFieldDestiny.NAME)
        desc_field = self.m_Table.GetFieldNameByDestiny(bd_table.TableFieldDestiny.DESC)
        photo_field = self.m_Table.GetFieldNameByDestiny(bd_table.TableFieldDestiny.PHOTO)
        access_field = self.m_Table.GetFieldNameByDestiny(bd_table.TableFieldDestiny.ACCESS)
        def_access_field = self.m_Table.GetFieldNameByDestiny(bd_table.TableFieldDestiny.DEFAULT_ACCESS)
        parent_id_field = self.m_Table.GetFieldNameByDestiny(bd_table.TableFieldDestiny.PARENT_ID)

        parent_table_name = None
        parent_key_name = None
        if self.m_ParentModName:
            parent_mod = self.GetModule(self.m_ParentModName)
            parent_table_name = parent_mod.m_Table.GetName()
            parent_key_name = parent_mod.GetKeyFieldName()

        def GetViewItemInlineKeyboardTemplate(a_ItemID):
            return self.GetViewItemInlineKeyboardTemplate(a_ItemID)

        GetButtonNameAndKeyValueAndAccess = self.m_GetButtonNameAndKeyValueAndAccessFunc
        GetAccess = self.m_GetAccessFunc

        defaul_keyboard_func = self.m_GetStartKeyboardButtonsFunc

        # Список 
        a_ButtonName = self.GetButton(ButtonNames.LIST)
        if a_ButtonName:
            a_Prefix = self.RegisterSelect(a_ButtonName, user_access.AccessMode.VIEW, only_parent = True)
            self.m_SelectPrefix = a_Prefix
            a_Prefix = bd_item_select.SelectRegisterHandlers(self.m_Bot,\
                    self.SelectSourceTemplate(a_Prefix, a_ButtonName), \
                    GetButtonNameAndKeyValueAndAccess,\
                    self.GetMessage(Messages.SELECT),\
                    GetAccess,\
                    access_mode = user_access.AccessMode.VIEW\
                    )
            bd_item_view.ShowBDItemRegisterHandlers(self.m_Bot,\
                    a_Prefix,\
                    table_name,\
                    key_name,\
                    self.ShowMessageTemplate(self.GetMessage(Messages.OPEN), GetViewItemInlineKeyboardTemplate, a_EnablePhoto = True),\
                    GetAccess,\
                    defaul_keyboard_func,\
                    access_mode = user_access.AccessMode.VIEW\
                    )

        # Удаление 
        a_ButtonName = self.GetButton(ButtonNames.DEL)
        if a_ButtonName:
            a_Prefix = self.RegisterSelect(a_ButtonName, user_access.AccessMode.VIEW)
            bd_item_delete.DeleteBDItemRegisterHandlers(self.m_Bot, \
                    a_Prefix, \
                    table_name, \
                    key_name, \
                    self.m_PreDeleteFunc, \
                    self.m_PostDeleteFunc, \
                    GetAccess, \
                    defaul_keyboard_func\
                    )

        # Добавление 
        a_ButtonName = self.GetButton(ButtonNames.ADD)
        if a_ButtonName:
            a_Prefix = self.RegisterSelect(a_ButtonName, user_access.AccessMode.VIEW, only_parent = True)

            check_func = bd_item.GetCheckForTextFunc(a_ButtonName)
            if a_Prefix:
                check_func = bd_item.GetCheckForPrefixFunc(a_Prefix)

            self.AddBDItemRegisterHandlers(\
                    check_func, \
                    self.m_AddBDItemFunc, \
                    a_Prefix,\
                    parent_table_name, \
                    parent_key_name, \
                    GetButtonNameAndKeyValueAndAccess, \
                    GetAccess, \
                    self.m_GetStartKeyboardButtonsFunc\
                    )

        # Редактирование
        edit_keyboard_func = self.m_GetEditKeyboardButtonsFunc
        a_ButtonName = self.GetButton(ButtonNames.EDIT)
        if a_ButtonName:
            self.m_Bot.RegisterMessageHandler(\
                simple_message.InfoMessageTemplate(\
                        self.m_Bot,\
                        self.GetMessage(Messages.START_EDIT),\
                        edit_keyboard_func,\
                        None,\
                        GetAccess,\
                        access_mode = user_access.AccessMode.EDIT),\
                        bd_item.GetCheckForTextFunc(a_ButtonName)\
                        )

        address_field = self.m_Table.GetFieldNameByDestiny(bd_table.TableFieldDestiny.ADDRESS)
        status_field = self.m_Table.GetFieldNameByDestiny(bd_table.TableFieldDestiny.STATUS)

        for f in self.m_Table.GetFields():
            self.RegisterEdit(f)

    def OnChange(self):
        pass