/*
    Voice Clock audio starter.
    For GNU/Linux 64 bit version.
    Version: 1 (C port).
    Written on C (https://en.wikipedia.org/wiki/C_(programming_language))
    Original Pascal version Copyright (C) 2025 Artyomov Alexander
    C port Copyright (C) 2026

    http://self-made-free.ru/
    aralni@mail.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <math.h>
#include <alsa/asoundlib.h>

#define SAMPLE_RATE 44100
#define SILENCE_THRESHOLD 0.9
#define BUFFER_SIZE 4096
#define BEEP_FILE "beep.wav"

static volatile int should_stop = 0;

void handle_signal(int sig) {
    switch (sig) {
        case SIGINT:
        case SIGTERM:
            printf("Получен сигнал завершения. Останавливаем программу...\n");
            should_stop = 1;
            break;
    }
}

// Функция для чтения WAV заголовка
int read_wav_header(const char* filename, int* num_channels, int* sample_rate, int* bits_per_sample) {
    FILE* fp = fopen(filename, "rb");
    if (!fp) return -1;
    
    unsigned char header[44];
    if (fread(header, 1, 44, fp) != 44) {
        fclose(fp);
        return -1;
    }
    
    // Проверка "RIFF" и "WAVE"
    if (memcmp(header, "RIFF", 4) != 0 || memcmp(header + 8, "WAVE", 4) != 0) {
        fclose(fp);
        return -1;
    }
    
    *num_channels = header[22] | (header[23] << 8);
    *sample_rate = header[24] | (header[25] << 8) | (header[26] << 16) | (header[27] << 24);
    *bits_per_sample = header[34] | (header[35] << 8);
    
    fclose(fp);
    return 0;
}

void play_beep(const char* filename) {
    snd_pcm_t* alsa_handle = NULL;
    snd_pcm_hw_params_t* alsa_params = NULL;
    FILE* fp = NULL;
    short* buffer = NULL;
    int num_channels = 1;
    int sample_rate = SAMPLE_RATE;
    int bits_per_sample = 16;
    int err;
    unsigned int rate;
    snd_pcm_uframes_t frames;
    int period_size = 1024;
    int dir = 0;
    size_t buffer_size;
    
    // Читаем заголовок WAV
    if (read_wav_header(filename, &num_channels, &sample_rate, &bits_per_sample) != 0) {
        fprintf(stderr, "Ошибка чтения заголовка WAV файла %s\n", filename);
        return;
    }
    
    printf("Количество каналов: %d, Частота: %d, Бит: %d\n", 
           num_channels, sample_rate, bits_per_sample);
    
    // Открываем файл
    fp = fopen(filename, "rb");
    if (!fp) {
        fprintf(stderr, "Ошибка открытия файла %s\n", filename);
        return;
    }
    
    // Пропускаем WAV заголовок (44 байта)
    fseek(fp, 44, SEEK_SET);
    
    // Открываем ALSA устройство
    err = snd_pcm_open(&alsa_handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
    if (err < 0) {
        fprintf(stderr, "Ошибка открытия ALSA устройства: %s\n", snd_strerror(err));
        goto cleanup;
    }
    
    snd_pcm_hw_params_malloc(&alsa_params);
    snd_pcm_hw_params_any(alsa_handle, alsa_params);
    snd_pcm_hw_params_set_access(alsa_handle, alsa_params, SND_PCM_ACCESS_RW_INTERLEAVED);
    snd_pcm_hw_params_set_format(alsa_handle, alsa_params, SND_PCM_FORMAT_S16_LE);
    snd_pcm_hw_params_set_channels(alsa_handle, alsa_params, num_channels);
    
    rate = sample_rate;
    err = snd_pcm_hw_params_set_rate_near(alsa_handle, alsa_params, &rate, &dir);
    if (err < 0) {
        fprintf(stderr, "Ошибка установки частоты: %s\n", snd_strerror(err));
        goto cleanup;
    }
    
    err = snd_pcm_hw_params_set_period_size_near(alsa_handle, alsa_params, 
                                                  (snd_pcm_uframes_t*)&period_size, &dir);
    if (err < 0) {
        fprintf(stderr, "Ошибка установки периода: %s\n", snd_strerror(err));
        goto cleanup;
    }
    
    frames = BUFFER_SIZE;
    err = snd_pcm_hw_params_set_buffer_size_near(alsa_handle, alsa_params, &frames);
    if (err < 0) {
        fprintf(stderr, "Ошибка установки буфера: %s\n", snd_strerror(err));
        goto cleanup;
    }
    
    err = snd_pcm_hw_params(alsa_handle, alsa_params);
    if (err < 0) {
        fprintf(stderr, "Ошибка применения параметров: %s\n", snd_strerror(err));
        goto cleanup;
    }
    
    // Выделяем буфер
    buffer_size = period_size * num_channels * 2; // 2 байта на сэмпл (16-bit)
    buffer = malloc(buffer_size);
    if (!buffer) {
        fprintf(stderr, "Ошибка выделения памяти\n");
        goto cleanup;
    }
    
    // Читаем и воспроизводим
    while (!should_stop) {
        size_t bytes_read = fread(buffer, 1, buffer_size, fp);
        if (bytes_read <= 0) break;
        
        snd_pcm_sframes_t frames_written = snd_pcm_writei(alsa_handle, buffer, 
                                                           bytes_read / (num_channels * 2));
        if (frames_written == -EPIPE) {
            fprintf(stderr, "Underrun!\n");
            snd_pcm_prepare(alsa_handle);
        } else if (frames_written < 0) {
            fprintf(stderr, "Ошибка записи: %s\n", snd_strerror(frames_written));
            break;
        }
    }
    
    snd_pcm_drain(alsa_handle);
    
cleanup:
    if (alsa_params) snd_pcm_hw_params_free(alsa_params);
    if (alsa_handle) snd_pcm_close(alsa_handle);
    if (buffer) free(buffer);
    if (fp) fclose(fp);
}

void listen_microphone(void) {
    snd_pcm_t* alsa_handle = NULL;
    snd_pcm_hw_params_t* alsa_params = NULL;
    short* buffer = NULL;
    int err;
    unsigned int rate = SAMPLE_RATE;
    snd_pcm_uframes_t frames = BUFFER_SIZE;
    int dir = 0;
    
    // Открываем ALSA устройство для захвата
    err = snd_pcm_open(&alsa_handle, "default", SND_PCM_STREAM_CAPTURE, 0);
    if (err < 0) {
        fprintf(stderr, "Ошибка открытия ALSA устройства для захвата: %s\n", snd_strerror(err));
        goto cleanup;
    }
    
    snd_pcm_hw_params_malloc(&alsa_params);
    snd_pcm_hw_params_any(alsa_handle, alsa_params);
    snd_pcm_hw_params_set_access(alsa_handle, alsa_params, SND_PCM_ACCESS_RW_INTERLEAVED);
    snd_pcm_hw_params_set_format(alsa_handle, alsa_params, SND_PCM_FORMAT_S16_LE);
    snd_pcm_hw_params_set_channels(alsa_handle, alsa_params, 1); // Моно
    
    err = snd_pcm_hw_params_set_rate_near(alsa_handle, alsa_params, &rate, &dir);
    if (err < 0) {
        fprintf(stderr, "Ошибка установки частоты: %s\n", snd_strerror(err));
        goto cleanup;
    }
    
    err = snd_pcm_hw_params_set_buffer_size_near(alsa_handle, alsa_params, &frames);
    if (err < 0) {
        fprintf(stderr, "Ошибка установки буфера: %s\n", snd_strerror(err));
        goto cleanup;
    }
    
    err = snd_pcm_hw_params(alsa_handle, alsa_params);
    if (err < 0) {
        fprintf(stderr, "Ошибка применения параметров: %s\n", snd_strerror(err));
        goto cleanup;
    }
    
    buffer = malloc(sizeof(short) * BUFFER_SIZE);
    if (!buffer) {
        fprintf(stderr, "Ошибка выделения памяти\n");
        goto cleanup;
    }
    
    printf("Слушаю микрофон...\n");
    
    while (!should_stop) {
        err = snd_pcm_readi(alsa_handle, buffer, BUFFER_SIZE);
        if (err < 0) {
            fprintf(stderr, "Ошибка чтения: %s\n", snd_strerror(err));
            break;
        }
        
        // Анализируем уровень звука
        double max_amplitude = 0.0;
        for (int i = 0; i < BUFFER_SIZE; i++) {
            double amp = fabs(buffer[i] / 32768.0);
            if (amp > max_amplitude) {
                max_amplitude = amp;
            }
        }
        
        // Если уровень превышает порог
        if (max_amplitude > SILENCE_THRESHOLD) {
            printf("Слышу звук! (амплитуда: %.2f)\n", max_amplitude);
            // play_beep(BEEP_FILE); // Раскомментировать при необходимости
            break;
        }
    }
    
cleanup:
    if (alsa_params) snd_pcm_hw_params_free(alsa_params);
    if (alsa_handle) snd_pcm_close(alsa_handle);
    if (buffer) free(buffer);
}

int main(void) {
    // Устанавливаем обработчики сигналов
    signal(SIGINT, handle_signal);
    signal(SIGTERM, handle_signal);
    
    while (!should_stop) {
        listen_microphone();
        system("vc2");
        printf("Сплю\n");
        sleep(120);
    }
    
    return 0;
}