#!/usr/bin/env python3
"""
Имитатор параллельной шины для gpio-interrupt (Raspberry Pi 4)
"""

import gpiod
import time
import argparse
import sys
import signal
from gpiod.line import Direction, Value


class ParallelBusImitator:
    """Имитатор 7-битной параллельной шины данных"""

    # Временные константы (секунды)
    DATA_SETUP_TIME = 0.000005
    STROBE_WIDTH = 0.000005
    HOLD_TIME = 0.000005

    WR_PIN = 12  # BCM12

    DATA_PINS_MAPPING = [
        (21, 18, "D0 (LSB)"),
        (7, 27, "D1"),
        (6, 22, "D2"),
        (5, 23, "D3"),
        (25, 17, "D4"),
        (24, 20, "D5"),
        (23, 21, "D6"),
        (22, None, "D7 (не подключён)"),
    ]

    def __init__(self, use_mask=True, chip_path='/dev/gpiochip0'):
        self.use_mask = use_mask
        self.counter = 0
        self.active_pins = []

        for wpi, bcm, desc in self.DATA_PINS_MAPPING:
            if bcm is not None:
                self.active_pins.append((wpi, bcm, desc))

        self.data_pins_bcm = [bcm for _, bcm, _ in self.active_pins]
        self.all_pins = self.data_pins_bcm + [self.WR_PIN]

        try:
            self.chip = gpiod.Chip(chip_path)
            self.lines = self.chip.request_lines(
                {
                    p: gpiod.LineSettings(
                        direction=Direction.OUTPUT,
                        output_value=Value.INACTIVE
                    )
                    for p in self.all_pins
                },
                consumer="par-bus-imitator"
            )
        except PermissionError:
            print("Нет прав доступа к GPIO. Используйте sudo.")
            sys.exit(1)
        except Exception as e:
            print(f"Ошибка GPIO: {e}")
            sys.exit(1)

        bit_width = 4 if use_mask else len(self.data_pins_bcm)

        print(f"✓ Имитатор шины ({bit_width}-бит режим)")
        print("Нажмите Ctrl+C для остановки\n")

    def send_byte(self, value):
        """Отправка одного байта"""
        if self.use_mask:
            value &= 0x0F
        else:
            value &= 0x7F

        out = {}

        for bit_pos, bcm_pin in enumerate(self.data_pins_bcm):
            bit = (value >> bit_pos) & 1
            out[bcm_pin] = Value.ACTIVE if bit else Value.INACTIVE

        self.lines.set_values(out)
        time.sleep(self.DATA_SETUP_TIME)

        self.lines.set_value(self.WR_PIN, Value.ACTIVE)
        time.sleep(self.STROBE_WIDTH)
        self.lines.set_value(self.WR_PIN, Value.INACTIVE)
        time.sleep(self.HOLD_TIME)

        self.counter += 1
        self.show_status(value)

    def show_status(self, value):
        """Красивый статус без спама"""
        bits = 4 if self.use_mask else 7
        bin_val = format(value, f'0{bits}b')

        msg = (
            f"\r▶ Передача: "
            f"HEX=0x{value:02X} | "
            f"BIN={bin_val} | "
            f"Пакетов: {self.counter}"
        )

        print(msg, end='', flush=True)

    def send_sequence(self, values, delay=0.2):
        for val in values:
            self.send_byte(val)
            time.sleep(delay)

    def cleanup(self):
        if hasattr(self, 'lines'):
            self.lines.release()
        if hasattr(self, 'chip'):
            self.chip.close()


def handle_signal(signum, frame):
    print("\nЗавершение работы...")
    sys.exit(0)


def main():
    signal.signal(signal.SIGINT, handle_signal)
    signal.signal(signal.SIGTERM, handle_signal)

    parser = argparse.ArgumentParser(
        description='Имитатор параллельной шины'
    )

    parser.add_argument('--no-mask', action='store_true',
                        help='7-битный режим')
    parser.add_argument('--test',
                        choices=['default', 'counter', 'pattern'],
                        default='default')
    parser.add_argument('--delay', type=float, default=0.2)
    parser.add_argument('--single', type=str)
    parser.add_argument('--chip', default='/dev/gpiochip0')

    args = parser.parse_args()

    imitator = ParallelBusImitator(
        use_mask=not args.no_mask,
        chip_path=args.chip
    )

    try:
        if args.single:
            val = int(args.single, 16)
            imitator.send_byte(val)
            print()
            return

        if args.test == 'default':
            test_data = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40]
        elif args.test == 'counter':
            test_data = list(range(128))
        else:
            test_data = [0x00, 0x01, 0x03, 0x07, 0x0F, 0x55, 0x2A]

        while True:
            imitator.send_sequence(test_data, args.delay)

    except KeyboardInterrupt:
        print("\nОстановка...")
    except Exception as e:
        print(f"\nОшибка: {e}")
    finally:
        imitator.cleanup()
        print("✓ GPIO освобождены")


if __name__ == "__main__":
    main()
