#!/bin/bash

# Проверка зависимостей
check_dependencies() {
    local deps=("nasm" "gcc" "ld" "grub-mkrescue" "qemu-system-x86_64")
    local missing=()
    
    echo "[*] Проверка зависимостей..."
    
    for dep in "${deps[@]}"; do
        if ! command -v "$dep" &> /dev/null; then
            missing+=("$dep")
        fi
    done
    
    if [ ${#missing[@]} -gt 0 ]; then
        echo "[-] Отсутствуют зависимости: ${missing[*]}"
        echo "    Установите их:"
        echo "    Ubuntu/Debian: sudo apt-get install nasm gcc make grub-pc-bin qemu-system-x86"
        echo "    Arch: sudo pacman -S nasm gcc make grub qemu-desktop"
        echo "    Fedora: sudo dnf install nasm gcc make grub2-tools qemu-system-x86"
        exit 1
    fi
    
    echo "[+] Все зависимости установлены"
}

# Очистка предыдущей сборки
cleanup() {
    echo "[*] Очистка предыдущей сборки..."
    make clean 2>/dev/null || true
    rm -rf PrismOS_Root 2>/dev/null || true
}

# Основная сборка
main() {
    echo "========================================="
    echo "    PrismOS Builder v1.0"
    echo "========================================="
    
    check_dependencies
    cleanup
    
    # Имя проекта
    PROJECT="PrismOS"
    mkdir -p $PROJECT/src
    mkdir -p $PROJECT/iso/boot/grub
    
    echo "[*] Создание структуры проекта PrismOS..."
    
    # ----------------------------------------------------------------------
    # 1. ЗАГРУЗЧИК (Assembly)
    # ----------------------------------------------------------------------
    echo "[+] Создание загрузчика (boot.s)..."
    cat << 'EOF' > $PROJECT/src/boot.s
; boot.s - точка входа в ядро
MBALIGN     equ  1 << 0             ; выравнивание загрузочных модулей
MEMINFO     equ  1 << 1             ; предоставить карту памяти
FLAGS       equ  MBALIGN | MEMINFO  ; флаги Multiboot
MAGIC       equ  0x1BADB002         ; "магическое" число для загрузчика
CHECKSUM    equ  -(MAGIC + FLAGS)   ; контрольная сумма

section .multiboot
align 4
    dd MAGIC
    dd FLAGS
    dd CHECKSUM

section .bss
align 16
stack_bottom:
    resb 16384 ; 16 KiB стека
stack_top:

section .text
global _start
_start:
start:
    ; Настройка стека (ESP)
    mov esp, stack_top

    ; Сброс EFLAGS
    push 0
    popfd

    ; Передача управления в C-ядро
    extern kernel_main
    call kernel_main

    ; Если ядро вернет управление (чего быть не должно), вешаем CPU
    cli
.hang:
    hlt
    jmp .hang
EOF

    # ----------------------------------------------------------------------
    # 2. ЯДРО (C)
    # ----------------------------------------------------------------------
    echo "[+] Создание ядра (kernel.c)..."
    cat << 'EOF' > $PROJECT/src/kernel.c
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

/* Аппаратные константы VGA */
static const size_t VGA_WIDTH = 80;
static const size_t VGA_HEIGHT = 25;
uint16_t* VGA_MEMORY = (uint16_t*) 0xB8000;

/* Цвета VGA */
enum vga_color {
    VGA_COLOR_BLACK = 0,
    VGA_COLOR_BLUE = 1,
    VGA_COLOR_GREEN = 2,
    VGA_COLOR_CYAN = 3,
    VGA_COLOR_RED = 4,
    VGA_COLOR_MAGENTA = 5,
    VGA_COLOR_BROWN = 6,
    VGA_COLOR_LIGHT_GREY = 7,
    VGA_COLOR_DARK_GREY = 8,
    VGA_COLOR_LIGHT_BLUE = 9,
    VGA_COLOR_LIGHT_GREEN = 10,
    VGA_COLOR_LIGHT_CYAN = 11,
    VGA_COLOR_LIGHT_RED = 12,
    VGA_COLOR_LIGHT_MAGENTA = 13,
    VGA_COLOR_LIGHT_BROWN = 14,
    VGA_COLOR_WHITE = 15,
};

static inline uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg) {
    return fg | bg << 4;
}

static inline uint16_t vga_entry(unsigned char uc, uint8_t color) {
    return (uint16_t) uc | (uint16_t) color << 8;
}

size_t terminal_row;
size_t terminal_column;
uint8_t terminal_color;
uint16_t* terminal_buffer;

/* Инициализация терминала */
void terminal_initialize(void) {
    terminal_row = 0;
    terminal_column = 0;
    terminal_color = vga_entry_color(VGA_COLOR_LIGHT_CYAN, VGA_COLOR_BLACK);
    terminal_buffer = VGA_MEMORY;
    for (size_t y = 0; y < VGA_HEIGHT; y++) {
        for (size_t x = 0; x < VGA_WIDTH; x++) {
            const size_t index = y * VGA_WIDTH + x;
            terminal_buffer[index] = vga_entry(' ', terminal_color);
        }
    }
}

/* Прокрутка (примитивная) */
void terminal_scroll() {
    for (size_t y = 0; y < VGA_HEIGHT - 1; y++) {
        for (size_t x = 0; x < VGA_WIDTH; x++) {
            terminal_buffer[y * VGA_WIDTH + x] = terminal_buffer[(y + 1) * VGA_WIDTH + x];
        }
    }
    for (size_t x = 0; x < VGA_WIDTH; x++) {
        terminal_buffer[(VGA_HEIGHT - 1) * VGA_WIDTH + x] = vga_entry(' ', terminal_color);
    }
    terminal_row = VGA_HEIGHT - 1;
}

/* Вывод символа */
void terminal_putentryat(char c, uint8_t color, size_t x, size_t y) {
    const size_t index = y * VGA_WIDTH + x;
    terminal_buffer[index] = vga_entry(c, color);
}

void terminal_putchar(char c) {
    if (c == '\n') {
        terminal_column = 0;
        if (++terminal_row == VGA_HEIGHT) {
            terminal_scroll();
        }
        return;
    }
    terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
    if (++terminal_column == VGA_WIDTH) {
        terminal_column = 0;
        if (++terminal_row == VGA_HEIGHT)
            terminal_scroll();
    }
}

void terminal_write(const char* data, size_t size) {
    for (size_t i = 0; i < size; i++)
        terminal_putchar(data[i]);
}

void terminal_writestring(const char* data) {
    size_t len = 0;
    const char* p = data;
    while (*p++) len++;
    terminal_write(data, len);
}

/* --- Inline ASM для работы с портами ввода/вывода (IO Ports) --- */
static inline void outb(uint16_t port, uint8_t val) {
    __asm__ volatile ( "outb %0, %1" : : "a"(val), "Nd"(port) );
}

static inline uint8_t inb(uint16_t port) {
    uint8_t ret;
    __asm__ volatile ( "inb %1, %0" : "=a"(ret) : "Nd"(port) );
    return ret;
}

/* Включение курсора VGA */
void enable_cursor() {
    outb(0x3D4, 0x0A);
    outb(0x3D5, (inb(0x3D5) & 0xC0) | 0);
    outb(0x3D4, 0x0B);
    outb(0x3D5, (inb(0x3D5) & 0xE0) | 15);
}

/* --- KERNEL MAIN --- */
void kernel_main(void) {
    terminal_initialize();
    enable_cursor();
    
    // Логотип
    terminal_writestring("    ____       _                 ____  _____\n");
    terminal_writestring("   / __ \\_____(_)________ ___   / __ \\/ ___/\n");
    terminal_writestring("  / /_/ / ___/ / ___/ __ `__ \\ / / / /\\__ \\ \n");
    terminal_writestring(" / ____/ /  / (__  ) / / / / // /_/ /___/ / \n");
    terminal_writestring("/_/   /_/  /_/____/_/ /_/ /_/ \\____//____/  \n\n");

    terminal_color = vga_entry_color(VGA_COLOR_WHITE, VGA_COLOR_BLACK);
    terminal_writestring("PrismOS Kernel v0.1 - Initialization Complete\n");
    terminal_writestring("==============================================\n");
    terminal_writestring("Memory: 0x100000 (Protected Mode)\n");
    terminal_writestring("VGA: 0xB8000 (80x25 Text Mode)\n");
    terminal_writestring("Status: Running...\n");
    terminal_writestring("==============================================\n");
    
    // Анимация загрузки
    terminal_color = vga_entry_color(VGA_COLOR_LIGHT_GREEN, VGA_COLOR_BLACK);
    terminal_writestring("[");
    for (int i = 0; i < 50; i++) {
        terminal_putchar('#');
        // Задержка
        for (volatile int d = 0; d < 100000; d++);
    }
    terminal_writestring("] 100%\n\n");
    
    terminal_color = vga_entry_color(VGA_COLOR_LIGHT_CYAN, VGA_COLOR_BLACK);
    terminal_writestring("System ready. Type 'help' for commands.\n");
    terminal_writestring("prismos> ");
    
    // Запоминаем позицию приглашения
    size_t prompt_row = terminal_row;
    size_t prompt_col = terminal_column - 9; // 9 = длина "prismos> "
    
    // --- ДРАЙВЕР КЛАВИАТУРЫ ---
    #define KEYBOARD_DATA_PORT 0x60
    #define KEYBOARD_STATUS_PORT 0x64
    
    // Таблица скан-кодов (US QWERTY)
    static const char scan_code_table[] = {
        0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',
        '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',
        0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`',
        0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0,
        '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    };
    
    // Главный цикл с обработкой клавиатуры
    while(1) {
        // Проверка буфера клавиатуры
        if (inb(KEYBOARD_STATUS_PORT) & 0x01) {
            uint8_t scan_code = inb(KEYBOARD_DATA_PORT);
            
            // Игнорируем отжатия клавиш (старший бит)
            if (scan_code & 0x80) continue;
            
            // Преобразуем в ASCII
            if (scan_code < 128 && scan_code_table[scan_code]) {
                char c = scan_code_table[scan_code];
                if (c == '\b') {
                    // Backspace только если курсор после приглашения
                    if (terminal_row > prompt_row || 
                        (terminal_row == prompt_row && terminal_column > prompt_col)) {
                        terminal_column--;
                        terminal_putentryat(' ', terminal_color, terminal_column, terminal_row);
                    }
                } else if (c == '\n') {
                    // Новая строка и перерисовка приглашения
                    terminal_putchar('\n');
                    terminal_writestring("prismos> ");
                    prompt_row = terminal_row;
                    prompt_col = terminal_column - 9;
                } else {
                    terminal_putchar(c);
                }
            }
        }
        
        // Небольшая задержка
        for(volatile int i = 0; i < 10000; i++);
    }
}
EOF

    # ----------------------------------------------------------------------
    # 3. LINKER SCRIPT
    # ----------------------------------------------------------------------
    echo "[+] Создание скрипта линковки (linker.ld)..."
    cat << 'EOF' > $PROJECT/src/linker.ld
ENTRY(_start)

SECTIONS
{
    /* Загрузчик обычно помещает нас по адресу 1 MiB */
    . = 1M;

    /* Сначала идет Multiboot заголовок, затем код */
    .text BLOCK(4K) : ALIGN(4K)
    {
        *(.multiboot)
        *(.text)
    }

    /* Read-only данные */
    .rodata BLOCK(4K) : ALIGN(4K)
    {
        *(.rodata)
    }

    /* Данные (инициализированные) */
    .data BLOCK(4K) : ALIGN(4K)
    {
        *(.data)
    }

    /* BSS (неинициализированные данные и стек) */
    .bss BLOCK(4K) : ALIGN(4K)
    {
        *(COMMON)
        *(.bss)
        *(.stack)
    }
}
EOF

    # ----------------------------------------------------------------------
    # 4. GRUB CONFIG
    # ----------------------------------------------------------------------
    echo "[+] Создание конфигурации GRUB (grub.cfg)..."
    cat << 'EOF' > $PROJECT/iso/boot/grub/grub.cfg
set timeout=5
set default=0

menuentry "PrismOS v0.1" {
    multiboot /boot/prismos.bin
    boot
}

menuentry "PrismOS (Serial Debug)" {
    multiboot /boot/prismos.bin
    serial --unit=0 --speed=115200
    terminal_output serial
    boot
}

menuentry "Reboot" {
    reboot
}

menuentry "Shutdown" {
    halt
}
EOF

    # ----------------------------------------------------------------------
    # 5. MAKEFILE
    # ----------------------------------------------------------------------
    echo "[+] Создание Makefile..."
    cat << 'EOF' > $PROJECT/Makefile
# Компиляторы и флаги
CC = gcc
AS = nasm
CFLAGS = -m32 -fno-builtin -fno-stack-protector -nostdlib \
         -nodefaultlibs -Wall -Wextra -ffreestanding -c \
         -O1 -fno-omit-frame-pointer
ASFLAGS = -felf32
LDFLAGS = -m elf_i386 -T src/linker.ld -nostdlib

# Файлы
OBJECTS = src/boot.o src/kernel.o
OUTPUT = prismos.bin
ISO_NAME = prismos.iso
ISO_DIR = iso

.PHONY: all clean run debug qemu

all: $(ISO_NAME)

$(ISO_NAME): $(OUTPUT)
	@mkdir -p $(ISO_DIR)/boot/grub
	@cp $(OUTPUT) $(ISO_DIR)/boot/
	@grub-mkrescue -o $(ISO_NAME) $(ISO_DIR) 2>/dev/null
	@echo "[+] ISO образ создан: $(ISO_NAME)"
	@echo "[+] Размер ядра: $$(stat -c%s $(OUTPUT)) байт"

$(OUTPUT): $(OBJECTS)
	@ld $(LDFLAGS) -o $(OUTPUT) $(OBJECTS)
	@echo "[+] Ядро скомпилировано: $(OUTPUT)"

src/boot.o: src/boot.s
	@$(AS) $(ASFLAGS) $< -o $@
	@echo "[+] Ассемблерный код скомпилирован"

src/kernel.o: src/kernel.c
	@$(CC) $(CFLAGS) $< -o $@
	@echo "[+] C код скомпилирован"

run: $(ISO_NAME)
	@echo "[*] Запуск PrismOS в QEMU..."
	@qemu-system-x86_64 -cdrom $(ISO_NAME) -no-reboot -no-shutdown

debug: $(ISO_NAME)
	@echo "[*] Запуск PrismOS в QEMU с отладкой..."
	@qemu-system-x86_64 -cdrom $(ISO_NAME) -no-reboot -no-shutdown -d int -D qemu.log

qemu: run

clean:
	@rm -f src/*.o $(OUTPUT) $(ISO_NAME) qemu.log
	@rm -rf $(ISO_DIR)/boot/prismos.bin
	@echo "[+] Очистка завершена"

help:
	@echo "Доступные команды:"
	@echo "  make all    - собрать всё (яро + ISO)"
	@echo "  make run    - собрать и запустить в QEMU"
	@echo "  make debug  - собрать и запустить с отладкой"
	@echo "  make clean  - очистить файлы сборки"
	@echo "  make help   - показать эту справку"
EOF

    # ----------------------------------------------------------------------
    # 6. BUILD SCRIPT
    # ----------------------------------------------------------------------
    echo "[+] Создание скрипта быстрой сборки..."
    cat << 'EOF' > $PROJECT/build.sh
#!/bin/bash

echo "=== PrismOS Build Script ==="
echo ""

# Проверка на наличие Makefile
if [ ! -f "Makefile" ]; then
    echo "Ошибка: Makefile не найден!"
    echo "Запустите скрипт из папки PrismOS_Root"
    exit 1
fi

case "$1" in
    "clean")
        make clean
        ;;
    "debug")
        make debug
        ;;
    "run"|"")
        make run
        ;;
    "help")
        make help
        ;;
    *)
        echo "Использование: $0 [clean|debug|run|help]"
        echo "  clean - очистить файлы сборки"
        echo "  debug - запустить с отладкой"
        echo "  run   - собрать и запустить (по умолчанию)"
        echo "  help  - показать справку"
        ;;
esac
EOF

    chmod +x $PROJECT/build.sh
}

main "$@"