/****************************************************************************
**
** Copyright (C) 2002-2007 Frank Hemer <frank@hemer.org>,
**                         Tilo Riemer <riemer@crossvc.com>
**
**
**----------------------------------------------------------------------------
**
**----------------------------------------------------------------------------
**
** CrossVC is available under two different licenses:
**
** If CrossVC is linked against the GPLed version of Qt 
** CrossVC is released under the terms of GPL also.
**
** If CrossVC is linked against a nonGPLed version of Qt 
** CrossVC is released under the terms of the 
** CrossVC License for non-Unix platforms (CLNU)
**
**
** CrossVC License for non-Unix platforms (CLNU):
**
** Redistribution and use in binary form, without modification, 
** are permitted provided that the following conditions are met:
**
** 1. Redistributions in binary form must reproduce the above copyright
**    notice, this list of conditions and the following disclaimer in the
**    documentation and/or other materials provided with the distribution.
** 2. It is not permitted to distribute the binary package under a name
**    different than CrossVC.
** 3. The name of the authors may not be used to endorse or promote
**    products derived from this software without specific prior written
**    permission.
** 4. The source code is the creative property of the authors.
**    Extensions and development under the terms of the Gnu Public License
**    are limited to the Unix platform. Any distribution or compilation of 
**    the source code against libraries licensed other than gpl requires 
**    the written permission of the authors.
**
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
** GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
**
**
** CrossVC License for Unix platforms:
**
** 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, version 2 of the License.
** 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 version 2 for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software Foundation,
** Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**
*****************************************************************************/

#include "config.h"


#include <qapplication.h>
#include <qgroupbox.h>
#include <qlistbox.h>
#include <qpushbutton.h>
#include <qcombobox.h>
#include <qradiobutton.h>
#include <qcheckbox.h>
#include <qfiledialog.h>
#include <qregexp.h>
#include <qdatetimeedit.h>
#include <qtimer.h>
#include <qwhatsthis.h>
#include <assert.h>

#include "globals.h"
#include "CheckoutDialogImpl.h"
#include "getCvsMod.h"
#include "RevisionWidgetImpl.h"

CheckoutDialogImpl::CheckoutDialogImpl( QString dlgCaption,
      const QIconSet &whatsThisIconSet,
      QString* defaultProfile,
      QStringList* ProfileList,
      QStringList* UserList,
      QStringList* ServerList,
      QStringList* RepositoryList,
      QStringList* WorkdirList,
      QStringList* ModuleList,
      QStringList* CheckoutAsList,
      QWidget* parent,
      const char* name,
      bool modal,
      WFlags fl )
   : CheckoutDialog( modal ? parent : (LookAndFeel::g_b0AsParent ? 0 : parent),
	 name, modal, fl ),
	  m_parent(parent),
	  m_defBrowseModButtonName(BrowseModulesButton->name()),
	  m_defBrowseModButtonText(BrowseModulesButton->text()),
	  m_ProfileList(*ProfileList),
	  m_UserList(*UserList),
	  m_ServerList(*ServerList),
	  m_RepositoryList(*RepositoryList),
	  m_WorkdirList(*WorkdirList),
	  m_ModuleList(*ModuleList),
	  m_CheckoutAsList(*CheckoutAsList),
	  m_running(false)
{
   setCaption(dlgCaption);
   m_defaultProfile = defaultProfile;

   m_pWhatsThis->setIconSet(whatsThisIconSet);
#ifdef Q_WS_MAC
  m_pWhatsThis->setMaximumWidth(m_pWhatsThis->height() * 2);
#else
  m_pWhatsThis->setMaximumWidth(m_pWhatsThis->height());
#endif
  
   //for now this emulates the enum used in ProfilesDialogImpl
   //this should be replaced by a common usage
   m_AccessDict.insert(tr("Password"),&PSERVER);
   m_AccessDict.insert(tr("Remote Shell"),&RSH);
   m_AccessDict.insert(tr("Local/NFS"),&LOCAL);
   m_AccessDict.insert(tr("SSPI"),&SSPI);
   m_AccessList.append(tr("Password"));
   m_AccessList.append(tr("Remote Shell"));
   m_AccessList.append(tr("Local/NFS"));
   m_AccessList.append(tr("SSPI"));

   m_ProfileList.sort();
   m_ProfileList.prepend(tr("No profile selected"));
   m_ProfileBox->insertStringList(m_ProfileList);
   m_UserList.sort();
   m_UserBox->insertStringList(m_UserList);
   m_ServerList.sort();
   m_ServerBox->insertStringList(m_ServerList);
   m_RepositoryList.sort();
   m_RepositoryBox->insertStringList(m_RepositoryList);
   m_AccessBox->insertStringList(m_AccessList);

   insertSortAndSelectFirst(m_BrowseModules,m_ModuleList);
   simplifyStringList(&m_CheckoutAsList);//need only one!
   m_CheckoutAsList.prepend("");//need an empty top entry
   insertSortAndSelectFirst(m_CheckoutAs,m_CheckoutAsList);
   m_checkoutAsDOption = "";
   insertSortAndSelectFirst(m_ImportDir,m_WorkdirList);

   //read environment(CVSROOT) and set default values
   QString connectMethod;
   QString userName;
   QString passwd;
   QString host;
   int port;
   QString rootDir;
   if (analyzeCVSROOT(connectMethod, userName, passwd, host, port, rootDir)) {
      if (!host.isNull() && connectMethod == "pserver") {
         host += ":" + QString::number(port);
      }
      m_UserBox->setCurrentText(userName);
      m_ServerBox->setCurrentText(host);
      m_RepositoryBox->setCurrentText(rootDir);
      if (connectMethod.startsWith("local") || connectMethod.startsWith("fork")) {
         m_AccessBox->setCurrentItem(2);
      } else if (connectMethod.startsWith("pserver")) {
         m_AccessBox->setCurrentItem(0);
      } else if (connectMethod.startsWith("sspi")) {
         m_AccessBox->setCurrentItem(3);
      } else {
         m_AccessBox->setCurrentItem(2);
      }
   } else {
      m_UserBox->setCurrentText("");
      m_ServerBox->setCurrentText("");
      m_RepositoryBox->setCurrentText("");
      m_AccessBox->setCurrentItem(2);
   }

   m_SshPresetList.append(tr("no ssh"));
   m_SshPresetList.append(tr("use ssh"));
   m_SshPresetList.append(tr("use own ssh-agent"));
   m_SshPresetList.append(tr("use running ssh-agent"));
   m_SshPreset->insertStringList(m_SshPresetList);

   m_ExternalRsh->setChecked(true);
   m_Rsh->setText(ExtApps::g_cvsRsh.path);
   accessActivated(m_AccessBox->currentText());

   m_TagList.clear();
   revisionWidgetImpl->init();
   revisionWidgetImpl->hideStickyTagFrame();
   revisionWidgetImpl->hideRevisionFrame();

   m_Readonly->setChecked(!bRWPermission);

   if (!m_defaultProfile->isEmpty()) {
      setDefaultProfile(*m_defaultProfile);
   }

   resetSize();
   connect(this,SIGNAL(showWarning(const QString&,const QString&)),parent,SLOT(showWarning(const QString&,const QString&)));
}

/*  
 *  Destroys the object and frees any allocated resources
 */
CheckoutDialogImpl::~CheckoutDialogImpl()
{
   // no need to delete child widgets, Qt does it all for us
}

int CheckoutDialogImpl::findlistIndex(QStringList *list, const QString item)
{
   int idx;
   idx = 0;
  
   for ( QStringList::Iterator it = list->begin(); it != list->end(); ++it )
   {
      if ( (*it).stripWhiteSpace()==item.stripWhiteSpace() )
      {
         return idx;
      }
      idx++;  
   }
   return 0;
}

void CheckoutDialogImpl::insertSortAndSelectFirst(QComboBox * box,QStringList & list) {
   if (list.isEmpty()) return;
   QString first = list.first();
   list.sort();
   box->insertStringList(list);
   box->setCurrentItem(findlistIndex(&list,first));
}

void CheckoutDialogImpl::setDefaultProfile(const QString& s) {

   /*! search item in combobox profile */
   m_ProfileBox->setCurrentItem( findlistIndex(&m_ProfileList, s) );

   /*! set the profile */
   profileActivated(s);
}

void CheckoutDialogImpl::profileActivated(const QString& s) {

   /*! search index in qlist */
   QValueList<CLincvsProfile>::const_iterator it;
   for (it = cvsProfileContentList.begin(); it != cvsProfileContentList.end(); it++) {
      if ((*it).name().stripWhiteSpace() == s.stripWhiteSpace() ) {
         /*! search item in combobox user */
         m_UserBox->setCurrentItem( findlistIndex(&m_UserList, (*it).user()) );
	
         /*! search item in combobox server */
         m_ServerBox->setCurrentItem( findlistIndex(&m_ServerList, (*it).server()) );
	
         /*! search item in combobox repository */
         m_RepositoryBox->setCurrentItem( findlistIndex(&m_RepositoryList, (*it).repository()) );
	
         /*! set item in combobox method */
         m_AccessBox->setCurrentItem( (*it).method());
         accessActivated( m_AccessBox->currentText());

         m_SshPreset->setCurrentItem( (*it).sshClientPreset());
         
         /*! set default profile */
         *m_defaultProfile = s.stripWhiteSpace();
	
         //and go away
         return;
      }
   }
   *m_defaultProfile = "";
   m_UserBox->setCurrentText("");
   m_ServerBox->setCurrentText("");
   m_RepositoryBox->setCurrentText("");
   m_AccessBox->setCurrentItem(0);
   m_SshPreset->setCurrentItem(0);
   accessActivated( m_AccessBox->currentText());
}

void CheckoutDialogImpl::extRshClicked() {
   m_InternalRsh->setChecked(false);
   m_Rsh->setEnabled(true);
   m_SshPreset->setEnabled(true);
}

void CheckoutDialogImpl::intRshClicked() {
   m_ExternalRsh->setChecked(false);
   m_Rsh->setEnabled(false);
   m_SshPreset->setEnabled(false);
}

void CheckoutDialogImpl::accessActivated(const QString& s) {
   if ((*m_AccessDict.find(s))==RSH) {
      RshFrame->show();
   } else {
      RshFrame->hide();
   }

   if ((*m_AccessDict.find(s))==LOCAL) {
      m_UserBox->setEnabled(false);
      m_ServerBox->setEnabled(false);
   } else {
      m_UserBox->setEnabled(true);
      m_ServerBox->setEnabled(true);
   }

   resetSize();
}

void CheckoutDialogImpl::browseDirClicked() {
   QString fn = QFileDialog::getExistingDirectory (m_ImportDir->currentText(), this, NULL,
	 tr("Choose target directory"),
	 true);

   if (!fn.isEmpty ()) {
      m_ImportDir->setCurrentText(fn);
   }
}

void CheckoutDialogImpl::browseModulesClicked() {

   if (m_running) {
    
      QTimer::singleShot(0,m_parent,SLOT(stopCurAction()));
      BrowseModulesButton->setName(m_defBrowseModButtonName);
      BrowseModulesButton->setText(m_defBrowseModButtonText);

   } else {

      QString connectMethod;
      QString userName;
      QString passwd;
      QString host;
      int port;
      QString rootDir;
      
      analyzeCVSROOT(connectMethod, userName, passwd, host, port, rootDir);
      if ( !m_RepositoryBox->currentText().isEmpty()) {
         rootDir = m_RepositoryBox->currentText();
      }
      
      if ( (*m_AccessDict.find(m_AccessBox->currentText()))==LOCAL) {
         getCvsModule *mBrowser = new getCvsModule(((QString)rootDir)+"/", this);
         connect (mBrowser, SIGNAL (cvsModuleSelected( const QString & )), 
         SLOT (insertModule(const QString &)));
         
         mBrowser->resize( 500, 400 );
         mBrowser->setCaption(tr("CVS Module Browser"));
         mBrowser->exec();
      } else {
         QString connMethod;
         if( (*m_AccessDict.find(m_AccessBox->currentText()))==PSERVER) {
            connMethod = "pserver";
         } else if((*m_AccessDict.find(m_AccessBox->currentText()))==RSH) {
            if(m_ExternalRsh->isChecked()) {
               connMethod = "ext";
            } else {
               connMethod = "server";
            }
         } else if((*m_AccessDict.find(m_AccessBox->currentText()))==SSPI) {
            connMethod = "sspi";
         }
         
         QString dir = "";
         QString server = m_ServerBox->currentText();
	 if (server.find(':') == -1) server += ":";
	 m_TmpBrowseModulesRoot =
	    ":"+connMethod+
	    ":"+m_UserBox->currentText()+
	    "@"+server+
	    m_RepositoryBox->currentText();
	 
	 if (validateCVSROOT(m_TmpBrowseModulesRoot) == 0) {

	    int sshAccess = getSshPreset();
	    //check ssh access settings
	    bUseSsh = false;
	    bUseSshAgent = false;
	    bUseSshAgentVars = false;
	    switch( sshAccess) {
	       case USESSH: {
             bUseSsh = true;
             break;
	       }
	       case USESSHAGENT: {
             bUseSshAgent = true;
             break;
	       }
	       case USESSHAGENTVARS: {
             bUseSshAgentVars = true;
             break;
	       }
	    }
	    
	    m_ModuleBrowser = new ModuleBrowserImpl(*(m_pWhatsThis->iconSet()),
		  NULL,
		  m_TmpBrowseModulesRoot,
		  m_parent,
		  this,
		  "Browse Modules",
		  true,
		  LookAndFeel::g_modalF | WDestructiveClose);
	    
	    connect(m_ModuleBrowser,SIGNAL(OkClicked()),this,SLOT(getAndSetModule()));
	    m_ModuleBrowser->start();

	 } else {
	    emit showWarning(tr("Warning"),tr("Cannot connect, invalid CVSROOT:")+"\n"+m_TmpBrowseModulesRoot);
	 }
      }
   }
}

void CheckoutDialogImpl::resetSize() {
   QTimer::singleShot(0,this,SLOT(reduceHeight()));
}

void CheckoutDialogImpl::reduceHeight() {
   adjustSize();
}

QSize CheckoutDialogImpl::sizeHint () const {
   QSize s = CheckoutDialog::sizeHint();
   s.setWidth(CheckoutDialog::width());
   return s;
}

void CheckoutDialogImpl::accept() {

   if (m_RepositoryBox->currentText().isEmpty()) {
      emit showWarning(tr("Warning"), tr("You have to enter a repository"));
      return;
   } else if (m_ImportDir->currentText().isEmpty()) {
      emit showWarning(tr("Warning"), tr("You have to enter a target dir"));
      return;
   } else if (m_BrowseModules->currentText().isEmpty()) {
      emit showWarning(tr("Warning"), tr("You have to enter a module name"));
      return;
   }
   CheckoutDialog::accept();
}

QString CheckoutDialogImpl::user() {
   return m_UserBox->currentText().stripWhiteSpace();
}

QString CheckoutDialogImpl::server() {
   return m_ServerBox->currentText().stripWhiteSpace();
}

QString CheckoutDialogImpl::localDir() {
   return m_ImportDir->currentText().stripWhiteSpace();
}

QString CheckoutDialogImpl::module() {
   return m_BrowseModules->currentText().stripWhiteSpace();
}

QString CheckoutDialogImpl::checkoutAs() {
   return m_CheckoutAs->currentText().stripWhiteSpace();
}

QString CheckoutDialogImpl::checkoutAsDOption() {
   return m_checkoutAsDOption;
}

QString CheckoutDialogImpl::repository() {
   return m_RepositoryBox->currentText().replace (QRegExp ("/+$"), "");
}

QString CheckoutDialogImpl::getCvsRsh() {
   return m_Rsh->displayText();
}

int CheckoutDialogImpl::getSshPreset() {
   switch (m_SshPreset->currentItem()) {
      case 0: return NOSSH;
      case 1: return USESSH;
      case 2: return USESSHAGENT;
      case 3: return USESSHAGENTVARS;
      default: return -1;
   }
}

QString CheckoutDialogImpl::getRevTagDate() {
   QString param = QString::null;
   QString txt;
   txt = revisionWidgetImpl->getRevision();
   if (!txt.isNull()) param += "-r " + txt;
   else {
      txt = revisionWidgetImpl->getTag();
      if ( !txt.isEmpty() ) param += "-r " + txt;
      if (!param.isEmpty()) param += " ";
      txt = revisionWidgetImpl->getDateTime();
      if (!txt.isEmpty()) param += "-D \"" + txt + "\"";
   }
   return param;
}

bool CheckoutDialogImpl::getRWPermission() {
   return m_Readonly->isChecked();
}

int CheckoutDialogImpl::mode() {
   return (*m_AccessDict.find(m_AccessBox->currentText()));
}

int CheckoutDialogImpl::rshMode() {
   if(m_ExternalRsh->isChecked()) {
      return RSH_EXT;
   } else {
      return RSH_SERVER;
   }
}

void CheckoutDialogImpl::getAndSetModule() {
   m_BrowseModules->setCurrentText(m_ModuleBrowser->getModule());
   m_checkoutAsDOption = m_ModuleBrowser->getCheckoutAsDOption();
   if (m_CheckoutAs->currentText().isEmpty()) {
      m_CheckoutAs->setCurrentText(m_checkoutAsDOption);
   }
   m_ModuleBrowser = NULL;
}

void CheckoutDialogImpl::insertModule(const QString& module) {
   m_BrowseModules->setCurrentText(module);
   m_checkoutAsDOption = "";
}

void CheckoutDialogImpl::setEnabled(bool state) {
   m_ProfileBox->setEnabled(state);
   m_UserBox->setEnabled(state);
   m_ServerBox->setEnabled(state);
   m_RepositoryBox->setEnabled(state);
   m_AccessBox->setEnabled(state);
   RshFrame->setEnabled(state);
   RevisionFrame->setEnabled(state);
   ROFrame->setEnabled(state);
   m_pWhatsThis->setEnabled(state);
   OkButton->setEnabled(state);
   CancelButton->setEnabled(state);
   m_ImportDir->setEnabled(state);
   m_BrowseModules->setEnabled(state);
   m_CheckoutAs->setEnabled(state);
   BrowseDirButton->setEnabled(state);
   BrowseModulesButton->setEnabled(state);
}

void CheckoutDialogImpl::cvsCallStarted() {
   m_running = true;
   setEnabled(FALSE);
   QApplication::setOverrideCursor(Qt::waitCursor);
}

void CheckoutDialogImpl::cvsCallFinished() {
   QApplication::restoreOverrideCursor();
   setEnabled(TRUE);
   m_running = false;
}

void CheckoutDialogImpl::afterCall( int cmd, CvsBuffer *, bool) {
   cvsCallFinished();

   switch (cmd) {
      default:
	 assert(false);
   }
}

void CheckoutDialogImpl::enterWhatsThisMode()
{
   QWhatsThis::enterWhatsThisMode();
}

