//
//  The contents of this file are subject to the Mozilla Public License
//  Version 1.0 (the "License"); you may not use this file except in
//  compliance with the License. You may obtain a copy of the License at
//  http://www.mozilla.org/MPL/
// 
//  Software distributed under the License is distributed on an "AS IS"
//  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
//  the License for the specific language governing rights and limitations
//  under the License.
// 
//  The Original Code is CPhone, a cross platform voip gui.
//
//  The Initial Developer of the Original Code is Derek Smithies.
//
//  Copyright (C) 2002 Indranet Technologies Ltd, 
//                                  http://www.indranet-technologies.com
//  All Rights Reserved.
//
//  Contributor(s): _______________


/*
 *
 *  $Log: settingsSub.cxx,v $
 *  Revision 1.10  2004/04/14 08:43:07  dereksmithies
 *  Fixes for windows etc.
 *
 *  Revision 1.9  2004/02/18 03:16:55  dereksmithies
 *  Fix Nat translation feature.  Thanks to dag@bakke.com for finding the problem.
 *
 *  Revision 1.8  2003/09/02 03:37:32  dereksmithies
 *  Add statistics, new icons, toolbuttons (icon + name)
 *
 *  Revision 1.7  2003/08/26 04:48:02  dereksmithies
 *  Fixes to get statistics working right, add icons, add ok,cancel,apply buttons
 *
 *  Revision 1.6  2003/08/11 01:48:14  dereksmithies
 *  Fix windows compile errors
 *
 *  Revision 1.5  2003/05/09 04:34:46  dereksmithies
 *  Tidy up gatekeeper finding. Works reliably.
 *
 *  Revision 1.4  2003/05/09 03:08:24  dereksmithies
 *  Fix Microtelco handling, and message display
 *
 *  Revision 1.3  2003/05/06 22:40:36  dereksmithies
 *  Fixes to get it working correctly on microtelco.
 *
 *  Revision 1.2  2003/04/04 04:37:49  dereksmithies
 *  Major upgrade.
 *  Ixj & microtelco support added. Fix threading issues.
 *
 *  Revision 1.1.1.1  2002/05/12 22:55:05  dereksmithies
 *  Initial release.
 *
 *
 *
 *
 */


#include <qapplication.h>
#include <qcheckbox.h>
#include <qfiledialog.h>
#include <qlineedit.h>
#include <qlistview.h>
#include <qobjcoll.h>
#include <qtoolbutton.h>
#include <qradiobutton.h>
#include <qslider.h>
#include <qspinbox.h>

#include "settingsSub.h"
#include "cpendpoint.h"
#include "message.h"
#include "vdisplay.h"

#include "images/microtelcoOn.xpm"
#include "images/microtelcoOff.xpm"

#include "images/nat_traversal_off.xpm"
#include "images/nat_traversal_on.xpm"

#include <ptlib.h>

#define CUSTOM_EVENT_SETUP      (QEvent::User + 1)

/* 
 *  Constructs a Settings which is a child of 'parent', with the 
 *  name 'name' and widget flags set to 'f' 
 *
 *  The dialog will by default be modeless, unless you set 'modal' to
 *  TRUE to construct a modal dialog.
 */
Settings::Settings(ConnectOptions & co)
  : FormConnectionSettings( 0, "Settings", FALSE, WDestructiveClose),
    connectOpts(co),
    controlsSet(false)
{
  move(300, 300);

  CPhone::Current().SetWindowId(eSettings, winId());

  QApplication::postEvent(this, new QCustomEvent(CUSTOM_EVENT_SETUP));
}

/*  
 *  Destroys the object and frees any allocated resources
 */
Settings::~Settings()
{
}

void Settings::Setup()
{
  InitialiseCodecSettings();
  
  CheckBoxAutoAnswer->setChecked(connectOpts.autoAnswer);
  CheckBoxDisableFastStart->setChecked(connectOpts.noFastStart);
  CheckBoxDisableH245Tunnel->setChecked(connectOpts.noH245Tunnelling);
  
  CheckBoxDefaultAudio->setChecked(connectOpts.useDefaultAudioBandwidth);
  CheckBoxDefaultVideo->setChecked(connectOpts.useDefaultVideoBandwidth);

  SpinBoxFramesEthernetPacket->setValue(connectOpts.packetUtilization);
  
  SpinBoxBandwidthAudio->setValue(connectOpts.audioBandwidth);
  SpinBoxBandwidthVideo->setValue(connectOpts.videoBandwidth);

  LineEditGatekeeper->setText((const char *)connectOpts.gatekeeperName.GetPointer());
  LineEditGatekeeper->setEnabled(!connectOpts.searchForGatekeeper);
  LineEditUserName->setText((const char *)connectOpts.userName.GetPointer());

  toolButtonNatTraversal->setOn((connectOpts.doNatTraversal));
  LineEditNatTraversal->setText((const char *)connectOpts.externalNatAddress.GetPointer());

  toolButtonEnableMicroTelco->setOn((connectOpts.doMicroTelco));
  LineEditUtAccount->setText((const char *)connectOpts.microTelcoAccount.GetPointer());
  LineEditUtPassword->setText((const char *)connectOpts.microTelcoPassword.GetPointer());

  SpinBoxLowUdp->setValue(connectOpts.lowUdpPort);
  SpinBoxHighUdp->setValue(connectOpts.highUdpPort);

  CheckBoxSearchForAGatekeeper->setChecked(connectOpts.searchForGatekeeper);

  checkBoxEnableLogging->setChecked(connectOpts.doLogInternalActivity);
  spinBoxLogLevel->setValue(connectOpts.logLevel);
  logFileName = (const char *)connectOpts.logFileName;

  checkBoxBlocks->setChecked(connectOpts.logBlocks);
  checkBoxDateTime->setChecked(connectOpts.logDateTime);
  checkBoxFileLine->setChecked(connectOpts.logFileLine);
  checkBoxLevelNumber->setChecked(connectOpts.logLevelNumber);
  checkBoxThreadAddress->setChecked(connectOpts.logThreadAddress);
  checkBoxThreadNames->setChecked(connectOpts.logThreadNames);
  checkBoxTimestamp->setChecked(connectOpts.logTimestamp);
   
  /////
  controlsSet = true;
 
  NatTraversalChangedSlot();
  MicroTelcoChangedSlot();
  UpdateLogFileName();
  EnableDisableLoggingSlot();

  UpdateCurrentGatekeeperText();

  BOOL inACall = !CPhone::GetUi().WaitingForACall();
  
  OnConnectionEstablished(inACall);
}

void Settings::InitialiseCodecSettings()
{
  ListViewActiveCodecs->setSorting(-1);
  ListViewActiveCodecs->setColumnWidthMode(0, QListView::Maximum);

  ListViewDormantCodecs->setSorting(-1);
  ListViewDormantCodecs->setColumnWidthMode(0, QListView::Maximum);
       
  PStringList activeCodecs  = connectOpts.GetEnabledCapabilities();
  PStringList dormantCodecs = connectOpts.GetDisabledCapabilities();
  
  PINDEX i;
  for(i=0; i < activeCodecs.GetSize(); i++) 
      AddItemToList(ListViewActiveCodecs, activeCodecs[i]);

  for(i=0; i < dormantCodecs.GetSize(); i++) 
      AddItemToList(ListViewDormantCodecs, dormantCodecs[i]);
}


void Settings::AddItemToList(QListView *srcList, PString label)
{
  QListViewItem *lastItem = srcList->firstChild();
  if (lastItem != NULL) {

    QListViewItem *lead = lastItem->nextSibling();
    while (lead != NULL) {
      lastItem = lead;
      lead     = lead->nextSibling();
    }
  }

  QListViewItem *item;

  if(lastItem == NULL)
    item = new QListViewItem(srcList);
  else
    item = new QListViewItem(srcList, lastItem);

  item->setText( 0, label.GetPointer() );
}


//Remove all the old codec kesy in the PConfig structure. Put new ones 
//in. The order of insertion is important, as it determines the 
//priority ranking of the codec.
void Settings::SaveCodecSettings()
{
  PStringList dormantCodecs;

  PStringList activeCodecs;
  QListViewItem *item = ListViewActiveCodecs->firstChild();
  while (item != NULL) {
    activeCodecs.AppendString((const char *)item->text(0));
    item = item->nextSibling();
  }
  connectOpts.SetEnabledCapabilities(activeCodecs);

  CPhone::GetUi().RebuildCapabilityTable();
}

void Settings::MakeCodecActive_slot()
{
  QListViewItem *next;
  QListViewItem *item = ListViewDormantCodecs->firstChild();

  while (item != NULL) {
    next = item->nextSibling();

    if(item->isSelected()) {
      QString codecName = item->text(0);
      delete item;

      AddItemToList(ListViewActiveCodecs, PString((const char *)codecName));
    } 
    item = next;
  }
}

void Settings::MakeCodecDormant_slot()
{
  QListViewItem *next;
  QListViewItem *item = ListViewActiveCodecs->firstChild();

  while (item != NULL) {
    next = item->nextSibling();

    if(item->isSelected()) {
      QString codecName = item->text(0);
      delete item;

      AddItemToList(ListViewDormantCodecs, PString((const char *)codecName));
    } 
    item = next;
  }
}

void Settings::MoveCodecUp_slot()
{
  PStringList list = GetSelectedCapabilities();

  if (list.GetSize() == 0)
    return;
  
  //First check that we can move everything down;
  QListViewItem *thisCodec,*parentCodec,*grandParentCodec;
  thisCodec = FindThisCodec(QString((const char *)list[0]));

  if(thisCodec->itemAbove() == NULL) {
    return;
  }
  //We can move all items up.  
  for(PINDEX i= 0; i < list.GetSize();  i++) {
    thisCodec = FindThisCodec(QString((const char *)list[i]));
    if(thisCodec == NULL)
      return;
    parentCodec = thisCodec->itemAbove();
    if(parentCodec == NULL) 
      return;

    grandParentCodec = parentCodec->itemAbove();

    if(grandParentCodec == NULL) {
      thisCodec->moveItem(ListViewActiveCodecs->firstChild());
      ListViewActiveCodecs->firstChild()->moveItem(thisCodec);
    } else
      thisCodec->moveItem(grandParentCodec);
  }
}

void Settings::MoveCodecDown_slot()
{
  PStringList list = GetSelectedCapabilities();

  if (list.GetSize() == 0)
    return;

  //First check that we can move everything down;
  QListViewItem *thisCodec;
  thisCodec = FindThisCodec(QString((const char *)list[list.GetSize()-1]));

  if(thisCodec == NULL)
    return;

  if(thisCodec->nextSibling() == NULL)
    return;

  //We can move all items down.  
  for(unsigned int i= list.GetSize(); i>0; i--) {
    thisCodec = FindThisCodec(QString((const char *)list[i-1]));
    if(thisCodec == NULL)
      return;
    thisCodec->moveItem(thisCodec->nextSibling());
  }
}


PStringList Settings::GetSelectedCapabilities()
{
  PStringList list;
  QListViewItem *item = ListViewActiveCodecs->firstChild();
  
  while (item != NULL) {
    if(item->isSelected()) 
      list.AppendString(PString((const char *)item->text(0)));
    item = item->nextSibling();
  }
  return list;
}


QListViewItem * Settings::FindThisCodec(QString sourceCodec) 
{
  QListViewItem  *thisCodec  = ListViewActiveCodecs->firstChild();

  while (thisCodec != NULL) {
    if (thisCodec->text(0) == sourceCodec) {
      return thisCodec;
    }
    thisCodec = thisCodec->nextSibling();
  }

  return NULL;
}  


void Settings::UpdateCurrentGatekeeperText()
{
  TextLabelMicroTelcoStatus->setText((const char *)CPhone::GetUi().GetGatekeeperName());

  if  (!TextLabelMicroTelcoStatus->text().isEmpty())
    DisplayMessage(QString("Gatekeeper set to ") + TextLabelMicroTelcoStatus->text());
}



void Settings::DisplayMessage(const char * message)
{
  DisplayMessage(QString(message));
}

void Settings::DisplayMessage(PString message)
{
  DisplayMessage(QString((const char *)message));
}

void Settings::DisplayMessage(QString message)
{
//  PTRACE(1, "GuiThread\t SettingsWindow " << (const char *)message);
  LabelDisplaySettingsEnabled->prepareDisplayText(message);
  QCustomEvent *ce = new QCustomEvent(QEvent::User);
  QApplication::postEvent(LabelDisplaySettingsEnabled, ce);

  killTimers();
  startTimer(20 * 1000); //display text for 20 seconds.
}

void Settings::OnConnectionEstablished(bool isEstablished)
{
  //isEstablished is true means this call is being created.
  //isEstablished is false means this call is terminated.
  DisplayMessage(isEstablished ? QString("Controls disabled during call") : QString("Controls now active"));

  char * patterns[] = {"LineEditGatekeeper" , "LineEditUserName" , "SpinBox.*" , "toolButton.*", 
		       "CheckBox.*", "LineEditNatTraversal", "toolButtonEnableMicroTelco",
		       "LineEditUtAccount,", "LineEditUtPassword", 
		       "SpinBoxLowUdp", "SpinBoxHighUdp", "checkBox.*", "toolButtonEnableLogging", 
		       "pushButtonDoSelectFile", "spinBox.*",  0 };

  int index = 0;
  while (patterns[index] != NULL) {
    QObjectList * l = topLevelWidget()->queryList("QObject", patterns[index]);
    QObjectListIt it( *l );             // iterate over the QObjects matching patterns[index]
    QObject * obj;
    while ((obj = it.current()) != 0) { // for each found QObject
      ++it;
      ((QWidget *)obj)->setEnabled(!isEstablished);
    }
  delete l;                           // delete the list, not the objects
  index ++;
  }
}

void Settings::UpdateLogFileName()
{
  TextLabelLogFileName->setText(logFileName);
}

void Settings::EnableDisableLoggingSlot()
{
  if (!controlsSet)
    return;
  // add more here if needed.
}

void Settings::SelectFileNameSlot()
{
  QFileDialog *dlg = new QFileDialog(QString::null, QString::null, 0, 0, FALSE);
  dlg->setCaption(QFileDialog::tr( "Select log file"));
  dlg->setMode(QFileDialog::AnyFile);
  
  if (dlg->exec() == QDialog::Accepted) 
    logFileName = dlg->selectedFile();
  
  delete dlg;
 
  UpdateLogFileName();
}

void Settings::MicroTelcoChangedSlot()
{
  if (!controlsSet)
    return;

  bool b = ( toolButtonEnableMicroTelco->state() == QButton::On);

  toolButtonEnableMicroTelco->setPixmap((const char **)(b ? microtelcoOn : microtelcoOff));

  toolButtonEnableMicroTelco->setTextLabel((b ? QString("Micro Telco Services Active") : QString("No Micro Telco")));


  CheckBoxSearchForAGatekeeper->setEnabled(!b);
  LineEditGatekeeper->setEnabled(!b);
}

void Settings::NatTraversalChangedSlot()
{
  if (!controlsSet)
    return;

  bool b = (toolButtonNatTraversal->state() == QButton::On);
  toolButtonNatTraversal->setText((b ? "Enable traversal" : "No traversal"));
  toolButtonNatTraversal->setPixmap((const char **) (b ? nat_traversal_on : nat_traversal_off));
}

void Settings::closeEvent(QCloseEvent *e)
{
  CPhone::Current().ForgetExists(eSettings, winId());

  e->accept();
}

void Settings::customEvent(QCustomEvent *e)
{
  if (e->type()  == CUSTOM_EVENT_DIE) 
    close(TRUE);

  if (e->type()  == CUSTOM_EVENT_SETUP) 
    Setup();
}

void Settings::timerEvent(QTimerEvent *)
{
  LabelDisplaySettingsEnabled->clear(); //setText("");
}

void Settings::ApplySettings_Slot()
{
  SaveCurrentEntries();
}

void Settings::CancelSettings_Slot()
{
  close(TRUE);
}

void Settings::OkSettings_Slot()
{
  SaveCurrentEntries();
  close(TRUE);
}

void Settings::SaveCurrentEntries()
{
  SaveCodecSettings();
  SaveGatekeeperMicroTelco();
  SaveConnectionInfo();
  SaveErrorLogging();
}

void Settings::SaveGatekeeperMicroTelco()
{
  PString res;

  connectOpts.doMicroTelco = (toolButtonEnableMicroTelco->state() == QButton::On);

  if (connectOpts.doMicroTelco) {
    BOOL success = CPhone::GetUi().MicroTelcoActivate(true);
    if (success) 
      DisplayMessage("Micro Telco Activated");
    else {
      toolButtonEnableMicroTelco->setOn(false);
      PString res = CPhone::GetUi().GetGatekeeperRegistrationMessage();
      DisplayMessage("Micro Telco Failed : " + res);
      connectOpts.doMicroTelco = false;
      MicroTelcoChangedSlot();
    }
  } 
  
  PString failMessage = "";
  if (toolButtonEnableMicroTelco->state() == QButton::Off) {
    connectOpts.gatekeeperName = (const char *)LineEditGatekeeper->text();
    if (LineEditGatekeeper->text().isEmpty()) {
      connectOpts.searchForGatekeeper = CheckBoxSearchForAGatekeeper->isChecked();
      failMessage = "Could not find any gatekeeper : ";
    } else {
      connectOpts.searchForGatekeeper = false;
      CheckBoxSearchForAGatekeeper->setChecked(false);
      failMessage = LineEditGatekeeper->text() + " not found : ";
    }

    if (!CPhone::GetUi().InitialiseGatekeeper()) {
      PString res = CPhone::GetUi().GetGatekeeperRegistrationMessage();
      DisplayMessage(failMessage + res);
    }
 }

  UpdateCurrentGatekeeperText();
}

void Settings::SaveConnectionInfo()
{
  connectOpts.autoAnswer = CheckBoxAutoAnswer->isChecked();
  connectOpts.noFastStart = CheckBoxDisableFastStart->isChecked();
  connectOpts.noH245Tunnelling = CheckBoxDisableH245Tunnel->isChecked();
  connectOpts.packetUtilization = SpinBoxFramesEthernetPacket->value();
  connectOpts.noH245Tunnelling = CheckBoxDisableH245Tunnel->isChecked();

  long i = SpinBoxBandwidthAudio->value();
  connectOpts.audioBandwidth = i;
  i = SpinBoxBandwidthVideo->value();
  connectOpts.videoBandwidth = i;

  bool b = CheckBoxDefaultAudio->isChecked();
  connectOpts.useDefaultAudioBandwidth = b;
  b = CheckBoxDefaultVideo->isChecked();
  connectOpts.useDefaultVideoBandwidth = b;

  b = (toolButtonNatTraversal->state() == QButton::On);
  if (b && LineEditNatTraversal->text().isEmpty()) {
    DisplayMessage("No external ip address supplied");
    toolButtonNatTraversal->setDown(false);
    b = false;
  }

  connectOpts.doNatTraversal = b;
  connectOpts.externalNatAddress = 
    (const char *)LineEditNatTraversal->text();
  CPhone::GetUi().UpdateNatTraversalAddress();

  QString s = LineEditUserName->text();
  if (s.isEmpty()) {
    s = PProcess::Current().GetUserName().GetPointer();
    DisplayMessage("Empty user name is ignored.  Using \"" + s + "\"");
  }
  connectOpts.userName = (const char *)s;
  CPhone::GetUi().UpdateUserName();

  s = LineEditUtPassword->text();
  connectOpts.microTelcoPassword = (const char *)s;

  int l, h, t;
  l = SpinBoxLowUdp->value();
  h = SpinBoxHighUdp->value();

  if (l > h) {
    t = l;
    l = h;
    h = t;
  }
  connectOpts.highUdpPort = h;  
  connectOpts.lowUdpPort = l;
}



void Settings::SaveErrorLogging()
{
  connectOpts.doLogInternalActivity = checkBoxEnableLogging->isChecked();
  connectOpts.logLevel= spinBoxLogLevel->value();
  connectOpts.logFileName = (const char *)logFileName;

  connectOpts.logBlocks        = checkBoxBlocks->isChecked();
  connectOpts.logDateTime      = checkBoxDateTime->isChecked();
  connectOpts.logFileLine      = checkBoxFileLine->isChecked();
  connectOpts.logLevelNumber   = checkBoxLevelNumber->isChecked();
  connectOpts.logThreadAddress = checkBoxThreadAddress->isChecked();
  connectOpts.logThreadNames   = checkBoxThreadNames->isChecked();
  connectOpts.logTimestamp     = checkBoxTimestamp->isChecked(); 

  connectOpts.InstallLogging();
}




syntax highlighted by Code2HTML, v. 0.9.1