<commit_before>// Begin CVS Header
//   $Source: /Volumes/Home/Users/shoops/cvs/copasi_dev/copasi/MIRIAMUI/CQMiriamWidget.cpp,v $
//   $Revision: 1.17 $
//   $Name:  $
//   $Author: aekamal $
//   $Date: 2009/09/28 14:53:30 $
// End CVS Header

// Copyright (C) 2008 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., EML Research, gGmbH, University of Heidelberg,
// and The University of Manchester.
// All rights reserved.

#include <QMessageBox>
#include <QHeaderView>
#include <QClipboard>

#include "CQMiriamWidget.h"
#include "copasi.h"
#include "UI/qtUtilities.h"
#include "MIRIAM/CModelMIRIAMInfo.h"
#include "report/CCopasiRootContainer.h"
#include "commandline/CConfigurationFile.h"

/*
 *  Constructs a CQMiriamWidget which is a child of 'parent', with the
 *  name 'name'.'
 */
CQMiriamWidget::CQMiriamWidget(QWidget* parent, const char* name)
    : CopasiWidget(parent, name)
{
  setupUi(this);

  // Create the MIRIAM Info
  mpMIRIAMInfo = new CMIRIAMInfo();

  //Create Data Models for the 4 tables
  mpCreatorDM = new CQCreatorDM(mpMIRIAMInfo, this);
  mpReferenceDM = new CQReferenceDM(mpMIRIAMInfo, this);
  mpBiologicalDescriptionDM = new CQBiologicalDescriptionDM(mpMIRIAMInfo, this);
  mpModifiedDM = new CQModifiedDM(mpMIRIAMInfo, this);

  //Create Proxy Data Models for the 4 tables
  mpCreatorPDM = new CQSortFilterProxyModel();
  mpReferencePDM = new CQSortFilterProxyModel();
  mpBiologicalDescriptionPDM = new CQSortFilterProxyModel();
  mpModifiedPDM = new CQSortFilterProxyModel();

  //Create Required Delegates
  mpResourceDelegate1 = new CQComboDelegate(&mResources, this);
  mpTblReferences->setItemDelegateForColumn(COL_RESOURCE_REFERENCE, mpResourceDelegate1);

  mpResourceDelegate2 = new CQComboDelegate(&mReferences, this);
  mpTblDescription->setItemDelegateForColumn(COL_RESOURCE_BD, mpResourceDelegate2);

  mpPredicateDelegate = new CQComboDelegate(&mPredicates, this);
  mpTblDescription->setItemDelegateForColumn(COL_RELATIONSHIP, mpPredicateDelegate);

  mWidgets.push_back(mpTblAuthors); mDMs.push_back(mpCreatorDM); mProxyDMs.push_back(mpCreatorPDM);
  mWidgets.push_back(mpTblReferences); mDMs.push_back(mpReferenceDM); mProxyDMs.push_back(mpReferencePDM);
  mWidgets.push_back(mpTblDescription); mDMs.push_back(mpBiologicalDescriptionDM); mProxyDMs.push_back(mpBiologicalDescriptionPDM);
  mWidgets.push_back(mpTblModified); mDMs.push_back(mpModifiedDM); mProxyDMs.push_back(mpModifiedPDM);

  // Build the list of supported predicates
  mPredicates.push_back("-- select --");
  mPredicates.push_back(FROM_UTF8(CRDFPredicate::getDisplayName(CRDFPredicate::copasi_encodes)));
  mPredicates.push_back(FROM_UTF8(CRDFPredicate::getDisplayName(CRDFPredicate::copasi_hasPart)));
  mPredicates.push_back(FROM_UTF8(CRDFPredicate::getDisplayName(CRDFPredicate::copasi_hasVersion)));
  mPredicates.push_back(FROM_UTF8(CRDFPredicate::getDisplayName(CRDFPredicate::copasi_is)));
  mPredicates.push_back(FROM_UTF8(CRDFPredicate::getDisplayName(CRDFPredicate::copasi_isEncodedBy)));
  mPredicates.push_back(FROM_UTF8(CRDFPredicate::getDisplayName(CRDFPredicate::copasi_isHomologTo)));
  mPredicates.push_back(FROM_UTF8(CRDFPredicate::getDisplayName(CRDFPredicate::copasi_isPartOf)));
  mPredicates.push_back(FROM_UTF8(CRDFPredicate::getDisplayName(CRDFPredicate::copasi_isVersionOf)));

  std::vector<QTableView*>::const_iterator it = mWidgets.begin();
  std::vector<QTableView*>::const_iterator end = mWidgets.end();

  std::vector<CQBaseDataModel*>::const_iterator itDM = mDMs.begin();
  std::vector<CQBaseDataModel*>::const_iterator endDM = mDMs.end();

  std::vector<CQSortFilterProxyModel*>::const_iterator itPDM = mProxyDMs.begin();
  std::vector<CQSortFilterProxyModel*>::const_iterator endPDM = mProxyDMs.end();

  for (; it != end && itDM != endDM && itPDM != endPDM; it++, itDM++, itPDM++)
    {
      //Set Proxy Data Model properties
      (*itPDM)->setDynamicSortFilter(true);
      (*itPDM)->setSortCaseSensitivity(Qt::CaseInsensitive);

      (*it)->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents);
      (*it)->verticalHeader()->hide();
      (*it)->sortByColumn(COL_ROW_NUMBER, Qt::AscendingOrder);

      connect((*itDM), SIGNAL(notifyGUI(ListViews::ObjectType, ListViews::Action, const std::string)),
              this, SLOT(protectedNotify(ListViews::ObjectType, ListViews::Action, const std::string)));
      connect((*itDM), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
              this, SLOT(dataChanged(const QModelIndex&, const QModelIndex&)));
    }

  // Build the list of known resources
  updateResourcesList();
}

/*
 *  Destroys the object and frees any allocated resources
 */
CQMiriamWidget::~CQMiriamWidget()
{
  pdelete(mpCreatorPDM);
  pdelete(mpReferencePDM);
  pdelete(mpBiologicalDescriptionPDM);
  pdelete(mpModifiedPDM);
  pdelete(mpCreatorDM);
  pdelete(mpReferenceDM);
  pdelete(mpBiologicalDescriptionDM);
  pdelete(mpModifiedDM);
  pdelete(mpResourceDelegate1);
  pdelete(mpResourceDelegate2);
  pdelete(mpPredicateDelegate);
  pdelete(mpMIRIAMInfo);
  // no need to delete child widgets, Qt does it all for us
}

/*
 *  Sets the strings of the subwidgets using the current
 *  language.
 */
void CQMiriamWidget::languageChange()
{
  retranslateUi(this);
}

void CQMiriamWidget::slotBtnDeleteClicked()
{
  if (mpTblAuthors->hasFocus())
    {deleteSelectedAuthors();}
  else if (mpTblReferences->hasFocus())
    {deleteSelectedReferences();}
  else if (mpTblModified->hasFocus())
    {deleteSelectedModifieds();}
  else if (mpTblDescription->hasFocus())
    {deleteSelectedBiologicalDescriptions();}
}

void CQMiriamWidget::deleteSelectedAuthors()
{
  QModelIndexList selRows = mpTblAuthors->selectionModel()->selectedRows(0);

  if (selRows.empty())
    {return;}

  QModelIndexList mappedSelRows;
  QModelIndexList::const_iterator i;

  for (i = selRows.begin(); i != selRows.end(); ++i)
    {mappedSelRows.append(mpCreatorPDM->mapToSource(*i));}

  mpCreatorDM->removeRows(mappedSelRows);
}

void CQMiriamWidget::deleteSelectedReferences()
{
  QModelIndexList selRows = mpTblReferences->selectionModel()->selectedRows(0);

  if (selRows.empty())
    {return;}

  QModelIndexList mappedSelRows;
  QModelIndexList::const_iterator i;

  for (i = selRows.begin(); i != selRows.end(); ++i)
    {mappedSelRows.append(mpReferencePDM->mapToSource(*i));}

  mpReferenceDM->removeRows(mappedSelRows);
}

void CQMiriamWidget::deleteSelectedBiologicalDescriptions()
{

  QModelIndexList selRows = mpTblDescription->selectionModel()->selectedRows(0);

  if (selRows.empty())
    {return;}

  QModelIndexList mappedSelRows;
  QModelIndexList::const_iterator i;

  for (i = selRows.begin(); i != selRows.end(); ++i)
    {mappedSelRows.append(mpBiologicalDescriptionPDM->mapToSource(*i));}

  mpBiologicalDescriptionDM->removeRows(mappedSelRows);
}

void CQMiriamWidget::deleteSelectedModifieds()
{

  QModelIndexList selRows = mpTblModified->selectionModel()->selectedRows(0);

  if (selRows.empty())
    {return;}

  QModelIndexList mappedSelRows;
  QModelIndexList::const_iterator i;

  for (i = selRows.begin(); i != selRows.end(); ++i)
    {mappedSelRows.append(mpModifiedPDM->mapToSource(*i));}

  mpModifiedDM->removeRows(mappedSelRows);
}

void CQMiriamWidget::slotBtnClearClicked()
{
  if (mpDTCreated->hasFocus())
    {
      mpDTCreated->setDateTime(QDateTime::currentDateTime());
      return;
    }

  if (mpTblAuthors->hasFocus())
    {
      int ret = QMessageBox::question(this, tr("Confirm Delete"), "Delete all Creators?",
                                      QMessageBox::Yes | QMessageBox::No, QMessageBox::No);

      if (ret == QMessageBox::Yes)
        {
          mpCreatorDM->clear();
        }
    }
  else if (mpTblReferences->hasFocus())
    {
      int ret = QMessageBox::question(this, tr("Confirm Delete"), "Delete all References?",
                                      QMessageBox::Yes | QMessageBox::No, QMessageBox::No);

      if (ret == QMessageBox::Yes)
        {
          mpReferenceDM->clear();
        }
    }
  else if (mpTblDescription->hasFocus())
    {
      int ret = QMessageBox::question(this, tr("Confirm Delete"), "Delete all Descriptions?",
                                      QMessageBox::Yes | QMessageBox::No, QMessageBox::No);

      if (ret == QMessageBox::Yes)
        {
          mpBiologicalDescriptionDM->clear();
        }
    }
  else if (mpTblModified->hasFocus())
    {
      int ret = QMessageBox::question(this, tr("Confirm Delete"), "Delete all Date/Time Modifieds?",
                                      QMessageBox::Yes | QMessageBox::No, QMessageBox::No);

      if (ret == QMessageBox::Yes)
        {
          mpModifiedDM->clear();
        }
    }
}

bool CQMiriamWidget::update(ListViews::ObjectType objectType, ListViews::Action C_UNUSED(action), const std::string & key)
{
  if (getIgnoreUpdates())
    return true;

  if (objectType != ListViews::MIRIAM)
    return true;

  if (key != mpMIRIAMInfo->getKey())
    return true;

  bool success = true;
  mpMIRIAMInfo->load(key);

  if (mpMIRIAMInfo->getCreatedDT() != "")
    mpDTCreated->setDateTime(QDateTime::fromString(FROM_UTF8(mpMIRIAMInfo->getCreatedDT()), Qt::ISODate));

  return success;
}

void CQMiriamWidget::slotCreatedDTChanged(QDateTime newDT)
{
  //Now update.
  // Created at
  std::string DT = "";

  if (newDT.isValid())
    {
      DT = TO_UTF8(newDT.toString(Qt::ISODate));
      DT += "Z";

      if (DT != mpMIRIAMInfo->getCreatedDT())
        {
          mpMIRIAMInfo->setCreatedDT(DT);
        }
    }
}

bool CQMiriamWidget::enterProtected()
{
  mpMIRIAMInfo->load(mKey);

  //Set Models for the 4 TableViews
  std::vector<QTableView*>::const_iterator it = mWidgets.begin();
  std::vector<QTableView*>::const_iterator end = mWidgets.end();

  std::vector<CQBaseDataModel*>::const_iterator itDM = mDMs.begin();
  std::vector<CQBaseDataModel*>::const_iterator endDM = mDMs.end();

  std::vector<CQSortFilterProxyModel*>::const_iterator itPDM = mProxyDMs.begin();
  std::vector<CQSortFilterProxyModel*>::const_iterator endPDM = mProxyDMs.end();

  for (; it != end && itDM != endDM && itPDM != endPDM; it++, itDM++, itPDM++)
    {
      (*itPDM)->setSourceModel(*itDM);
      (*it)->setModel(NULL);
      (*it)->setModel(*itPDM);
      (*it)->resizeColumnsToContents();
    }

  QDateTime DTCreated;

  if (mpMIRIAMInfo->getCreatedDT() != "")
    DTCreated = QDateTime::fromString(FROM_UTF8(mpMIRIAMInfo->getCreatedDT()), Qt::ISODate);

  if (DTCreated.isValid())
    mpDTCreated->setDateTime(DTCreated);
  else
    {
      mpDTCreated->setDateTime(QDateTime::currentDateTime());
    }

  return true;
}

bool CQMiriamWidget::leave()
{
  mpMIRIAMInfo->save();
  return true;
}

const CMIRIAMInfo & CQMiriamWidget::getMIRIAMInfo() const
{return *mpMIRIAMInfo;}

void CQMiriamWidget::updateResourcesList()
{
  mResources.clear();
  mReferences.clear();
  // Build the list of known resources
  assert(CCopasiRootContainer::getDatamodelList()->size() > 0);
  const CMIRIAMResources * pResource = &CCopasiRootContainer::getConfiguration()->getRecentMIRIAMResources();
  mResources.push_back("-- select --");
  mReferences.push_back("-- select --");

  unsigned C_INT32 i, imax = pResource->getResourceList().size();

  for (i = 0; i < imax; i++)
    if (pResource->getMIRIAMResource(i).getMIRIAMCitation())
      mResources.push_back(FROM_UTF8(pResource->getMIRIAMResource(i).getMIRIAMDisplayName()));
    else
      mReferences.push_back(FROM_UTF8(pResource->getMIRIAMResource(i).getMIRIAMDisplayName()));
}

void CQMiriamWidget::keyPressEvent(QKeyEvent* ev)
{
  if (ev->key() == Qt::Key_Delete)
    slotBtnDeleteClicked();
  else if (ev->key() == Qt::Key_C && ev->modifiers() & Qt::ControlModifier)
    slotCopyEvent();
}

void CQMiriamWidget::dataChanged(const QModelIndex& C_UNUSED(topLeft), const QModelIndex& C_UNUSED(bottomRight))
{
  std::vector<QTableView*>::const_iterator it = mWidgets.begin();
  std::vector<QTableView*>::const_iterator end = mWidgets.end();

  for (; it != end; it++)
    (*it)->resizeColumnsToContents();
}

void CQMiriamWidget::slotCopyEvent()
{
  CQSortFilterProxyModel* pProxyModel = NULL;
  CQBaseDataModel* pBaseDM = NULL;
  QTableView* pTbl = NULL;

  if (mpTblAuthors->hasFocus())
    {
      pProxyModel = mpCreatorPDM;
      pBaseDM = mpCreatorDM;
      pTbl = mpTblAuthors;
    }
  else if (mpTblReferences->hasFocus())
    {
      pProxyModel = mpReferencePDM;
      pBaseDM = mpReferenceDM;
      pTbl = mpTblReferences;
    }
  else if (mpTblModified->hasFocus())
    {
      pProxyModel = mpModifiedPDM;
      pBaseDM = mpModifiedDM;
      pTbl = mpTblModified;
    }
  else if (mpTblDescription->hasFocus())
    {
      pProxyModel = mpBiologicalDescriptionPDM;
      pBaseDM = mpBiologicalDescriptionDM;
      pTbl = mpTblDescription;
    }

  QModelIndexList selRows = pTbl->selectionModel()->selectedRows(0);

  if (selRows.empty())
    {return;}

  QString str;
  QModelIndexList::const_iterator i;

  for (i = selRows.begin(); i != selRows.end(); ++i)
    {
      for (int x = 0; x < pBaseDM->columnCount(); ++x)
        {
          if (!pTbl->isColumnHidden(x))
            {
              if (!str.isEmpty())
                str += "\t";

              str += pBaseDM->index(pProxyModel->mapToSource(*i).row(), x).data().toString();
            }
        }

      str += "\n";
    }

  QApplication::clipboard()->setText(str);
}
<commit_msg>Fixed crash when creating a new table entry.<commit_after>// Begin CVS Header
//   $Source: /Volumes/Home/Users/shoops/cvs/copasi_dev/copasi/MIRIAMUI/CQMiriamWidget.cpp,v $
//   $Revision: 1.18 $
//   $Name:  $
//   $Author: shoops $
//   $Date: 2009/11/06 16:02:39 $
// End CVS Header

// Copyright (C) 2008 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., EML Research, gGmbH, University of Heidelberg,
// and The University of Manchester.
// All rights reserved.

#include <QMessageBox>
#include <QHeaderView>
#include <QClipboard>

#include "CQMiriamWidget.h"
#include "copasi.h"
#include "UI/qtUtilities.h"
#include "MIRIAM/CModelMIRIAMInfo.h"
#include "report/CCopasiRootContainer.h"
#include "commandline/CConfigurationFile.h"

/*
 *  Constructs a CQMiriamWidget which is a child of 'parent', with the
 *  name 'name'.'
 */
CQMiriamWidget::CQMiriamWidget(QWidget* parent, const char* name)
    : CopasiWidget(parent, name)
{
  setupUi(this);

  // Create the MIRIAM Info
  mpMIRIAMInfo = new CMIRIAMInfo();

  //Create Data Models for the 4 tables
  mpCreatorDM = new CQCreatorDM(mpMIRIAMInfo, this);
  mpReferenceDM = new CQReferenceDM(mpMIRIAMInfo, this);
  mpBiologicalDescriptionDM = new CQBiologicalDescriptionDM(mpMIRIAMInfo, this);
  mpModifiedDM = new CQModifiedDM(mpMIRIAMInfo, this);

  //Create Proxy Data Models for the 4 tables
  mpCreatorPDM = new CQSortFilterProxyModel();
  mpReferencePDM = new CQSortFilterProxyModel();
  mpBiologicalDescriptionPDM = new CQSortFilterProxyModel();
  mpModifiedPDM = new CQSortFilterProxyModel();

  //Create Required Delegates
  mpResourceDelegate1 = new CQComboDelegate(&mResources, this);
  mpTblReferences->setItemDelegateForColumn(COL_RESOURCE_REFERENCE, mpResourceDelegate1);

  mpResourceDelegate2 = new CQComboDelegate(&mReferences, this);
  mpTblDescription->setItemDelegateForColumn(COL_RESOURCE_BD, mpResourceDelegate2);

  mpPredicateDelegate = new CQComboDelegate(&mPredicates, this);
  mpTblDescription->setItemDelegateForColumn(COL_RELATIONSHIP, mpPredicateDelegate);

  mWidgets.push_back(mpTblAuthors); mDMs.push_back(mpCreatorDM); mProxyDMs.push_back(mpCreatorPDM);
  mWidgets.push_back(mpTblReferences); mDMs.push_back(mpReferenceDM); mProxyDMs.push_back(mpReferencePDM);
  mWidgets.push_back(mpTblDescription); mDMs.push_back(mpBiologicalDescriptionDM); mProxyDMs.push_back(mpBiologicalDescriptionPDM);
  mWidgets.push_back(mpTblModified); mDMs.push_back(mpModifiedDM); mProxyDMs.push_back(mpModifiedPDM);

  // Build the list of supported predicates
  mPredicates.push_back("-- select --");
  mPredicates.push_back(FROM_UTF8(CRDFPredicate::getDisplayName(CRDFPredicate::copasi_encodes)));
  mPredicates.push_back(FROM_UTF8(CRDFPredicate::getDisplayName(CRDFPredicate::copasi_hasPart)));
  mPredicates.push_back(FROM_UTF8(CRDFPredicate::getDisplayName(CRDFPredicate::copasi_hasVersion)));
  mPredicates.push_back(FROM_UTF8(CRDFPredicate::getDisplayName(CRDFPredicate::copasi_is)));
  mPredicates.push_back(FROM_UTF8(CRDFPredicate::getDisplayName(CRDFPredicate::copasi_isEncodedBy)));
  mPredicates.push_back(FROM_UTF8(CRDFPredicate::getDisplayName(CRDFPredicate::copasi_isHomologTo)));
  mPredicates.push_back(FROM_UTF8(CRDFPredicate::getDisplayName(CRDFPredicate::copasi_isPartOf)));
  mPredicates.push_back(FROM_UTF8(CRDFPredicate::getDisplayName(CRDFPredicate::copasi_isVersionOf)));

  std::vector<QTableView*>::const_iterator it = mWidgets.begin();
  std::vector<QTableView*>::const_iterator end = mWidgets.end();

  std::vector<CQBaseDataModel*>::const_iterator itDM = mDMs.begin();
  std::vector<CQBaseDataModel*>::const_iterator endDM = mDMs.end();

  std::vector<CQSortFilterProxyModel*>::const_iterator itPDM = mProxyDMs.begin();
  std::vector<CQSortFilterProxyModel*>::const_iterator endPDM = mProxyDMs.end();

  for (; it != end && itDM != endDM && itPDM != endPDM; it++, itDM++, itPDM++)
    {
      //Set Proxy Data Model properties
      (*itPDM)->setDynamicSortFilter(true);
      (*itPDM)->setSortCaseSensitivity(Qt::CaseInsensitive);

      (*it)->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents);
      (*it)->verticalHeader()->hide();
      (*it)->sortByColumn(COL_ROW_NUMBER, Qt::AscendingOrder);

      connect((*itDM), SIGNAL(notifyGUI(ListViews::ObjectType, ListViews::Action, const std::string)),
              this, SLOT(protectedNotify(ListViews::ObjectType, ListViews::Action, const std::string)));
      connect((*itDM), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
              this, SLOT(dataChanged(const QModelIndex&, const QModelIndex&)));
    }

  // Build the list of known resources
  updateResourcesList();
}

/*
 *  Destroys the object and frees any allocated resources
 */
CQMiriamWidget::~CQMiriamWidget()
{
  pdelete(mpCreatorPDM);
  pdelete(mpReferencePDM);
  pdelete(mpBiologicalDescriptionPDM);
  pdelete(mpModifiedPDM);
  pdelete(mpCreatorDM);
  pdelete(mpReferenceDM);
  pdelete(mpBiologicalDescriptionDM);
  pdelete(mpModifiedDM);
  pdelete(mpResourceDelegate1);
  pdelete(mpResourceDelegate2);
  pdelete(mpPredicateDelegate);
  pdelete(mpMIRIAMInfo);
  // no need to delete child widgets, Qt does it all for us
}

/*
 *  Sets the strings of the subwidgets using the current
 *  language.
 */
void CQMiriamWidget::languageChange()
{
  retranslateUi(this);
}

void CQMiriamWidget::slotBtnDeleteClicked()
{
  if (mpTblAuthors->hasFocus())
    {deleteSelectedAuthors();}
  else if (mpTblReferences->hasFocus())
    {deleteSelectedReferences();}
  else if (mpTblModified->hasFocus())
    {deleteSelectedModifieds();}
  else if (mpTblDescription->hasFocus())
    {deleteSelectedBiologicalDescriptions();}
}

void CQMiriamWidget::deleteSelectedAuthors()
{
  QModelIndexList selRows = mpTblAuthors->selectionModel()->selectedRows(0);

  if (selRows.empty())
    {return;}

  QModelIndexList mappedSelRows;
  QModelIndexList::const_iterator i;

  for (i = selRows.begin(); i != selRows.end(); ++i)
    {mappedSelRows.append(mpCreatorPDM->mapToSource(*i));}

  mpCreatorDM->removeRows(mappedSelRows);
}

void CQMiriamWidget::deleteSelectedReferences()
{
  QModelIndexList selRows = mpTblReferences->selectionModel()->selectedRows(0);

  if (selRows.empty())
    {return;}

  QModelIndexList mappedSelRows;
  QModelIndexList::const_iterator i;

  for (i = selRows.begin(); i != selRows.end(); ++i)
    {mappedSelRows.append(mpReferencePDM->mapToSource(*i));}

  mpReferenceDM->removeRows(mappedSelRows);
}

void CQMiriamWidget::deleteSelectedBiologicalDescriptions()
{

  QModelIndexList selRows = mpTblDescription->selectionModel()->selectedRows(0);

  if (selRows.empty())
    {return;}

  QModelIndexList mappedSelRows;
  QModelIndexList::const_iterator i;

  for (i = selRows.begin(); i != selRows.end(); ++i)
    {mappedSelRows.append(mpBiologicalDescriptionPDM->mapToSource(*i));}

  mpBiologicalDescriptionDM->removeRows(mappedSelRows);
}

void CQMiriamWidget::deleteSelectedModifieds()
{

  QModelIndexList selRows = mpTblModified->selectionModel()->selectedRows(0);

  if (selRows.empty())
    {return;}

  QModelIndexList mappedSelRows;
  QModelIndexList::const_iterator i;

  for (i = selRows.begin(); i != selRows.end(); ++i)
    {mappedSelRows.append(mpModifiedPDM->mapToSource(*i));}

  mpModifiedDM->removeRows(mappedSelRows);
}

void CQMiriamWidget::slotBtnClearClicked()
{
  if (mpDTCreated->hasFocus())
    {
      mpDTCreated->setDateTime(QDateTime::currentDateTime());
      return;
    }

  if (mpTblAuthors->hasFocus())
    {
      int ret = QMessageBox::question(this, tr("Confirm Delete"), "Delete all Creators?",
                                      QMessageBox::Yes | QMessageBox::No, QMessageBox::No);

      if (ret == QMessageBox::Yes)
        {
          mpCreatorDM->clear();
        }
    }
  else if (mpTblReferences->hasFocus())
    {
      int ret = QMessageBox::question(this, tr("Confirm Delete"), "Delete all References?",
                                      QMessageBox::Yes | QMessageBox::No, QMessageBox::No);

      if (ret == QMessageBox::Yes)
        {
          mpReferenceDM->clear();
        }
    }
  else if (mpTblDescription->hasFocus())
    {
      int ret = QMessageBox::question(this, tr("Confirm Delete"), "Delete all Descriptions?",
                                      QMessageBox::Yes | QMessageBox::No, QMessageBox::No);

      if (ret == QMessageBox::Yes)
        {
          mpBiologicalDescriptionDM->clear();
        }
    }
  else if (mpTblModified->hasFocus())
    {
      int ret = QMessageBox::question(this, tr("Confirm Delete"), "Delete all Date/Time Modifieds?",
                                      QMessageBox::Yes | QMessageBox::No, QMessageBox::No);

      if (ret == QMessageBox::Yes)
        {
          mpModifiedDM->clear();
        }
    }
}

bool CQMiriamWidget::update(ListViews::ObjectType objectType, ListViews::Action C_UNUSED(action), const std::string & key)
{
  if (getIgnoreUpdates())
    return true;

  if (objectType != ListViews::MIRIAM)
    return true;

  if (key == "" || key != mpMIRIAMInfo->getKey())
    return true;

  bool success = true;
  mpMIRIAMInfo->load(key);

  if (mpMIRIAMInfo->getCreatedDT() != "")
    mpDTCreated->setDateTime(QDateTime::fromString(FROM_UTF8(mpMIRIAMInfo->getCreatedDT()), Qt::ISODate));

  return success;
}

void CQMiriamWidget::slotCreatedDTChanged(QDateTime newDT)
{
  //Now update.
  // Created at
  std::string DT = "";

  if (newDT.isValid())
    {
      DT = TO_UTF8(newDT.toString(Qt::ISODate));
      DT += "Z";

      if (DT != mpMIRIAMInfo->getCreatedDT())
        {
          mpMIRIAMInfo->setCreatedDT(DT);
        }
    }
}

bool CQMiriamWidget::enterProtected()
{
  mpMIRIAMInfo->load(mKey);

  //Set Models for the 4 TableViews
  std::vector<QTableView*>::const_iterator it = mWidgets.begin();
  std::vector<QTableView*>::const_iterator end = mWidgets.end();

  std::vector<CQBaseDataModel*>::const_iterator itDM = mDMs.begin();
  std::vector<CQBaseDataModel*>::const_iterator endDM = mDMs.end();

  std::vector<CQSortFilterProxyModel*>::const_iterator itPDM = mProxyDMs.begin();
  std::vector<CQSortFilterProxyModel*>::const_iterator endPDM = mProxyDMs.end();

  for (; it != end && itDM != endDM && itPDM != endPDM; it++, itDM++, itPDM++)
    {
      (*itPDM)->setSourceModel(*itDM);
      (*it)->setModel(NULL);
      (*it)->setModel(*itPDM);
      (*it)->resizeColumnsToContents();
    }

  QDateTime DTCreated;

  if (mpMIRIAMInfo->getCreatedDT() != "")
    DTCreated = QDateTime::fromString(FROM_UTF8(mpMIRIAMInfo->getCreatedDT()), Qt::ISODate);

  if (DTCreated.isValid())
    mpDTCreated->setDateTime(DTCreated);
  else
    {
      mpDTCreated->setDateTime(QDateTime::currentDateTime());
    }

  return true;
}

bool CQMiriamWidget::leave()
{
  mpMIRIAMInfo->save();
  return true;
}

const CMIRIAMInfo & CQMiriamWidget::getMIRIAMInfo() const
{return *mpMIRIAMInfo;}

void CQMiriamWidget::updateResourcesList()
{
  mResources.clear();
  mReferences.clear();
  // Build the list of known resources
  assert(CCopasiRootContainer::getDatamodelList()->size() > 0);
  const CMIRIAMResources * pResource = &CCopasiRootContainer::getConfiguration()->getRecentMIRIAMResources();
  mResources.push_back("-- select --");
  mReferences.push_back("-- select --");

  unsigned C_INT32 i, imax = pResource->getResourceList().size();

  for (i = 0; i < imax; i++)
    if (pResource->getMIRIAMResource(i).getMIRIAMCitation())
      mResources.push_back(FROM_UTF8(pResource->getMIRIAMResource(i).getMIRIAMDisplayName()));
    else
      mReferences.push_back(FROM_UTF8(pResource->getMIRIAMResource(i).getMIRIAMDisplayName()));
}

void CQMiriamWidget::keyPressEvent(QKeyEvent* ev)
{
  if (ev->key() == Qt::Key_Delete)
    slotBtnDeleteClicked();
  else if (ev->key() == Qt::Key_C && ev->modifiers() & Qt::ControlModifier)
    slotCopyEvent();
}

void CQMiriamWidget::dataChanged(const QModelIndex& C_UNUSED(topLeft), const QModelIndex& C_UNUSED(bottomRight))
{
  std::vector<QTableView*>::const_iterator it = mWidgets.begin();
  std::vector<QTableView*>::const_iterator end = mWidgets.end();

  for (; it != end; it++)
    (*it)->resizeColumnsToContents();
}

void CQMiriamWidget::slotCopyEvent()
{
  CQSortFilterProxyModel* pProxyModel = NULL;
  CQBaseDataModel* pBaseDM = NULL;
  QTableView* pTbl = NULL;

  if (mpTblAuthors->hasFocus())
    {
      pProxyModel = mpCreatorPDM;
      pBaseDM = mpCreatorDM;
      pTbl = mpTblAuthors;
    }
  else if (mpTblReferences->hasFocus())
    {
      pProxyModel = mpReferencePDM;
      pBaseDM = mpReferenceDM;
      pTbl = mpTblReferences;
    }
  else if (mpTblModified->hasFocus())
    {
      pProxyModel = mpModifiedPDM;
      pBaseDM = mpModifiedDM;
      pTbl = mpTblModified;
    }
  else if (mpTblDescription->hasFocus())
    {
      pProxyModel = mpBiologicalDescriptionPDM;
      pBaseDM = mpBiologicalDescriptionDM;
      pTbl = mpTblDescription;
    }

  QModelIndexList selRows = pTbl->selectionModel()->selectedRows(0);

  if (selRows.empty())
    {return;}

  QString str;
  QModelIndexList::const_iterator i;

  for (i = selRows.begin(); i != selRows.end(); ++i)
    {
      for (int x = 0; x < pBaseDM->columnCount(); ++x)
        {
          if (!pTbl->isColumnHidden(x))
            {
              if (!str.isEmpty())
                str += "\t";

              str += pBaseDM->index(pProxyModel->mapToSource(*i).row(), x).data().toString();
            }
        }

      str += "\n";
    }

  QApplication::clipboard()->setText(str);
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015 The Jackson Laboratory
 *
 * This software was developed by Gary Churchill's Lab at The Jackson
 * Laboratory (see http://research.jax.org/faculty/churchill).
 *
 * This 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 software 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 software. If not, see <http://www.gnu.org/licenses/>.
 */

//
//  populase.cpp
//
//
//  Created by Glen Beane on 8/20/14.
//
//

#include <iostream>

#include <getopt.h>

#include "alignment_incidence_matrix.h"
#include "sample_allelic_expression.h"
#include "python_interface.h"

void print_help();

int main(int argc, char **argv)
{
    clock_t t1, t2;
    float diff;

    int num_iterations;
    int max_iterations = 200;
    int read_length = 100;

    SampleAllelicExpression::model model = SampleAllelicExpression::MODEL_2;
    int m;

    std::string input_filename;
    std::string output_filename;
    std::string transcript_length_file;
    std::string extension = ".pcl.bz2";

    //getopt related variables
    opterr = 0;
    int c;
    int option_index = 0;

    static struct option long_options[] = {
        {"help", no_argument, 0, 'h'},
        {"model", required_argument, 0, 'm'},
        {"output", required_argument, 0, 'o'},
        {"read-length", required_argument, 0, 'k'},
        {"transcript-lengths", required_argument, 0, 'l'},
        {"max-iterations", required_argument, 0, 'i'},
        {0, 0, 0, 0}
    };

    while ((c = getopt_long(argc, argv, "hm:o:k:l:i:", long_options, &option_index)) != -1) {
        switch (c) {
            case 'h':
                print_help();
                return 0;

            case 'm':
                m = std::stoi(optarg);
                if (m < 1 || m > 4) {
                    std::cerr << "Invalid model number specified. Valid options: 1, 2, 3, 4\n";
                }

                switch (m) {
                    case 1:
                        model = SampleAllelicExpression::MODEL_1;
                        break;
                    case 2:
                        model = SampleAllelicExpression::MODEL_2;
                        break;
                    case 3:
                        std::cerr << "Model 3 is currently unimplemented, please specify a different model\n";
                        return 1;
                    case 4:
                        model = SampleAllelicExpression::MODEL_4;
                        break;
                }

                break;

            case 'o':
                output_filename = std::string(optarg);
                break;

            case 'k':
                read_length = std::stoi(optarg);
                break;

            case 'l':
                transcript_length_file = std::string(optarg);
                break;

            case 'i':
                max_iterations = std::stoi(optarg);

        }
    }

    input_filename = argv[optind];

    if (extension.size() >= input_filename.size() || !std::equal(extension.rbegin(), extension.rend(), input_filename.rbegin())) {
        std::cerr << "Error, expected file with .pcl.bz2 extension. Input file should be prepared with bam_to_pcl.py script.\n";
        return 1;
    }

    if (output_filename.empty()) {
        //use default, based on the input file name but placed in current working directdory
        output_filename = input_filename.substr(0, input_filename.size() - extension.size()).append(".stacksum.tsv");

        //check to see if there was a path in the input file name.  If so, trim it off
        std::size_t found = output_filename.rfind('/');
        if (found != std::string::npos) {
            output_filename = output_filename.substr(found+1);
        }

    }

    PythonInterface pi = PythonInterface();
    if (pi.init()){
        std::cerr << "Error importing TranscriptHits Python module.\n";
        std::cerr << '\t' << pi.getErrorString() << std::endl;
        return 1;
    }

    std::cout << "Loading " << input_filename << ". This may take a while..." << std::endl;
    AlignmentIncidenceMatrix *aim = pi.load(input_filename);

    if (!aim) {
        std::cerr << "Error loading pcl file\n";
        std::cerr << '\t' << pi.getErrorString() << std::endl;
        return 1;
    }

    std::cout << "Loaded Pickled Alignment Incidence file " << input_filename << std::endl;
    std::vector<std::string> hap_names = aim->get_haplotype_names();

    std::cout << "File had the following haplotype names:\n";
    for (std::vector<std::string>::iterator it = hap_names.begin(); it != hap_names.end(); ++it) {
        std::cout << *it << "\t";
    }
    std::cout << std::endl;

    std::cout << aim->num_reads() << " reads loaded\n";
    std::cout << aim->num_transcripts() << " transcripts\n";

    std::cout << std::endl;

    if (!transcript_length_file.empty()) {
        std::cout << "Loading Transcript Length File " << transcript_length_file << std::endl;
        aim->loadTranscriptLengths(transcript_length_file);
    }

    if (model != SampleAllelicExpression::MODEL_4 && !aim->has_gene_mappings()) {
        std::cerr << "File does not contain transcript to gene mapping information.  Only normalization Model 4 can be used.\n";
        return 1;
    }

    t1 = clock();
    SampleAllelicExpression sae(aim, read_length);
    t2 = clock();
    diff = ((float)t2-(float)t1)/CLOCKS_PER_SEC;
    std::cout << "Time for initializing stack sum = " << diff << "s" << std::endl;

    if (max_iterations > 0) {
        num_iterations = 0;
        std::cout << "Beginning EM Iterations" << std::endl;
        t1 = clock();
        do {
            sae.update(model);
        } while (++num_iterations < max_iterations && !sae.converged());
        t2 = clock();

        diff = ((float)t2-(float)t1)/CLOCKS_PER_SEC;
        std::cout << "Time for " << num_iterations << " iterations = " << diff << "s\n";
        std::cout << "Time per iteration " << diff/num_iterations << "s\n";
    }

    std::cout << "Saving results to " << output_filename << std::endl;
    sae.saveStackSums(output_filename);
    std::cout << "Done.\n";

    return 0;
}

void print_help()
{
    std::cout << "EMASE Help\n\n"
              << "USAGE: emase [options] <alignment_incidence>\n\n"
              << "INPUT: Alignment Incidence file prepared with bam_to_pcl.py script\n\n"
              << "OPTIONS\n"
              << "  --model (-m) : Specify normalization model (can be 1-4, default=2)\n"
              << "  --output (-o) : Specify filename for output file (default is input_basename.stacksum.tsv)\n"
              << "  --transcript-lengths (-l) : Filename for transcript length file. Format is \"transcript_id\tlength\"\n"
              << "  --read-length (-k) : Specify read length for use when applying transcript length adjustment.\n"
              << "                       Ignored unless combined with --transcript-lengths. (Default 100)\n"
              << "  --max-iterations (-i) : Specify the maximum number of EM iterations. (Default 200)\n"
              << "  --help (-h) : Print this message and exit\n\n";
}
<commit_msg>do some checking of args, avoid segfaulting if user does not pass filename!<commit_after>/*
 * Copyright (c) 2015 The Jackson Laboratory
 *
 * This software was developed by Gary Churchill's Lab at The Jackson
 * Laboratory (see http://research.jax.org/faculty/churchill).
 *
 * This 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 software 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 software. If not, see <http://www.gnu.org/licenses/>.
 */

//
//  populase.cpp
//
//
//  Created by Glen Beane on 8/20/14.
//
//

#include <iostream>

#include <getopt.h>

#include "alignment_incidence_matrix.h"
#include "sample_allelic_expression.h"
#include "python_interface.h"

void print_help();

int main(int argc, char **argv)
{
    clock_t t1, t2;
    float diff;

    int num_iterations;
    int max_iterations = 200;
    int read_length = 100;

    SampleAllelicExpression::model model = SampleAllelicExpression::MODEL_2;
    int m;

    std::string input_filename;
    std::string output_filename;
    std::string transcript_length_file;
    std::string extension = ".pcl.bz2";

    //getopt related variables
    int c;
    int option_index = 0;
    int bad_args = 0;

    static struct option long_options[] = {
        {"help", no_argument, 0, 'h'},
        {"model", required_argument, 0, 'm'},
        {"output", required_argument, 0, 'o'},
        {"read-length", required_argument, 0, 'k'},
        {"transcript-lengths", required_argument, 0, 'l'},
        {"max-iterations", required_argument, 0, 'i'},
        {0, 0, 0, 0}
    };

    while ((c = getopt_long(argc, argv, "hm:o:k:l:i:", long_options, &option_index)) != -1) {
        switch (c) {
            case 'h':
                print_help();
                return 0;

            case 'm':
                m = std::stoi(optarg);
                if (m < 1 || m > 4) {
                    std::cerr << "Invalid model number specified. Valid options: 1, 2, 3, 4\n";
                }

                switch (m) {
                    case 1:
                        model = SampleAllelicExpression::MODEL_1;
                        break;
                    case 2:
                        model = SampleAllelicExpression::MODEL_2;
                        break;
                    case 3:
                        std::cerr << "Model 3 is currently unimplemented, please specify a different model\n";
                        return 1;
                    case 4:
                        model = SampleAllelicExpression::MODEL_4;
                        break;
                }

                break;

            case 'o':
                output_filename = std::string(optarg);
                break;

            case 'k':
                read_length = std::stoi(optarg);
                break;

            case 'l':
                transcript_length_file = std::string(optarg);
                break;

            case 'i':
                max_iterations = std::stoi(optarg);
                break;

            case '?':
                bad_args++;

        }
    }

    if (bad_args) {
        print_help();
        return 1;
    }

    if (argc - optind == 1) {
        input_filename = argv[optind];
    }
    else {
        // TODO print error message.  no
        print_help();
        return 1;
    }

    if (extension.size() >= input_filename.size() || !std::equal(extension.rbegin(), extension.rend(), input_filename.rbegin())) {
        std::cerr << "Error, expected file with .pcl.bz2 extension. Input file should be prepared with bam_to_pcl.py script.\n";
        return 1;
    }

    if (output_filename.empty()) {
        //use default, based on the input file name but placed in current working directdory
        output_filename = input_filename.substr(0, input_filename.size() - extension.size()).append(".stacksum.tsv");

        //check to see if there was a path in the input file name.  If so, trim it off
        std::size_t found = output_filename.rfind('/');
        if (found != std::string::npos) {
            output_filename = output_filename.substr(found+1);
        }

    }

    PythonInterface pi = PythonInterface();
    if (pi.init()){
        std::cerr << "Error importing TranscriptHits Python module.\n";
        std::cerr << '\t' << pi.getErrorString() << std::endl;
        return 1;
    }

    std::cout << "Loading " << input_filename << ". This may take a while..." << std::endl;
    AlignmentIncidenceMatrix *aim = pi.load(input_filename);

    if (!aim) {
        std::cerr << "Error loading pcl file\n";
        std::cerr << '\t' << pi.getErrorString() << std::endl;
        return 1;
    }

    std::cout << "Loaded Pickled Alignment Incidence file " << input_filename << std::endl;
    std::vector<std::string> hap_names = aim->get_haplotype_names();

    std::cout << "File had the following haplotype names:\n";
    for (std::vector<std::string>::iterator it = hap_names.begin(); it != hap_names.end(); ++it) {
        std::cout << *it << "\t";
    }
    std::cout << std::endl;

    std::cout << aim->num_reads() << " reads loaded\n";
    std::cout << aim->num_transcripts() << " transcripts\n";

    std::cout << std::endl;

    if (!transcript_length_file.empty()) {
        std::cout << "Loading Transcript Length File " << transcript_length_file << std::endl;
        aim->loadTranscriptLengths(transcript_length_file);
    }

    if (model != SampleAllelicExpression::MODEL_4 && !aim->has_gene_mappings()) {
        std::cerr << "File does not contain transcript to gene mapping information.  Only normalization Model 4 can be used.\n";
        return 1;
    }

    t1 = clock();
    SampleAllelicExpression sae(aim, read_length);
    t2 = clock();
    diff = ((float)t2-(float)t1)/CLOCKS_PER_SEC;
    std::cout << "Time for initializing stack sum = " << diff << "s" << std::endl;

    if (max_iterations > 0) {
        num_iterations = 0;
        std::cout << "Beginning EM Iterations" << std::endl;
        t1 = clock();
        do {
            sae.update(model);
        } while (++num_iterations < max_iterations && !sae.converged());
        t2 = clock();

        diff = ((float)t2-(float)t1)/CLOCKS_PER_SEC;
        std::cout << "Time for " << num_iterations << " iterations = " << diff << "s\n";
        std::cout << "Time per iteration " << diff/num_iterations << "s\n";
    }

    std::cout << "Saving results to " << output_filename << std::endl;
    sae.saveStackSums(output_filename);
    std::cout << "Done.\n";

    return 0;
}

void print_help()
{
    std::cout << std::endl << std::endl
              << "EMASE Help\n"
              << "----------\n\n"
              << "USAGE: emase [options] <alignment_incidence>\n\n"
              << "INPUT: Alignment Incidence file prepared with bam_to_pcl.py script\n\n"
              << "OPTIONS\n"
              << "  --model (-m) : Specify normalization model (can be 1-4, default=2)\n"
              << "  --output (-o) : Specify filename for output file (default is input_basename.stacksum.tsv)\n"
              << "  --transcript-lengths (-l) : Filename for transcript length file. Format is \"transcript_id\tlength\"\n"
              << "  --read-length (-k) : Specify read length for use when applying transcript length adjustment.\n"
              << "                       Ignored unless combined with --transcript-lengths. (Default 100)\n"
              << "  --max-iterations (-i) : Specify the maximum number of EM iterations. (Default 200)\n"
              << "  --help (-h) : Print this message and exit\n\n";
}
<|endoftext|>


<commit_before>/// @file   moveAction.cpp
/// @brief  Implementation of the class for a move action.
/// @author Enrico Fraccaroli
/// @date   Jul 14 2016
/// @copyright
/// Copyright (c) 2016 Enrico Fraccaroli <enrico.fraccaroli@gmail.com>
/// Permission is hereby granted, free of charge, to any person obtaining a
/// copy of this software and associated documentation files (the "Software"),
/// to deal in the Software without restriction, including without limitation
/// the rights to use, copy, modify, merge, publish, distribute, sublicense,
/// and/or sell copies of the Software, and to permit persons to whom the
/// Software is furnished to do so, subject to the following conditions:
///     The above copyright notice and this permission notice shall be included
///     in all copies or substantial portions of the Software.
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
/// DEALINGS IN THE SOFTWARE.

#include "moveAction.hpp"

#include "effectFactory.hpp"
#include "character.hpp"
#include "logger.hpp"
#include "room.hpp"

using namespace std::chrono;

MoveAction::MoveAction(Character * _actor, Room * _destination, Direction _direction) :
    GeneralAction(_actor),
    destination(_destination),
    direction(_direction)
{
    // Debugging message.
    Logger::log(LogLevel::Debug, "Created MoveAction.");
    // Reset the cooldown of the action.
    this->resetCooldown(MoveAction::getCooldown(_actor));
}

MoveAction::~MoveAction()
{
    Logger::log(LogLevel::Debug, "Deleted move action.");
}

bool MoveAction::check(std::string & error) const
{
    if (!GeneralAction::check(error))
    {
        return false;
    }
    if (this->destination == nullptr)
    {
        Logger::log(LogLevel::Error, "No destination has been set.");
        error = "You cannot reach the destination.";
        return false;
    }
    if (this->direction == Direction::None)
    {
        Logger::log(LogLevel::Error, "No direction has been set.");
        error = "You have lost your direction.";
        return false;
    }
    // Calculate the time needed to move.
    if ((actor->posture != CharacterPosture::Stand) &&
        (actor->posture != CharacterPosture::Crouch) &&
        (actor->posture != CharacterPosture::Prone))
    {
        error = "You first need to stand up.";
        return false;
    }
    return true;
}

ActionType MoveAction::getType() const
{
    return ActionType::Move;
}

std::string MoveAction::getDescription() const
{
    return "moving";
}

std::string MoveAction::stop()
{
    return "You stop moving.";
}

ActionStatus MoveAction::perform()
{
    // Check if the cooldown is ended.
    if (!this->checkElapsed())
    {
        return ActionStatus::Running;
    }
    std::string error;
    if (!this->check(error))
    {
        actor->sendMsg(error + "\n\n");
        return ActionStatus::Error;
    }
    if (!MoveAction::canMoveTo(actor, direction, error, false))
    {
        // Notify that the actor can't move because too tired.
        actor->sendMsg(error + "\n");
        return ActionStatus::Error;
    }
    // Get the amount of required stamina.
    auto consumedStamina = this->getConsumedStamina(actor, actor->posture);
    // Consume the stamina.
    actor->remStamina(consumedStamina, true);
    // Check if the actor was aiming.
    if (actor->combatHandler.getAimedTarget() != nullptr)
    {
        actor->effects.forceAddEffect(EffectFactory::disturbedAim(actor, 1, -3));
    }
    // Move character.
    actor->moveTo(
        destination,
        actor->getNameCapital() + " goes " + direction.toString() + ".\n",
        actor->getNameCapital() + " arrives from " + direction.getOpposite().toString() + ".\n");
    return ActionStatus::Finished;
}

unsigned int MoveAction::getConsumedStamina(const Character * character, const CharacterPosture & posture)
{
    auto multiplier = 1.0;
    if (posture == CharacterPosture::Crouch)
    {
        multiplier = 0.75;
    }
    else if (posture == CharacterPosture::Prone)
    {
        multiplier = 0.50;
    }
    // BASE     [+1.0]
    // STRENGTH [-0.0 to -1.40]
    // WEIGHT   [+1.6 to +2.51]
    // CARRIED  [+0.0 to +2.48]
    unsigned int consumedStamina = 1;
    consumedStamina -= character->getAbilityLog(Ability::Strength, 0.0, 1.0);
    consumedStamina = SafeSum(consumedStamina, SafeLog10(character->weight));
    consumedStamina = SafeSum(consumedStamina, SafeLog10(character->getCarryingWeight()));
    return static_cast<unsigned int>(consumedStamina * multiplier);
}

unsigned int MoveAction::getCooldown(const Character * character)
{
    unsigned int cooldown = 2;
    if (character->posture == CharacterPosture::Crouch)
    {
        cooldown = 4;
    }
    else if (character->posture == CharacterPosture::Prone)
    {
        cooldown = 6;
    }
    return cooldown;
}

bool MoveAction::canMoveTo(Character * character, const Direction & direction, std::string & error, bool allowInCombat)
{
    if ((character->getAction()->getType() == ActionType::Combat) && !allowInCombat)
    {
        // Check if the character is locked into close combat.
        bool lockedInCombat = false;
        // Check if he is in the same room of one of its aggressors.
        for (auto iterator : character->combatHandler)
        {
            if (iterator->aggressor != nullptr)
            {
                if (iterator->aggressor->room == character->room)
                {
                    lockedInCombat = true;
                    break;
                }
            }
        }
        // Check even the aimed character.
        if (character->combatHandler.getAimedTarget() != nullptr)
        {
            if (character->combatHandler.getAimedTarget()->room == character->room)
            {
                lockedInCombat = true;
            }
        }
        if (lockedInCombat)
        {
            error = "You cannot move while fighting in close combat.";
        }
        return !lockedInCombat;
    }
    // Check if the character is in a no-walk position.
    if ((character->posture != CharacterPosture::Stand) &&
        (character->posture != CharacterPosture::Crouch) &&
        (character->posture != CharacterPosture::Prone))
    {
        error = "You first need to stand up.";
        return false;
    }
    // Find the exit to the destination.
    auto destExit = character->room->findExit(direction);
    if (destExit == nullptr)
    {
        error = "You cannot go that way.";
        return false;
    }
    // Check if the actor has enough stamina to execute the action.
    if (MoveAction::getConsumedStamina(character, character->posture) > character->getStamina())
    {
        error = "You are too tired to move.\n";
        return false;
    }
    // If the direction is upstairs, check if there is a stair.
    if (direction == Direction::Up)
    {
        if (!HasFlag(destExit->flags, ExitFlag::Stairs))
        {
            error = "You can't go upstairs, there are no stairs.";
            return false;
        }
    }
    // Check if the destination is correct.
    if (destExit->destination == nullptr)
    {
        error = "That direction can't take you anywhere.";
        return false;
    }
    // Check if the destination is bocked by a door.
    auto door = destExit->destination->findDoor();
    if (door != nullptr)
    {
        if (HasFlag(door->flags, ItemFlag::Closed))
        {
            error = "Maybe you have to open that door first.";
            return false;
        }
    }
    // Check if the destination has a floor.
    std::shared_ptr<Exit> destDown = destExit->destination->findExit(Direction::Down);
    if (destDown != nullptr)
    {
        if (!HasFlag(destDown->flags, ExitFlag::Stairs))
        {
            error = "Do you really want to fall in that pit?";
            return false;
        }
    }
    // Check if the destination is forbidden for mobiles.
    return !(character->isMobile() && HasFlag(destExit->flags, ExitFlag::NoMob));
}
<commit_msg>Remove 'using namespace' where not needed<commit_after>/// @file   moveAction.cpp
/// @brief  Implementation of the class for a move action.
/// @author Enrico Fraccaroli
/// @date   Jul 14 2016
/// @copyright
/// Copyright (c) 2016 Enrico Fraccaroli <enrico.fraccaroli@gmail.com>
/// Permission is hereby granted, free of charge, to any person obtaining a
/// copy of this software and associated documentation files (the "Software"),
/// to deal in the Software without restriction, including without limitation
/// the rights to use, copy, modify, merge, publish, distribute, sublicense,
/// and/or sell copies of the Software, and to permit persons to whom the
/// Software is furnished to do so, subject to the following conditions:
///     The above copyright notice and this permission notice shall be included
///     in all copies or substantial portions of the Software.
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
/// DEALINGS IN THE SOFTWARE.

#include "moveAction.hpp"

#include "effectFactory.hpp"
#include "character.hpp"
#include "logger.hpp"
#include "room.hpp"

MoveAction::MoveAction(Character * _actor, Room * _destination, Direction _direction) :
    GeneralAction(_actor),
    destination(_destination),
    direction(_direction)
{
    // Debugging message.
    Logger::log(LogLevel::Debug, "Created MoveAction.");
    // Reset the cooldown of the action.
    this->resetCooldown(MoveAction::getCooldown(_actor));
}

MoveAction::~MoveAction()
{
    Logger::log(LogLevel::Debug, "Deleted move action.");
}

bool MoveAction::check(std::string & error) const
{
    if (!GeneralAction::check(error))
    {
        return false;
    }
    if (this->destination == nullptr)
    {
        Logger::log(LogLevel::Error, "No destination has been set.");
        error = "You cannot reach the destination.";
        return false;
    }
    if (this->direction == Direction::None)
    {
        Logger::log(LogLevel::Error, "No direction has been set.");
        error = "You have lost your direction.";
        return false;
    }
    // Calculate the time needed to move.
    if ((actor->posture != CharacterPosture::Stand) &&
        (actor->posture != CharacterPosture::Crouch) &&
        (actor->posture != CharacterPosture::Prone))
    {
        error = "You first need to stand up.";
        return false;
    }
    return true;
}

ActionType MoveAction::getType() const
{
    return ActionType::Move;
}

std::string MoveAction::getDescription() const
{
    return "moving";
}

std::string MoveAction::stop()
{
    return "You stop moving.";
}

ActionStatus MoveAction::perform()
{
    // Check if the cooldown is ended.
    if (!this->checkElapsed())
    {
        return ActionStatus::Running;
    }
    std::string error;
    if (!this->check(error))
    {
        actor->sendMsg(error + "\n\n");
        return ActionStatus::Error;
    }
    if (!MoveAction::canMoveTo(actor, direction, error, false))
    {
        // Notify that the actor can't move because too tired.
        actor->sendMsg(error + "\n");
        return ActionStatus::Error;
    }
    // Get the amount of required stamina.
    auto consumedStamina = this->getConsumedStamina(actor, actor->posture);
    // Consume the stamina.
    actor->remStamina(consumedStamina, true);
    // Check if the actor was aiming.
    if (actor->combatHandler.getAimedTarget() != nullptr)
    {
        actor->effects.forceAddEffect(EffectFactory::disturbedAim(actor, 1, -3));
    }
    // Move character.
    actor->moveTo(
        destination,
        actor->getNameCapital() + " goes " + direction.toString() + ".\n",
        actor->getNameCapital() + " arrives from " + direction.getOpposite().toString() + ".\n");
    return ActionStatus::Finished;
}

unsigned int MoveAction::getConsumedStamina(const Character * character, const CharacterPosture & posture)
{
    auto multiplier = 1.0;
    if (posture == CharacterPosture::Crouch)
    {
        multiplier = 0.75;
    }
    else if (posture == CharacterPosture::Prone)
    {
        multiplier = 0.50;
    }
    // BASE     [+1.0]
    // STRENGTH [-0.0 to -1.40]
    // WEIGHT   [+1.6 to +2.51]
    // CARRIED  [+0.0 to +2.48]
    unsigned int consumedStamina = 1;
    consumedStamina -= character->getAbilityLog(Ability::Strength, 0.0, 1.0);
    consumedStamina = SafeSum(consumedStamina, SafeLog10(character->weight));
    consumedStamina = SafeSum(consumedStamina, SafeLog10(character->getCarryingWeight()));
    return static_cast<unsigned int>(consumedStamina * multiplier);
}

unsigned int MoveAction::getCooldown(const Character * character)
{
    unsigned int cooldown = 2;
    if (character->posture == CharacterPosture::Crouch)
    {
        cooldown = 4;
    }
    else if (character->posture == CharacterPosture::Prone)
    {
        cooldown = 6;
    }
    return cooldown;
}

bool MoveAction::canMoveTo(Character * character, const Direction & direction, std::string & error, bool allowInCombat)
{
    if ((character->getAction()->getType() == ActionType::Combat) && !allowInCombat)
    {
        // Check if the character is locked into close combat.
        bool lockedInCombat = false;
        // Check if he is in the same room of one of its aggressors.
        for (auto iterator : character->combatHandler)
        {
            if (iterator->aggressor != nullptr)
            {
                if (iterator->aggressor->room == character->room)
                {
                    lockedInCombat = true;
                    break;
                }
            }
        }
        // Check even the aimed character.
        if (character->combatHandler.getAimedTarget() != nullptr)
        {
            if (character->combatHandler.getAimedTarget()->room == character->room)
            {
                lockedInCombat = true;
            }
        }
        if (lockedInCombat)
        {
            error = "You cannot move while fighting in close combat.";
        }
        return !lockedInCombat;
    }
    // Check if the character is in a no-walk position.
    if ((character->posture != CharacterPosture::Stand) &&
        (character->posture != CharacterPosture::Crouch) &&
        (character->posture != CharacterPosture::Prone))
    {
        error = "You first need to stand up.";
        return false;
    }
    // Find the exit to the destination.
    auto destExit = character->room->findExit(direction);
    if (destExit == nullptr)
    {
        error = "You cannot go that way.";
        return false;
    }
    // Check if the actor has enough stamina to execute the action.
    if (MoveAction::getConsumedStamina(character, character->posture) > character->getStamina())
    {
        error = "You are too tired to move.\n";
        return false;
    }
    // If the direction is upstairs, check if there is a stair.
    if (direction == Direction::Up)
    {
        if (!HasFlag(destExit->flags, ExitFlag::Stairs))
        {
            error = "You can't go upstairs, there are no stairs.";
            return false;
        }
    }
    // Check if the destination is correct.
    if (destExit->destination == nullptr)
    {
        error = "That direction can't take you anywhere.";
        return false;
    }
    // Check if the destination is bocked by a door.
    auto door = destExit->destination->findDoor();
    if (door != nullptr)
    {
        if (HasFlag(door->flags, ItemFlag::Closed))
        {
            error = "Maybe you have to open that door first.";
            return false;
        }
    }
    // Check if the destination has a floor.
    std::shared_ptr<Exit> destDown = destExit->destination->findExit(Direction::Down);
    if (destDown != nullptr)
    {
        if (!HasFlag(destDown->flags, ExitFlag::Stairs))
        {
            error = "Do you really want to fall in that pit?";
            return false;
        }
    }
    // Check if the destination is forbidden for mobiles.
    return !(character->isMobile() && HasFlag(destExit->flags, ExitFlag::NoMob));
}
<|endoftext|>


<commit_before>#include <netdb.h>
#include <unistd.h>

#include "client.h"
#include "duckchat.h"

// Variables
struct sockaddr_in client_addr;
struct sockaddr_in server_addr;
int client_socket;
struct addrinfo *server_info;
char *channel;

// Prints an error message and exits the program.
void Error(const char *msg) {
  std::cerr << msg << std::endl;
  exit(1);
}

// Connects to the server at a the given port.
void Connect(char *domain, const char *port) {
  std::cout << "Connecting to " << domain << std::endl;

  struct addrinfo hints;
  struct addrinfo *server_info_tmp;
  int status;

  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_DGRAM;
  hints.ai_protocol = 0;

  if ((status = getaddrinfo(domain, port, &hints, &server_info_tmp)) != 0) {
    std::cerr << "client: unable to resolve address: " << gai_strerror(status) << std::endl;
    exit(1);
  }

  // getaddrinfo() returns a list of address structures into server_info_tmp.
  // Try each address until we successfully connect().
  // If socket() (or connect()) fails, close the socket and try the next address.

  for (server_info = server_info_tmp; server_info != NULL; server_info = server_info->ai_next) {
    if ((client_socket = socket(server_info->ai_family, server_info->ai_socktype, server_info->ai_protocol)) < 0) {
      continue;
    }
    if (connect(client_socket, server_info->ai_addr, server_info->ai_addrlen) != -1) {
      break; // Success
    }
    close(client_socket);
  }

  if (server_info == NULL) {
    Error("client: all sockets failed to connect");
  }
}

// Sends a message to all users in on the active channel.
int RequestSay(const char *message) {
  struct request_say say;
  memset(&say, 0, sizeof(say));
  say.req_type = REQ_SAY;
  strncpy(say.req_text, message, SAY_MAX);
  strncpy(say.req_channel, channel, CHANNEL_MAX);

  if (sendto(client_socket, &say, sizeof(say), 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to send message\n");
  }

  return 0;
}

// Sends login requests to the server.
int RequestLogin(char *username) {
  struct request_login login;
  memset(&login, 0, sizeof(login));
  login.req_type = REQ_LOGIN;
  strncpy(login.req_username, username, USERNAME_MAX);

  size_t message_size = sizeof(struct request_login);

  if (sendto(client_socket, &login, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request login\n");
  }

  return 0;
}

// Sends logout requests to the server.
int RequestLogout() {
  struct request_logout logout;
  memset((char *) &logout, 0, sizeof(logout));
  logout.req_type = REQ_LOGOUT;

  size_t message_size = sizeof(struct request_logout);

  if (sendto(client_socket, &logout, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request logout\n");
  }

  return 0;
}

// Sends join requests to the server.
int RequestJoin(char *channel) {
  struct request_join join;
  memset((char *) &join, 0, sizeof(join));
  join.req_type = REQ_JOIN;
  strncpy(join.req_channel, channel, CHANNEL_MAX);

  size_t message_size = sizeof(struct request_join);

  if (sendto(client_socket, &join, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request join\n");
  }

  return 0;
}

// Splits strings around spaces.
std::vector<std::string> StringSplit(std::string input) {
  std::istringstream iss(input);
  std::vector<std::string> result{std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>{}};

  return result;
}

// Splits strings around spaces.
std::vector<std::string> SplitString(std::string input, char delimiter) {
  std::vector<std::string> result;
  std::string word = "";

  for (char c : input) {
    if (c != delimiter) {
      word += c;
    } else {
      result.push_back(word);
      word = "";
    }
  }
  result.push_back(word);

  return result;
}

// Processes the input string to decide what type of command it is.
bool ProcessInput(std::string input) {
  std::vector<std::string> inputs = StringSplit(input);
  bool result = true;

  if (inputs[0] == "/exit") {
    RequestLogout();
    result = false;
  } else if (inputs[0] == "/list") {

  } else if (inputs[0] == "/join") {

  } else if (inputs[0] == "/leave") {

  } else if (inputs[0] == "/who") {

  } else if (inputs[0] == "/switch") {

  } else {
    std::cout << "Invalid command" << std::endl;
  }

  return result;
}

int main(int argc, char *argv[]) {
  char *domain;
  char *port_str;
  int port_num;
  char *username;
  std::string input;

  struct timeval timeout;
  fd_set read_set;
  int file_desc = 0;
  int result;
  char receive_buffer[SAY_MAX];
  memset(&receive_buffer, 0, SAY_MAX);

  if (argc < 4) {
    Error("usage: client [server name] [port] [username]");
  }

  domain = argv[1];
  port_str = argv[2];
  port_num = atoi(argv[2]);
  username = argv[3];

  if (strlen(domain) > UNIX_PATH_MAX) {
    Error("client: server name must be less than 108 characters");
  }

  if (port_num < 0 || port_num > 65535) {
    Error("client: port number must be between 0 and 65535");
  }

  if (strlen(username) > USERNAME_MAX) {
    Error("client: username must be less than 32 characters");
  }

  Connect(domain, port_str);

  RequestLogin(username);

  channel = (char *) "Common";
  RequestJoin(channel);

  // TODO handle response from send

  while(1) {
    FD_ZERO(&read_set);
    FD_SET(file_desc, &read_set);

    timeout.tv_sec = 5; // TODO change time value?
    timeout.tv_usec = 0;

//    if ((result = select(file_desc + 1, &read_set, NULL, NULL, NULL)) < 0) {
//      continue;
//    }
    if ((result = select(file_desc + 1, &read_set, NULL, NULL, &timeout)) < 0) {
      Error("client: problem using select");
    }

//    size_t size = sizeof(receive_buffer);

    std::cout << "past select, result: " << result << std::endl;

    if (result > 0) {
      if (FD_ISSET(file_desc, &read_set)) {
        // Socket has data
        result = recv(file_desc, receive_buffer, SAY_MAX, 0);
      }

      if (result == 0) {
        close(file_desc);
      } else {
        // TODO capture user input, store, clean input, then print buffer, afterward replace input
        std::cout << "[" << channel << "]" << "[" << username << "]: " << receive_buffer << std::endl;
      }
    }

    std::cout << "past check result" << std::endl;

    std::cout << "> ";
    getline(std::cin, input);

    if (input[0] == '/') {
      if (!ProcessInput(input)) {
        break;
      }
    } else {
      // Send chat messages
      RequestSay(input.c_str());
      std::cout << "[" << channel << "]" << "[" << username << "]: " << input << std::endl;
    }

//    if (FD_ISSET(STDIN_FILENO, &read_set)) {
//      std::cout << "> ";
//      getline(std::cin, input);
//
//      if (input[0] == '/') {
//        if (!ProcessInput(input)) {
//          break;
//        }
//      } else {
//        // Send chat messages
//        RequestSay(input.c_str());
//        std::cout << "[" << channel << "]" << "[" << username << "]: " << input << std::endl;
//      }
//    }

    std::cout << "past getline" << std::endl;

  }

  return 0;
}<commit_msg>test select again<commit_after>#include <netdb.h>
#include <unistd.h>

#include "client.h"
#include "duckchat.h"

// Variables
struct sockaddr_in client_addr;
struct sockaddr_in server_addr;
int client_socket;
struct addrinfo *server_info;
char *channel;

// Prints an error message and exits the program.
void Error(const char *msg) {
  std::cerr << msg << std::endl;
  exit(1);
}

// Connects to the server at a the given port.
void Connect(char *domain, const char *port) {
  std::cout << "Connecting to " << domain << std::endl;

  struct addrinfo hints;
  struct addrinfo *server_info_tmp;
  int status;

  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_DGRAM;
  hints.ai_protocol = 0;

  if ((status = getaddrinfo(domain, port, &hints, &server_info_tmp)) != 0) {
    std::cerr << "client: unable to resolve address: " << gai_strerror(status) << std::endl;
    exit(1);
  }

  // getaddrinfo() returns a list of address structures into server_info_tmp.
  // Try each address until we successfully connect().
  // If socket() (or connect()) fails, close the socket and try the next address.

  for (server_info = server_info_tmp; server_info != NULL; server_info = server_info->ai_next) {
    if ((client_socket = socket(server_info->ai_family, server_info->ai_socktype, server_info->ai_protocol)) < 0) {
      continue;
    }
    if (connect(client_socket, server_info->ai_addr, server_info->ai_addrlen) != -1) {
      break; // Success
    }
    close(client_socket);
  }

  if (server_info == NULL) {
    Error("client: all sockets failed to connect");
  }
}

// Sends a message to all users in on the active channel.
int RequestSay(const char *message) {
  struct request_say say;
  memset(&say, 0, sizeof(say));
  say.req_type = REQ_SAY;
  strncpy(say.req_text, message, SAY_MAX);
  strncpy(say.req_channel, channel, CHANNEL_MAX);

  if (sendto(client_socket, &say, sizeof(say), 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to send message\n");
  }

  return 0;
}

// Sends login requests to the server.
int RequestLogin(char *username) {
  struct request_login login;
  memset(&login, 0, sizeof(login));
  login.req_type = REQ_LOGIN;
  strncpy(login.req_username, username, USERNAME_MAX);

  size_t message_size = sizeof(struct request_login);

  if (sendto(client_socket, &login, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request login\n");
  }

  return 0;
}

// Sends logout requests to the server.
int RequestLogout() {
  struct request_logout logout;
  memset((char *) &logout, 0, sizeof(logout));
  logout.req_type = REQ_LOGOUT;

  size_t message_size = sizeof(struct request_logout);

  if (sendto(client_socket, &logout, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request logout\n");
  }

  return 0;
}

// Sends join requests to the server.
int RequestJoin(char *channel) {
  struct request_join join;
  memset((char *) &join, 0, sizeof(join));
  join.req_type = REQ_JOIN;
  strncpy(join.req_channel, channel, CHANNEL_MAX);

  size_t message_size = sizeof(struct request_join);

  if (sendto(client_socket, &join, message_size, 0, server_info->ai_addr, server_info->ai_addrlen) < 0) {
    Error("client: failed to request join\n");
  }

  return 0;
}

// Splits strings around spaces.
std::vector<std::string> StringSplit(std::string input) {
  std::istringstream iss(input);
  std::vector<std::string> result{std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>{}};

  return result;
}

// Splits strings around spaces.
std::vector<std::string> SplitString(std::string input, char delimiter) {
  std::vector<std::string> result;
  std::string word = "";

  for (char c : input) {
    if (c != delimiter) {
      word += c;
    } else {
      result.push_back(word);
      word = "";
    }
  }
  result.push_back(word);

  return result;
}

// Processes the input string to decide what type of command it is.
bool ProcessInput(std::string input) {
  std::vector<std::string> inputs = StringSplit(input);
  bool result = true;

  if (inputs[0] == "/exit") {
    RequestLogout();
    result = false;
  } else if (inputs[0] == "/list") {

  } else if (inputs[0] == "/join") {

  } else if (inputs[0] == "/leave") {

  } else if (inputs[0] == "/who") {

  } else if (inputs[0] == "/switch") {

  } else {
    std::cout << "Invalid command" << std::endl;
  }

  return result;
}

int main(int argc, char *argv[]) {
  char *domain;
  char *port_str;
  int port_num;
  char *username;
  std::string input;

  struct timeval timeout;
  fd_set read_set;
  int file_desc = 0;
  int result;
  char receive_buffer[SAY_MAX];
  memset(&receive_buffer, 0, SAY_MAX);

  if (argc < 4) {
    Error("usage: client [server name] [port] [username]");
  }

  domain = argv[1];
  port_str = argv[2];
  port_num = atoi(argv[2]);
  username = argv[3];

  if (strlen(domain) > UNIX_PATH_MAX) {
    Error("client: server name must be less than 108 characters");
  }

  if (port_num < 0 || port_num > 65535) {
    Error("client: port number must be between 0 and 65535");
  }

  if (strlen(username) > USERNAME_MAX) {
    Error("client: username must be less than 32 characters");
  }

  Connect(domain, port_str);

  RequestLogin(username);

  channel = (char *) "Common";
  RequestJoin(channel);

  // TODO handle response from send

  while(1) {
    FD_ZERO(&read_set);
    FD_SET(file_desc, &read_set);

    timeout.tv_sec = 5; // TODO change time value?
    timeout.tv_usec = 0;

//    if ((result = select(file_desc + 1, &read_set, NULL, NULL, NULL)) < 0) {
//      continue;
//    }
    if ((result = select(file_desc + 1, &read_set, NULL, NULL, &timeout)) < 0) {
      Error("client: problem using select");
    }

//    size_t size = sizeof(receive_buffer);

//    std::cout << "past select, result: " << result << std::endl;

    if (result > 0) {
      if (FD_ISSET(file_desc, &read_set)) {
        // Socket has data
        result = read(client_socket, receive_buffer, sizeof(receive_buffer));
      }

      if (result == 0) {
        close(file_desc);
      } else {
        // TODO capture user input, store, clean input, then print buffer, afterward replace input
        std::cout << "[" << channel << "]" << "[" << username << "]: " << receive_buffer << std::endl;
      }
    }

//    std::cout << "past check result" << std::endl;

    std::cout << "> ";
    getline(std::cin, input);

    if (input[0] == '/') {
      if (!ProcessInput(input)) {
        break;
      }
    } else {
      // Send chat messages
      RequestSay(input.c_str());
      std::cout << "[" << channel << "]" << "[" << username << "]: " << input << std::endl;
    }

//    if (FD_ISSET(STDIN_FILENO, &read_set)) {
//      std::cout << "> ";
//      getline(std::cin, input);
//
//      if (input[0] == '/') {
//        if (!ProcessInput(input)) {
//          break;
//        }
//      } else {
//        // Send chat messages
//        RequestSay(input.c_str());
//        std::cout << "[" << channel << "]" << "[" << username << "]: " << input << std::endl;
//      }
//    }

//    std::cout << "past getline" << std::endl;

  }

  return 0;
}<|endoftext|>