extends Node

enum PrdState {WORK, CONTROL}

## Границы секторов блоков ФС по частоте в режиме РТО.
const FS_BAND_RTO: Dictionary = {
    0: [200.0, 250.0],
    1: [250.0, 300.0],
    2: [300.0, 350.0],
    3: [350.0, 400.0],
    4: [400.0, 450.0],
    5: [450.0, 500.0],
    6: [500.0, 550.0] }

## Границы секторов блоков ФС по частоте в режиме РЛС.
const FS_BAND_RLS: Dictionary = {
    0: [480.0, 490.0],
    1: [490.0, 500.0],
    2: [500.0, 510.0],
    3: [510.0, 520.0],
    4: [520.0, 530.0],
    5: [530.0, 540.0],
    6: [540.0, 550.0] }

## Границы секторов блоков ФС по пеленгу.
const FS_SECTORS: Dictionary = {
    0: [0.0, 90.0],
    3: [90.0, 180.0],
    2: [180.0, 270.0],
    1: [270.0, 360.0] }

const FS_FREQ: Dictionary = {
    0: [390.0,  510.0, 450.0],
    1: [800.0,  1065.0, 932.0],
    2: [1065.0, 1210.0, 1137.0],
    3: [1150.0, 1380.0, 1215.0],
    4: [1380.0, 2000.0, 1575.0],
    5: [2000.0, 3500.0, 2750.0],
    6: [3500.0, 6000.0, 4750.0] }

## Названия приборов ПРД
const PRD_NAMES: Array = [
    'уарэп-яу07-1н',
    'уарэп-яу07-1в',
    'уарэп-яу07-1к',
    'уарэп-яу07-2н',
    'уарэп-яу07-2в',
    'уарэп-яу07-2к',
    'уарэп-яу07-3н',
    'уарэп-яу07-3в',
    'уарэп-яу07-3к',
    'уарэп-яу07-4н',
    'уарэп-яу07-4в',
    'уарэп-яу07-4к', ]

## Соответствие модулей ФС их нахождению в приборе ПРД и номеру от КАПС РПБ
const FS_PRD: Dictionary = {
    0:  ['уарэп-яу07-1к', 11],
    1:  ['уарэп-яу07-4к', 41],
    2:  ['уарэп-яу07-3к', 31],
    3:  ['уарэп-яу07-2к', 21],
    4:  ['уарэп-яу07-1н', 12],
    5:  ['уарэп-яу07-4н', 42],
    6:  ['уарэп-яу07-3н', 32],
    7:  ['уарэп-яу07-2н', 22],
    8:  ['уарэп-яу07-1н', 13],
    9:  ['уарэп-яу07-4н', 43],
    10: ['уарэп-яу07-3н', 33],
    11: ['уарэп-яу07-2н', 23],
    12: ['уарэп-яу07-1в', 14],
    13: ['уарэп-яу07-4в', 44],
    14: ['уарэп-яу07-3в', 34],
    15: ['уарэп-яу07-2в', 24],
    16: ['уарэп-яу07-1в', 15],
    17: ['уарэп-яу07-4в', 45],
    18: ['уарэп-яу07-3в', 35],
    19: ['уарэп-яу07-2в', 25],
    20: ['уарэп-яу07-1к', 16],
    21: ['уарэп-яу07-4к', 46],
    22: ['уарэп-яу07-3к', 36],
    23: ['уарэп-яу07-2к', 26],
    24: ['уарэп-яу07-1к', 17],
    25: ['уарэп-яу07-4к', 47],
    26: ['уарэп-яу07-3к', 37],
    27: ['уарэп-яу07-2к', 27], }

var fs_dict: Dictionary  ## Словарь с модулями ФС
var prd_dict: Dictionary ## Словарь с приборами ПРД
var course = 0.0         ## Курс корабля
var fs_caps_id: Dictionary   ## Словарь соответствия номеров ФС от КАПС РПБ внутрипрограммной нумерации


class Fs:
    const UM_PORTS: Dictionary = {
    1 : 0x106,
    2 : 0x108,
    3 : 0x106,
    4 : 0x108,
    5 : 0x106,
    6 : 0x108 }

    const CODE_UM: Array = [
    [-5, 0],
    [0 , 10],
    [10, 20],
    [20, 30],
    [30, 40],
    [40, 50],
    [50, 55]]

    var index                            ## Индекс блока усилителей
    var modulation: bool                 ## Состояние модуляции
    var prd_name: String                 ## Ипя прибора ПРД в котором стоит модуль
    var um_port: int = 0                 ## Порт для угла места
    var um_code_tx: int = 0              ## Код угла места для отправки в прибор
    var um_code_rx: int = 0              ## Текущий код угла места
    var isg: int = 2                     ## Состояние модуля fs от КАПС РПБ
    var pow_caps_rx: float = 0.0         ## Мощность излучения принятая от КАПС РПБ
    var freq_caps_rx: float = 0.0        ## Частота  принятая от КАПС РПБ
    var width_caps_rx: float = 0.0       ## Ширина полосы принятая от КАПС РПБ
    var msvk: Array

    signal update_modulation(index)

    func _init(i, p_name):
        prd_name = p_name
        index = i
        if UM_PORTS.has(index/4):
            um_port = UM_PORTS[index/4]

    func set_modulation(val):
        modulation = val

    func set_um_code(um: float):
        if um < CODE_UM[0][0]:
            um_code_tx = 0
        elif um > CODE_UM[len(CODE_UM) - 1][1]:
            um_code_tx = len(CODE_UM) - 1
        else:
            for i in CODE_UM.size():
                if (um >= CODE_UM[i][0]) and (um < CODE_UM[i][1]):
                     um_code_tx = i

    func set_um_data(data_arr, u):
        if UM_PORTS.has(index/4):
            var val = um_code_tx
            if u.isa_ports.has(um_port):
                val = tools.set_bits(u.isa_ports[um_port], um_code_tx, 0x0007)
            data_arr.append(um_port)
            data_arr.append(val)


class PRD:
    var prd_name: String                                ## Имя прибора
    var unit_yau07                                      ## Юнит эмс
    var fs_dic: Dictionary                              ## Массив блоков ФС
    var yemsg_board: yemsboards.EmsBoard                ## Плата ЭМС-Г
    var state = PrdState.WORK
    var cmd_array: Array                                ## Очередь команд
    var ug_online: bool = false                         ## Состояние ячейки УГ
    var serviceability: bool = false
    var set_ug_state: bool = true                       ## Необходимо ли записать в УГ настройки
    signal update_serviceability()

    func _init(u_name, u_yau07):
        prd_name = u_name
        unit_yau07 = u_yau07
        init_ems_g()

    func init_ems_g():
        var in_tbl: Array = []
        var out_tbl: Array = []
        for j in yemsconsts.NUM_INPUT_G:
            in_tbl.append([j+1])
            out_tbl.append([j+yemsconsts.STATUS_OUT_OFFSET])
        yemsg_board = yemsboards.EmsBoard.new(yemsconsts.EMG_G_ID, 0x120, in_tbl, out_tbl, unit_yau07)
        yemsg_board.ems_g_fn = yemsconsts.EMS_G_BOARDS[prd_name]
        var params = ['','',0.0, 0,0]
        for item in yemsg_board.in_tbl:
            var in_dev: yemsdevs.InDev = yemsdevs.InDev.new(item[0], params, 0, 0x120)
            yemsg_board.in_devs[in_dev.id] = in_dev
        for item in yemsg_board.out_tbl:
            var out_dev = yemsdevs.OutDev.new(item[0], params, 0, 0x120)
            yemsg_board.out_devs[out_dev.id] = out_dev
        yemsg_board.need_write = true
        unit_yau07.connect('data_received', Callable(self, 'on_data_received'))
        unit_yau07.connect('command_done', Callable(self, 'on_command_done'))
        unit_yau07.connect('line_changed', Callable(self, 'on_line_changed'))


    func on_command_done(u):
        if yemsg_board.state_board != yemsg_board.WAIT or yemsg_board.state_cmd != yemsg_board.WAIT:
            yemsg_board.on_command_done(u)
            return
        for fs in fs_dic.values():
            for port in u.isa_ports.keys():
                if port == fs.um_port:
                    fs.um_code_rx = u.isa_ports[port] & 0x7


    func on_line_changed(unit_ems):
        emit_signal('update_serviceability')
        if not unit_ems.online:
            yemsg_board.ems_load = yemsg_board.LoadState.NOT_DEFINED
            yemsg_board.set_wait()
            set_ug_state = true

    func on_data_received(_u):
        set_prd_status()
        if yemsg_board.state_board != yemsg_board.WAIT or yemsg_board.state_cmd != yemsg_board.WAIT:
            return
        if yemsg_board.online:
            if yemsg_board.ems_load == yemsg_board.LoadState.NOT_LOAD:
                start_load()
            elif yemsg_board.ems_load == yemsg_board.LoadState.IN_PROCESS:
                yemsg_board.set_load_done()
            elif (yemsg_board.ems_load == yemsg_board.LoadState.DONE) and len(yemsg_board.cmd_array):
                yemsg_board.execute_cmd()
            else:
                execute_cmd()
            return
        execute_cmd()

    func set_prd_status():
        yemsg_board.set_status(unit_yau07.status)
        ug_online = (unit_yau07.status[0] >> 1) & 1
        if not ug_online:
            set_ug_state = true
        var ukp_1_online = (unit_yau07.status[0] >> 2) & 1
        var ukp_2_online = (unit_yau07.status[0] >> 3) & 1
        var ems_g_online = (unit_yau07.status[0] & 1)  & 1
        var check_serviceability = ug_online and ukp_1_online and ukp_2_online and ems_g_online
        if serviceability != check_serviceability:
            serviceability = check_serviceability
            emit_signal('update_serviceability')


    func execute_cmd():
        if not len(cmd_array): return
        if unit_yau07.cmd_state != unit_yau07.CmdState.DONE: return
        var cmd: Array = cmd_array.pop_front()
        unit_yau07.send_isa(cmd[0], cmd[1])

    func start_load():
        var file = FileAccess.open(yemsconsts.emsg_files_folder + yemsg_board.ems_g_fn, FileAccess.READ)
        var abs_dir = ProjectSettings.globalize_path(yemsconsts.emsg_files_folder + yemsg_board.ems_g_fn)
        if file == null:
            yemsg_board.ems_load = yemsg_board.LoadState.FAIL
            yemsg_board.set_wait()
            log.error('Не удалось прочитать файл настроек ячейки ЭМС-Г %s' %abs_dir)
            return
        var read = file.get_as_text()
        file.close()
        var json_conv: = JSON.new()
        if Error.OK == json_conv.parse(read):
            var json_data = json_conv.get_data()
            yemsg_board.need_write = true
            yemsg_board.ems_load = yemsg_board.LoadState.IN_PROCESS
            var read_ok = yemsg_board.btn_load(json_data)
            if read_ok:
                yemsg_board.state_board = yemsg_board.WRITE
                yemsg_board.start_rw_kems(false)
            else:
                yemsg_board.ems_load = yemsg_board.LoadState.FAIL
                yemsg_board.set_wait()
                log.error('Не удалось прочитать файл настроек ячейки ЭМС-Г %s' %abs_dir)
        else:
            yemsg_board.ems_load = yemsg_board.LoadState.FAIL
            yemsg_board.set_wait()
            log.error('Не удалось прочитать файл настроек ячейки ЭМС-Г %s' %abs_dir)


    func on_update_modulation(index):
        yemsg_board.add_cmd(index/4, fs_dic[index].modulation)

    func set_um(data_arr):
        cmd_array.append([unit_yau07.CmdCode.WRITE_ISA, data_arr])


func _ready() -> void:
    check_dir()
    for prd_name in PRD_NAMES:
        var unit_prd = network.get_unit_instance(prd_name)
        var new_prd = PRD.new(prd_name, unit_prd)
        prd_dict[prd_name] = new_prd
    for key in FS_PRD.keys():
        var fs = Fs.new(key, FS_PRD[key][0])
        fs_dict[key] = fs
        if prd_dict.has(fs.prd_name):
            prd_dict[fs.prd_name].fs_dic[key] = fs
            fs.connect('update_modulation', prd_dict[fs.prd_name].on_update_modulation.bind(fs.index))

    for key in FS_PRD.keys():
        fs_caps_id[FS_PRD[key][1]] = key
    var unit_caps = network.get_unit_instance('уарэп-капсрпб')
    unit_caps.connect('data_received', Callable(self, 'on_data_capsrpb_received'))
    signaller.connect('режим_контроль', Callable(self, 'on_mode_changed').bind(PrdState.CONTROL))
    signaller.connect('режим_работа', Callable(self, 'on_mode_changed').bind(PrdState.WORK))
    var unit_nav = network.get_unit_instance('навигация')
    unit_nav.connect('data_received', on_navi_data_received)


func get_fs_param(index: int) -> Array:
    var freq = (FS_FREQ[index / 4][2])
    var sector = (FS_SECTORS[index % 4][0] + FS_SECTORS[index % 4][1])/2 - 45.0
    if sector < 0:
        sector += 360.0
    var width = FS_FREQ[index / 4][1] - FS_FREQ[index / 4][0]
    return [sector, freq, width, index]


## Функция возвращает массив с индексами модулей ФС для выбранной помехи
func get_fs_index(ecm, fs_index_arr):
    var angle: float = ecm.kni
    if ecm.params.has('freq'):
        find_fs_index(angle, ecm.params['freq'], fs_index_arr)


func find_fs_index(aoa: float, freq: int, fs_index_arr: Array):
    var sector: int
    var band: Array
    var angle = aoa + 45.0
    if angle > 360.0:
        angle -= 360.0
    for sec in FS_SECTORS.values():
        if float(angle) >= sec[0] and float(angle) <= sec[1]:
            sector = FS_SECTORS.find_key(sec)
    for diap in FS_FREQ.values():
        if freq >= diap[0] and freq <= diap[1]:
            band.append(FS_FREQ.find_key(diap))
    for item in band:
        fs_index_arr.append(item * 4 + sector)


## Обновление состояния модулей формирователя сигналов
func update_fs_state(ecms, trenaz_mode: bool):
    for fs in fs_dict.values():             ## Запрет модуляции для всех ФС
        fs.set_modulation(false)
    for ecm in ecms.values():               ## Выставление состояния помехи в ФС
        var fs_ids: Array
        get_fs_index(ecm, fs_ids)
        for fs_index in fs_ids:
            var fs = fs_dict[fs_index]
            fs.set_um_code(ecm.um)
    for prd_module in prd_dict.values():    ## Отправка команды на исполнение в ПРД
        if prd_module.state != PrdState.WORK: continue
        if not prd_module.unit_yau07.online: continue
        if not trenaz_mode:
            prd_module.cmd_array.append([prd_module.unit_yau07.CmdCode.WRITE_ISA, [prd_module.yemsg_board.base_addr_isa, 6666]])
        var um_data_array: Array = []
        for fs in prd_module.fs_dic.values():
            if not prd_module.ug_online: continue
            if prd_module.set_ug_state:
                fs.set_um_data(um_data_array, prd_module.unit_yau07)
            else:
                if fs.um_code_tx != fs.um_code_rx:
                    fs.set_um_data(um_data_array, prd_module.unit_yau07)
        if um_data_array.size():
            prd_module.set_ug_state = false
            prd_module.set_um(um_data_array)



## Проверяет ниличие директории, если их нет, создаёт
func check_dir():
    var dir = DirAccess.open(yemsconsts.emsg_files_folder)
    if dir: return
    else: DirAccess.make_dir_absolute(yemsconsts.emsg_files_folder)


## Производит приём состояния модулей ФС.
func on_data_capsrpb_received(unit0: capsrpb.CapsRpb):
    if not unit0.json_dic.has('ts'):    return
    if not unit0.json_dic['ts'] == 7:   return
    if not unit0.json_dic.has('msfs'):  return

    var msfs = unit0.json_dic['msfs']
    for fs in msfs:
        if fs is Dictionary:
            if not fs.has('nmfs'):
                return
            var nmfs = int(fs['nmfs'])
            if not fs_caps_id.has(nmfs):
                continue
            var ii = fs_caps_id[nmfs]
            if not fs_dict.has(ii):
                continue
            var fs_module = fs_dict[ii]
            if fs.has('isg'):
                fs_module.isg = int(fs['isg'])
            if fs.has('pow'):
                fs_module.pow_caps_rx = int(fs['pow'])
            if fs.has('freq'):
                fs_module.freq_caps_rx = int(fs['freq'])
            if fs.has('width'):
                fs_module.width_caps_rx = int(fs['width'])

    signaller.emit_signal('fs_update_state', fs_dict)


## Вызывается при переходе в режим работа/контроль
func on_mode_changed(changed_mode: int) -> void:
    for pdr in prd.prd_dict.values():
        pdr.state = changed_mode


func on_navi_data_received(data: Dictionary):
    course = data.get('k', 0.0)
