/*
 * Descent 3
 * Copyright (C) 2024 Parallax Software
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

// AmbientSoundPattern.cpp : implementation file
//

#include "stdafx.h"
#include "editor.h"
#include "AmbientSoundPattern.h"
#include "AmbientSoundElement.h"
#include "EditLineDialog.h"
#include "ambient.h"
#include "ssl_lib.h"
#include "pserror.h"
#include "gamefilepage.h"
#include "ddio.h"
#include "mem.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CAmbientSoundPattern dialog

CAmbientSoundPattern::CAmbientSoundPattern(CWnd *pParent /*=NULL*/) : CDialog(CAmbientSoundPattern::IDD, pParent) {
  //{{AFX_DATA_INIT(CAmbientSoundPattern)
  // NOTE: the ClassWizard will add member initialization here
  //}}AFX_DATA_INIT
}

void CAmbientSoundPattern::DoDataExchange(CDataExchange *pDX) {
  CDialog::DoDataExchange(pDX);
  //{{AFX_DATA_MAP(CAmbientSoundPattern)
  // NOTE: the ClassWizard will add DDX and DDV calls here
  //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAmbientSoundPattern, CDialog)
//{{AFX_MSG_MAP(CAmbientSoundPattern)
ON_CBN_SELENDOK(IDC_ASP_LIST, OnSelendokASPList)
ON_BN_CLICKED(IDC_ASP_CHECK_IN, OnASPCheckIn)
ON_BN_CLICKED(IDC_ASP_DELETE, OnASPDelete)
ON_BN_CLICKED(IDC_ASP_EDIT_ELEMENT, OnASPEditElement)
ON_BN_CLICKED(IDC_ASP_LOCK, OnASPLock)
ON_BN_CLICKED(IDC_ASP_NEW, OnASPNew)
ON_BN_CLICKED(IDC_ASP_NEW_ELEMENT, OnASPNewElement)
ON_BN_CLICKED(IDC_ASP_RENAME, OnASPRename)
ON_LBN_SELCHANGE(IDC_ASP_ELEMENT_LIST, OnSelchangeASPElementList)
ON_BN_CLICKED(IDC_ASP_DELETE_ELEMENT, OnASPDeleteElement)
ON_BN_CLICKED(IDC_ASP_UNDO_LOCK, OnASPUndoLock)
ON_EN_KILLFOCUS(IDC_ASP_MAX_DELAY, OnKillfocusASPMaxDelay)
ON_EN_KILLFOCUS(IDC_ASP_MIN_DELAY, OnKillfocusASPMinDelay)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CAmbientSoundPattern message handlers

BOOL CAmbientSoundPattern::OnInitDialog() {
  CDialog::OnInitDialog();

  m_current_asp = m_current_element = -1;

  m_locked = (mng_FindTrackLock(AMBIENT_FILE_NAME, PAGETYPE_GAMEFILE) != -1);

  UpdateDialog();

  return TRUE; // return TRUE unless you set the focus to a control
               // EXCEPTION: OCX Property Pages should return FALSE
}

void CAmbientSoundPattern::UpdateDialog() {
  int i;

  // Build the ambient sound list
  CComboBox *asplist = (CComboBox *)GetDlgItem(IDC_ASP_LIST);
  asplist->ResetContent();
  for (i = 0; i < Num_ambient_sound_patterns; i++)
    if (Ambient_sound_patterns[i].name[0]) {
      asplist->AddString(Ambient_sound_patterns[i].name);
      if (m_current_asp == -1)
        m_current_asp = i;
    }
  if (m_current_asp != -1)
    asplist->SelectString(-1, Ambient_sound_patterns[m_current_asp].name);

  // Write out elements for current ASP
  CListBox *aselist = (CListBox *)GetDlgItem(IDC_ASP_ELEMENT_LIST);
  char tbuf[100];
  asp *asp = &Ambient_sound_patterns[m_current_asp];
  aselist->ResetContent();
  for (i = 0; i < asp->num_sounds; i++) {
    ase *ase = &asp->sounds[i];
    sprintf(tbuf, "%s:   vol=%.2f,%.2f   prob=%d", Sounds[ase->handle].name, ase->min_volume, ase->max_volume,
            ase->probability);
    aselist->AddString(tbuf);
  }

  // Write out the delay values
  CEdit *ebox = (CEdit *)GetDlgItem(IDC_ASP_MIN_DELAY);
  char buf[20];
  sprintf(buf, "%.1f", asp->min_delay);
  ebox->SetWindowText(buf);

  ebox = (CEdit *)GetDlgItem(IDC_ASP_MAX_DELAY);
  sprintf(buf, "%.1f", asp->max_delay);
  ebox->SetWindowText(buf);

  // Select current element
  if (aselist->GetCount() > 0)
    aselist->SetCurSel(m_current_element);

  // Enable/disable editing buttons
  ((CButton *)GetDlgItem(IDC_ASP_DELETE))->EnableWindow(m_locked);
  ((CButton *)GetDlgItem(IDC_ASP_EDIT_ELEMENT))->EnableWindow(m_locked);
  ((CButton *)GetDlgItem(IDC_ASP_NEW))->EnableWindow(m_locked);
  ((CButton *)GetDlgItem(IDC_ASP_NEW_ELEMENT))->EnableWindow(m_locked);
  ((CButton *)GetDlgItem(IDC_ASP_RENAME))->EnableWindow(m_locked);
  ((CButton *)GetDlgItem(IDC_ASP_DELETE_ELEMENT))->EnableWindow(m_locked);
  ((CButton *)GetDlgItem(IDC_ASP_MIN_DELAY))->EnableWindow(m_locked);
  ((CButton *)GetDlgItem(IDC_ASP_MAX_DELAY))->EnableWindow(m_locked);

  // Enable/disable locking buttons
  ((CButton *)GetDlgItem(IDC_ASP_CHECK_IN))->EnableWindow(m_locked);
  ((CButton *)GetDlgItem(IDC_ASP_UNDO_LOCK))->EnableWindow(m_locked);
  ((CButton *)GetDlgItem(IDC_ASP_LOCK))->EnableWindow(!m_locked);
}

void CAmbientSoundPattern::OnSelendokASPList() {
  char name[200];

  CComboBox *asplist = (CComboBox *)GetDlgItem(IDC_ASP_LIST);

  asplist->GetLBText(asplist->GetCurSel(), name);
  m_current_asp = FindAmbientSoundPattern(name);

  if (Ambient_sound_patterns[m_current_asp].num_sounds)
    m_current_element = 0;
  else
    m_current_element = -1;

  UpdateDialog();
}

void CAmbientSoundPattern::OnASPRename() {
  if (!InputString(Ambient_sound_patterns[m_current_asp].name, sizeof(Ambient_sound_patterns[m_current_asp].name),
                   "Ambient Sound Pattern", "Enter a new name"))
    return;

  UpdateDialog();
}

void CAmbientSoundPattern::OnSelchangeASPElementList() {
  CListBox *lb = (CListBox *)GetDlgItem(IDC_ASP_ELEMENT_LIST);

  m_current_element = lb->GetCurSel();
}

void CAmbientSoundPattern::OnASPDelete() {
  if (OutrageMessageBox(MBOX_YESNO, "Do you really want to delete the ambient sound pattern '%s'?",
                        AmbientSoundPatternName(m_current_asp)) == IDYES) {
    asp *asp = &Ambient_sound_patterns[m_current_asp];

    asp->name[0] = 0; // null string for name
    mem_free(asp->sounds);
    asp->num_sounds = 0;
    m_current_asp = -1;

    UpdateDialog();
  }
}

void CAmbientSoundPattern::OnASPNew() {
  asp *asp = &Ambient_sound_patterns[Num_ambient_sound_patterns];

  if (InputString(asp->name, sizeof(asp->name), "Ambient Sound Pattern", "Enter a name for your new pattern"))
    if (asp->name[0]) {
      asp->num_sounds = 0;
      asp->min_delay = asp->max_delay = 0.0;
      m_current_asp = Num_ambient_sound_patterns++;
      UpdateDialog();
    }
}

void CAmbientSoundPattern::OnASPNewElement() {
  asp *asp = &Ambient_sound_patterns[m_current_asp];
  ase new_element;

  new_element.handle = -1;
  new_element.min_volume = new_element.max_volume = 1.0;
  new_element.probability = 50;

  CAmbientSoundElement dlg(&new_element);

  theApp.pause();
  dlg.DoModal();
  theApp.resume();

  if (new_element.handle != -1) {

    ase *new_sounds = (ase *)mem_malloc(sizeof(*new_sounds) * (asp->num_sounds + 1));

    for (int s = 0; s < asp->num_sounds; s++)
      new_sounds[s] = asp->sounds[s];

    mem_free(asp->sounds);

    asp->sounds = new_sounds;
    asp->sounds[s] = new_element;

    asp->num_sounds++;

    m_current_element = s;

    UpdateDialog();
  }
}

void CAmbientSoundPattern::OnASPEditElement() {
  if (m_current_element > -1) {
    CAmbientSoundElement dlg(&Ambient_sound_patterns[m_current_asp].sounds[m_current_element]);

    theApp.pause();
    dlg.DoModal();
    theApp.resume();

    UpdateDialog();
  }
}

void CAmbientSoundPattern::OnASPDeleteElement() {
  asp *asp = &Ambient_sound_patterns[m_current_asp];
  ase *new_sounds;

  if (m_current_element > -1) {
    if (OutrageMessageBox(MBOX_YESNO, "Do you really want to delete sound '%s' from pattern '%s'?",
                          Sounds[asp->sounds[m_current_element].handle].name, asp->name) == IDYES) {
      new_sounds = (ase *)mem_malloc(sizeof(*new_sounds) * (asp->num_sounds - 1));

      for (int s = 0; s < m_current_element; s++)
        new_sounds[s] = asp->sounds[s];

      for (; s < asp->num_sounds - 1; s++)
        new_sounds[s] = asp->sounds[s + 1];

      mem_free(asp->sounds);

      asp->sounds = new_sounds;

      asp->num_sounds--;

      UpdateDialog();
    }
  }
}

void CAmbientSoundPattern::OnKillfocusASPMinDelay() {
  asp *asp = &Ambient_sound_patterns[m_current_asp];

  CEdit *ebox;
  char buf[20];

  ebox = (CEdit *)GetDlgItem(IDC_ASP_MIN_DELAY);
  ebox->GetWindowText(buf, sizeof(buf));

  asp->min_delay = atof(buf);

  UpdateDialog();
}

void CAmbientSoundPattern::OnKillfocusASPMaxDelay() {
  asp *asp = &Ambient_sound_patterns[m_current_asp];

  CEdit *ebox;
  char buf[20];

  ebox = (CEdit *)GetDlgItem(IDC_ASP_MAX_DELAY);
  ebox->GetWindowText(buf, sizeof(buf));

  asp->max_delay = atof(buf);

  UpdateDialog();
}

void CAmbientSoundPattern::OnCancel() {
  // Re-read old data
  ReadAmbientData();

  CDialog::OnCancel();
}

void CAmbientSoundPattern::OnOK() {
  // Verify probabilities
  for (int p = 0; p < Num_ambient_sound_patterns; p++) {
    asp *asp = &Ambient_sound_patterns[p];
    if (asp->num_sounds) {
      int prob = 0;

      for (int s = 0; s < asp->num_sounds; s++)
        prob += asp->sounds[s].probability;

      if (prob != 100) {
        OutrageMessageBox("Error:  the probabilities for pattern '%s' don't add up to 100.", asp->name);
        return;
      }
    }
  }

  // Update the data
  WriteAmbientData();

  CDialog::OnOK();
}

void CAmbientSoundPattern::OnASPLock() {
  int n;
  mngs_Pagelock temp_pl;
  mngs_gamefile_page gamefilepage;
  int r;

  // Find where it is in the list
  n = FindGamefileName(AMBIENT_FILE_NAME);

  if (n == -1) {
    Int3(); // Get Matt or Jason - AMBIENT file name is no longer in the table file!
    return;
  }

  if (!mng_MakeLocker())
    return;

  // Make sure it can be locked
  strcpy(temp_pl.name, AMBIENT_FILE_NAME);
  temp_pl.pagetype = PAGETYPE_GAMEFILE;

  r = mng_CheckIfPageLocked(&temp_pl);
  if (r == 2) {
    int answer;
    answer = OutrageMessageBox(MBOX_YESNO, "This page is not even in the table file, or the database maybe corrupt.  "
                                           "Override to 'Unlocked'? (Select NO if you don't know what you're doing)");
    if (answer == IDYES) {
      strcpy(temp_pl.holder, "UNLOCKED");
      if (!mng_ReplacePagelock(temp_pl.name, &temp_pl))
        MessageBox(ErrorString, "Error!");
    }
  } else if (r < 0)
    OutrageMessageBox(ErrorString);
  else if (r == 1)
    OutrageMessageBox(InfoString);
  else {

    // Everything is ok.  Tell the network we're locking it and get a copy to
    // our local drive
    strcpy(temp_pl.holder, TableUser);

    // Search thru the net pagefile and get a new copy in RAM in case anyone
    // changed it since we started the editor
    if (mng_FindSpecificGamefilePage(temp_pl.name, &gamefilepage, 0)) {

      if (mng_AssignGamefilePageToGamefile(&gamefilepage, n)) {
        if (!mng_ReplacePage(Gamefiles[n].name, Gamefiles[n].name, n, PAGETYPE_GAMEFILE, 1)) {
          OutrageMessageBox("There was problem writing that page locally!");
          mng_EraseLocker();
          return;
        } else {

          if (!mng_ReplacePagelock(temp_pl.name, &temp_pl)) {
            MessageBox(ErrorString, "Error!");
            mng_EraseLocker();
            return;
          }
        }
        OutrageMessageBox("Gamefile locked.");
      } else
        OutrageMessageBox(
            "There was a problem loading this gamefile.  You might encounter problems in dealing with it.  Good luck!");

      mng_AllocTrackLock(Gamefiles[n].name, PAGETYPE_GAMEFILE);
      UpdateDialog();
    } else
      OutrageMessageBox("Couldn't find that gamefile in the table file!");
  }

  mng_EraseLocker();

  // Read the data again in case we just got a newer file
  ReadAmbientData();

  m_locked = 1;

  UpdateDialog();
}

void CAmbientSoundPattern::OnASPCheckIn() {
  // Write the updated data to the file
  WriteAmbientData();

  // Find where it is in the files index
  int n = FindGamefileName(AMBIENT_FILE_NAME);

  if (n == -1) {
    Int3(); // Get Matt or Jason - AMBIENT file name is no longer in the table file!
    return;
  }

  mngs_Pagelock temp_pl;
  int r;

  // Lock the network table file
  if (!mng_MakeLocker())
    return;

  // Make sure we own this gamefile
  strcpy(temp_pl.name, Gamefiles[n].name);
  temp_pl.pagetype = PAGETYPE_GAMEFILE;

  r = mng_CheckIfPageOwned(&temp_pl, TableUser);
  if (r < 0)
    OutrageMessageBox(ErrorString);
  else if (r == 0)
    OutrageMessageBox(InfoString);
  else {
    // Change the pagelock state to UNLOCKED
    strcpy(temp_pl.holder, "UNLOCKED");
    if (!mng_ReplacePagelock(temp_pl.name, &temp_pl)) {
      MessageBox(ErrorString, "Error!");
      mng_EraseLocker();
      return;
    } else {
      // Now actually replace the copy on the net with our local one

      if (!mng_ReplacePage(Gamefiles[n].name, Gamefiles[n].name, n, PAGETYPE_GAMEFILE, 0))
        OutrageMessageBox(ErrorString);
      else {
        // Save this gamefile file to the network for all

        char destname[100], srcname[100];

        ddio_MakePath(srcname, LocalD3Dir, "data", Gamefiles[n].dir_name, Gamefiles[n].name, NULL);
        ddio_MakePath(destname, NetD3Dir, "data", Gamefiles[n].dir_name, Gamefiles[n].name, NULL);

        cf_CopyFile(destname, srcname);

        OutrageMessageBox("File checked in.");

        // Delete it from local pagefile if its there
        int dret = mng_DeletePage(Gamefiles[n].name, PAGETYPE_GAMEFILE, 1);
        ASSERT(dret == 1);
        mng_EraseLocker();

        // Free the tracklock
        int p = mng_FindTrackLock(Gamefiles[n].name, PAGETYPE_GAMEFILE);
        ASSERT(p != -1);
        mng_FreeTrackLock(p);
      }
    }
  }

  m_locked = 0;
  UpdateDialog();
}

void CAmbientSoundPattern::OnASPUndoLock() {
  int tl;
  mngs_Pagelock pl;
  mngs_gamefile_page page;

  // Should have this item locked
  if ((tl = mng_FindTrackLock(AMBIENT_FILE_NAME, PAGETYPE_GAMEFILE)) == -1)
    return;

  if (OutrageMessageBox(MBOX_YESNO,
                        "Are you sure you want to undo your lock and lose any changes you may have made?") != IDYES)
    return;

  if (!mng_MakeLocker())
    return;

  // Find where it is in the files index
  int n = FindGamefileName(AMBIENT_FILE_NAME);

  if (n == -1) {
    Int3(); // Get Matt or Jason - AMBIENT file name is no longer in the table file!
    return;
  }

  strcpy(pl.name, AMBIENT_FILE_NAME);
  pl.pagetype = PAGETYPE_GAMEFILE;

  mng_FreeTrackLock(tl);

  // Delete local page
  if (!mng_DeletePage(AMBIENT_FILE_NAME, PAGETYPE_GAMEFILE, 1)) {
    mprintf(0, ErrorString);
    Int3();
  }

  // Get old data from net
  if (!mng_FindSpecificGamefilePage(pl.name, &page))
    Int3();

  // Copy old get into our local array
  if (!mng_AssignGamefilePageToGamefile(&page, n))
    Int3();

  // We're done
  mng_EraseLocker();

  // Re-read old data
  ReadAmbientData();

  m_current_asp = -1;
  m_locked = 0;

  UpdateDialog();
}
