unit SoundPlugin;

{$MODE OBJFPC}{$H+}

{
    Part of AdvancedChatAI.
    For GNU/Linux 64 bit version.
    Version: 1.
    Written on FreePascal (https://freepascal.org/).
    Copyright (C) 2025-2026 Artyomov Alexander
    Used https://chat.deepseek.com/
    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/>.
}


interface

uses
  PluginInterface, SysUtils, Classes, Process, StrUtils;

type
  TSoundTheme = (stClassic, stEmotional, stSciFi, stGame);
  TSoundEvent = (
    seGreeting,      // Приветствие
    sePositive,      // Положительный ответ
    seNegative,      // Отрицательный/ошибочный
    seNotification,  // Уведомление
    seProcessing,    // Обработка запроса
    seExit,          // Выход
    seMysterious,    // Загадочный ответ
    seJoke           // Шутка/юмор
  );

  TSoundPlugin = class(TInterfacedObject, IPlugin)
  private
    FSoundTheme: TSoundTheme;
    FSoundPlayerPath: string;
    FEnabled: Boolean;
    
    procedure GenerateSoundFile(Event: TSoundEvent; out FileName: string);
    procedure PlaySound(const FileName: string);
    function GetSoundData(Event: TSoundEvent): TBytes;
  public
    constructor Create;
    destructor Destroy; override;
    
    function CanHandle(const Input: string): Boolean;
    function HandleInput(const Input: string): string;
    function GetName: string;
    
    procedure SetTheme(Theme: TSoundTheme);
    procedure SetEnabled(Enabled: Boolean);
  end;

implementation

constructor TSoundPlugin.Create;
begin
  inherited;
  FSoundTheme := stClassic;
  FEnabled := True;
  // Путь к проигрывателю - можно изменить через конфиг
  FSoundPlayerPath := './gorg64_spkplay';
end;

destructor TSoundPlugin.Destroy;
begin
  inherited;
end;

function TSoundPlugin.CanHandle(const Input: string): Boolean;
begin
  // Этот плагин обрабатывает все сообщения для звукового сопровождения
  Result := FEnabled;
end;
{
function TSoundPlugin.HandleInput(const Input: string): string;
var
  LowerInput: string;
  SoundEvent: TSoundEvent;
  SoundFile: string;
begin
  Result := '';
  if not FEnabled then Exit;

  LowerInput := LowerCase(Trim(Input));
  
  // Определяем тип звукового события по контексту
  if LowerInput = 'привет' then
    SoundEvent := seGreeting
  else if ContainsText(LowerInput, 'ошибка') or ContainsText(LowerInput, 'не могу') then
    SoundEvent := seNegative
  else if ContainsText(LowerInput, 'шутк') or ContainsText(LowerInput, 'анекдот') then
    SoundEvent := seJoke
  else if (Pos('?', Input) > 0) or ContainsText(LowerInput, 'почему') then
    SoundEvent := seMysterious
  else
    SoundEvent := sePositive;

  // Генерируем и проигрываем звук
  try
    SoundFile := Format('sound_%d_%d.speaker', [Ord(FSoundTheme), Ord(SoundEvent)]);
    if FileExists(SoundFile) then
      DeleteFile(SoundFile);
    
    GenerateSoundFile(SoundEvent, SoundFile);
    
    if FileExists(SoundFile) then
    begin
      PlaySound(SoundFile);
      DeleteFile(SoundFile);
    end
    else
      WriteLn('[SoundPlugin] Ошибка: файл ', SoundFile, ' не создан');
  except
    on E: Exception do
      WriteLn('[SoundPlugin] Ошибка: ', E.ClassName, ': ', E.Message);
  end;
end;
}
function TSoundPlugin.HandleInput(const Input: string): string;
var
  LowerInput: string;
begin
  Result := '';
  LowerInput := LowerCase(Trim(Input));

  try
    if LowerInput = 'звук вкл' then
    begin
      FEnabled := True;
      Result := 'Звуковое сопровождение включено';
    end
    else if (LowerInput = 'звук выкл') or (LowerInput = 'звук выключить') then
    begin
      FEnabled := False;
      Result := 'Звуковое сопровождение отключено';
    end;
  except
    on E: Exception do
      Result := 'Ошибка управления звуком: ' + E.Message;
  end;
end;

function TSoundPlugin.GetName: string;
begin
  Result := 'Sound Effects Plugin v1.0';
end;

procedure TSoundPlugin.SetTheme(Theme: TSoundTheme);
begin
  FSoundTheme := Theme;
end;

procedure TSoundPlugin.SetEnabled(Enabled: Boolean);
begin
  FEnabled := Enabled;
end;

procedure TSoundPlugin.GenerateSoundFile(Event: TSoundEvent; out FileName: string);
var
  SoundData: TBytes;
  F: File;
begin
  FileName := Format('sound_%d_%d.speaker', [Ord(FSoundTheme), Ord(Event)]);
  SoundData := GetSoundData(Event);
  
  AssignFile(F, FileName);
  try
    Rewrite(F, 1);
    BlockWrite(F, SoundData[0], Length(SoundData));
  finally
    CloseFile(F);
  end;
end;

procedure TSoundPlugin.PlaySound(const FileName: string);
var
  Process: TProcess;
begin
  Process := TProcess.Create(nil);
  try
    Process.Executable := FSoundPlayerPath;
    Process.Parameters.Add(FileName);
    Process.Options := [poWaitOnExit];
    Process.Execute;
  finally
    Process.Free;
  end;
end;

function TSoundPlugin.GetSoundData(Event: TSoundEvent): TBytes;
type
  TTW = packed record
    tone, duration: Word;
  end;
var
  Notes: array of TTW;
  i: Integer;
begin
  // Инициализируем массив нот для всех событий
  case Event of
    seGreeting:
      begin
        SetLength(Notes, 5);
        Notes[0].tone := 9121; Notes[0].duration := 100;
        Notes[1].tone := 8126; Notes[1].duration := 100;
        Notes[2].tone := 7239; Notes[2].duration := 100;
        Notes[3].tone := 8126; Notes[3].duration := 100;
        Notes[4].tone := 9121; Notes[4].duration := 150;
      end;
    
    sePositive:
      begin
        SetLength(Notes, 3);
        Notes[0].tone := 9121; Notes[0].duration := 80;
        Notes[1].tone := 10893; Notes[1].duration := 80;
        Notes[2].tone := 12175; Notes[2].duration := 120;
      end;
    
    seNegative:
      begin
        SetLength(Notes, 4);
        Notes[0].tone := 12175; Notes[0].duration := 100;
        Notes[1].tone := 10893; Notes[1].duration := 100;
        Notes[2].tone := 9121;  Notes[2].duration := 100;
        Notes[3].tone := 8126;  Notes[3].duration := 150;
      end;
    
    seNotification:
      begin
        SetLength(Notes, 2);
        Notes[0].tone := 12175; Notes[0].duration := 50;
        Notes[1].tone := 12175; Notes[1].duration := 50;
      end;
    
    seProcessing:
      begin
        SetLength(Notes, 6);
        for i := 0 to 5 do
        begin
          Notes[i].tone := 10000 - i*500;
          Notes[i].duration := 60;
        end;
      end;
    
    seExit:
      begin
        SetLength(Notes, 4);
        Notes[0].tone := 9121;  Notes[0].duration := 100;
        Notes[1].tone := 7239;  Notes[1].duration := 100;
        Notes[2].tone := 6087;  Notes[2].duration := 100;
        Notes[3].tone := 4560;  Notes[3].duration := 200;
      end;
    
    seMysterious:
      begin
        SetLength(Notes, 8);
        for i := 0 to 7 do
        begin
          Notes[i].tone := 8000 + Random(4000);
          Notes[i].duration := 80 + Random(70);
        end;
      end;
    
    seJoke:
      begin
        // Весёлая "кукушка" для шуток
        SetLength(Notes, 6);
        Notes[0].tone := 1046; Notes[0].duration := 150; // До
        Notes[1].tone := 1175; Notes[1].duration := 150; // Ре
        Notes[2].tone := 1318; Notes[2].duration := 150; // Ми
        Notes[3].tone := 1046; Notes[3].duration := 150; // До
        Notes[4].tone := 1318; Notes[4].duration := 150; // Ми
        Notes[5].tone := 1568; Notes[5].duration := 300; // Соль
      end;
  end;

  // Преобразуем массив нот в байты
  SetLength(Result, Length(Notes) * SizeOf(TTW));
  if Length(Notes) > 0 then
    Move(Notes[0], Result[0], Length(Result));
end;

{
    Для управления плагином можно добавить команды:
// В обработчике ввода:
else if LowerInput = 'звук вкл' then
  (PlugManager.FindPlugin('Sound Effects Plugin') as TSoundPlugin).SetEnabled(True)
else if LowerInput = 'звук выкл' then
  (PlugManager.FindPlugin('Sound Effects Plugin') as TSoundPlugin).SetEnabled(False)
else if ContainsText(LowerInput, 'звуковая тема') then
begin
  // Переключение тем
end;
}

end.