from itertools import accumulate

from yattag import Doc, indent


class XMLGenerator:
    """
    Генератор XML синхронизации в формате, совместимом с test1_full/srv_interp.py
    """

    def generate(self, sync_data: dict, path: str, hw):
        number_of_blocks = sync_data["number_of_blocks"]
        gate_rf = sync_data["gate_rf"]
        gate_tr_switch = sync_data["gate_tr_switch"]
        gate_adc = sync_data["gate_adc"]
        blocks_duration = sync_data["blocks_duration"]
        synchro_block_timer = sync_data["synchro_block_timer"]
        min_block_time = sync_data["min_block_time"]

        doc, tag, text = Doc().tagtext()

        with tag("root"):
            with tag("ParamCount"):
                text(number_of_blocks + 1)

            adc_times_values = []
            adc_times_starts = []

            # Префиксные суммы длительностей: duration_prefix[k] ==
            # sum(blocks_duration[0:k]). Считаются один раз, иначе суммирование
            # на каждом ADC-фронте давало O(N^2) на длинных последовательностях.
            duration_prefix = list(accumulate(blocks_duration, initial=0))

            # Массивы синхронизатора имеют ведущий seed-элемент на индексе 0
            # (стартовый блок), значимые блоки идут по индексам 1..nb. Seed
            # представлен заголовочным тегом (*1), поэтому в цикле эмитим
            # элементы 1..nb: block_iter k -> индекс массива k + 1.
            # (Совпадает с эталонной логикой srv_interp.synchronization.)

            with tag("RF"):
                with tag("RF1"):
                    text(0)
                for rf_iter in range(number_of_blocks):
                    with tag("RF" + str(rf_iter + 2)):
                        text(gate_rf[rf_iter + 1])

            with tag("SW"):
                with tag("SW1"):
                    text(1)
                for sw_iter in range(number_of_blocks):
                    with tag("SW" + str(sw_iter + 2)):
                        text(gate_tr_switch[sw_iter + 1])

            with tag("ADC"):
                with tag("ADC1"):
                    text(0)
                for adc_iter in range(number_of_blocks):
                    idx = adc_iter + 1
                    # Непрерывный участок HIGH (в т.ч. собранный из разбитых
                    # блоков) считается одним событием ADC: засекаем фронт и
                    # суммируем длительность всего участка для points-окна.
                    is_rising_edge = (
                        gate_adc[idx] == 1 and gate_adc[idx - 1] == 0
                    )
                    if is_rising_edge:
                        adc_times_starts.append(duration_prefix[idx])
                        run_duration = 0
                        run_iter = idx
                        while (run_iter < len(gate_adc)
                               and gate_adc[run_iter] == 1):
                            run_duration += blocks_duration[run_iter]
                            run_iter += 1
                        adc_times_values.append(run_duration)
                    with tag("ADC" + str(adc_iter + 2)):
                        text(gate_adc[idx])

            with tag("GR"):
                with tag("GR1"):
                    text(1)
                for gr_iter in range(number_of_blocks):
                    # Последнее событие импульсной последовательности
                    # переводим из LOW в HIGH (включение градиентной системы)
                    gr_value = 1 if gr_iter == number_of_blocks - 1 else 0
                    with tag("GR" + str(gr_iter + 2)):
                        text(gr_value)

            with tag("CL"):
                with tag("CL1"):
                    text(int(min_block_time / synchro_block_timer))
                for cl_iter in range(number_of_blocks):
                    with tag("CL" + str(cl_iter + 2)):
                        text(int(blocks_duration[cl_iter + 1] / synchro_block_timer))

        xml_string = indent(doc.getvalue(), indentation=" " * 4, newline="\r")
        with open(path, "w", encoding="utf-8") as f:
            f.write(xml_string)

        return adc_times_values, adc_times_starts
