/****************************************************************************
**
** Copyright (C) 2004-2006 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 "Checkout.h"

#include <qapplication.h>
#include <assert.h>

#include "globals.h"
#include "CheckoutDialogImpl.h"

QString Checkout::s_lastProfile = "";

Checkout * Checkout::checkoutProject(const QIconSet &whatsThisIconSet, QWidget* parent, CvsDirListView * w)
{
   return setup(whatsThisIconSet, parent, w, CHECKOUT);
}

Checkout * Checkout::exportProject(const QIconSet &whatsThisIconSet, QWidget* parent, CvsDirListView * w)
{
   return setup(whatsThisIconSet, parent, w, EXPORT);
}

Checkout * Checkout::setup(const QIconSet &whatsThisIconSet, QWidget* parent, CvsDirListView * w, int mode) {

   QStringList * workDirList = NULL;
   QStringList * modulesList = NULL;
   QStringList * checkoutAsList = NULL;
   bool bDeleteLists = false;
   
   QString caption;
   switch(mode) {
      case CHECKOUT: {
         caption = tr("Checkout Project");
         workDirList = &WORKDIRHISTORYLIST;
         modulesList = &MODULEHISTORYLIST;
         checkoutAsList = &CHECKOUTASLIST;
         break;
      }
      case EXPORT: {//don't change lists
         caption = tr("Export Project");
         workDirList = new QStringList(WORKDIRHISTORYLIST);
         modulesList = new QStringList(MODULEHISTORYLIST);
         checkoutAsList = new QStringList(CHECKOUTASLIST);
         bDeleteLists = true;
         
         DirBase * dir = w->selectedItem();
         if(dir) {
            modulesList->prepend(dir->repository());//add current module to top of list
            simplifyStringList(modulesList);
         }
         break;
      }
      default:
      assert(false);
      break;
   }
   
   CheckoutDialogImpl * dlg = new CheckoutDialogImpl( caption,
   whatsThisIconSet,
   &s_lastProfile,
   &cvsProfileNameList,
   &cvsUsers,
   &cvsServers,
   &cvsRepositories,
   workDirList,
   modulesList,
   checkoutAsList,
   parent, "Checkout Dialog", true,
   LookAndFeel::g_modalF);
   
   if (bDeleteLists) {
      delete workDirList;
      delete modulesList;
      delete checkoutAsList;
   }
   
   if (dlg->exec()) {
      Checkout * p = new Checkout(whatsThisIconSet, parent, w, mode);
      p->m_dlg = dlg;
      p->acceptCvs();
   } else {
      delete dlg;
      dlg = NULL;
   }

   return NULL;
}

Checkout::Checkout(const QIconSet &whatsThisIconSet, QWidget* parent, CvsDirListView * workBench, int mode)
   :  m_whatsThisIconSet(whatsThisIconSet),
      m_parent(parent),
      m_workBench(workBench),
      m_dlg(NULL),
      m_mode(mode),
      m_localDirectory(""),
      m_module(""),
      m_sshAccess(NOSSH)
{
   connect(this,SIGNAL(deleteObject(QObject *)),parent,SLOT(slot_deleteObject(QObject *)));
   connect(this,SIGNAL(checkTree(DirBase *)),parent,SLOT(slot_checkStatusOfTree(DirBase *)));
   connect(this,SIGNAL(showInfo(const QString&,const QString&)),parent,SLOT(showInfo(const QString&,const QString&)));
   connect(this,SIGNAL(showWarning(const QString&,const QString&)),parent,SLOT(showWarning(const QString&,const QString&)));
   connect (this, SIGNAL (showOkCancelQuestion (const QString &, const QString &, bool *)), parent, SLOT (showOkCancelQuestion (const QString &, const QString &, bool *)));
   connect(this,SIGNAL(addCheckedOutProject(QString,QString,int)),parent,SLOT(slot_addCheckedOutProject(QString,QString,int)));
}

Checkout::~Checkout() {
   if (m_dlg) delete m_dlg;
   m_dlg = NULL;
}

void Checkout::acceptCvs() {

   assert(m_dlg);
   CheckoutDialogImpl * dlg = static_cast<CheckoutDialogImpl *>(m_dlg);
   assert(dlg);

   int cmd;
   QString cvsRoot = "";
   QString files = "";
   QString tmpCvsRsh = "";
   bool needcvsroot = false;

   switch(m_mode) {
      case CHECKOUT: {
         QDir tmpDir(dlg->localDir());
         if(!tmpDir.exists()) {
            bool result;
	    emit showOkCancelQuestion (tr ("Warning"), tr ("The directory doesn't exist:") + "\n" + dlg->localDir() + "\n" + tr ("Create?"), &result);
	    if (result && ! tmpDir.mkdir (dlg->localDir ())) {
               emit showWarning(tr("Warning")+", "+tr("checkout stopped"),
                     tr("The directory doesn't exist:")+"\n"+
                     dlg->localDir());
               reject();
               return;
	    }
         }
         m_localDirectory = tmpDir.absPath();
         break;
      }
      case EXPORT: {
         QDir newDir(dlg->localDir());
         if(!newDir.exists()) {
            if(!newDir.mkdir(dlg->localDir())) {
               emit showWarning( tr("Warning")+", "+tr("export stopped"),
               tr("Couldn't create local dir:")+"\n"+ 
               dlg->localDir() );
               reject();
               return;
            }
         }
         m_localDirectory = newDir.absPath();
         break;
      }
      default:
      assert(false);
      break;
   }
   
   QString module = dlg->module();
   QString checkoutAs = dlg->checkoutAs();
   QString checkoutAsDOption = dlg->checkoutAsDOption();
   QString repository = dlg->repository();
   QString tmpRevTagDate = dlg->getRevTagDate();
   bool tmpRWPermission = dlg->getRWPermission();
   int mode = dlg->mode();
   int rshMode = dlg->rshMode();
   QString server = dlg->server();
   QString user = dlg->user();
   m_sshAccess = dlg->getSshPreset();
   tmpCvsRsh = dlg->getCvsRsh();
	 
   delete m_dlg;
   m_dlg = NULL;

   if (m_mode == CHECKOUT) {
      //prevent co of allready existing project
      QString tmpModule = checkoutAs;
      if (tmpModule.isEmpty()) tmpModule = checkoutAsDOption;
      if (tmpModule.isEmpty()) tmpModule = module;
      QDir testDir(m_localDirectory+"/"+tmpModule);
      if (testDir.exists()) {
         emit showWarning(tr("Warning")+", "+tr("checkout stopped"),
         tr("Project is already there:")+"\n"+
         m_localDirectory+"/"+tmpModule);
         reject();
         return;
      }
   }

   //check ssh access settings
   bUseSsh = false;
   bUseSshAgent = false;
   bUseSshAgentVars = false;
   switch( m_sshAccess) {
      case USESSH: {
         bUseSsh = true;
         break;
      }
      case USESSHAGENT: {
         bUseSshAgent = true;
         break;
      }
      case USESSHAGENTVARS: {
         bUseSshAgentVars = true;
         break;
      }
   }
	 
   //modify application-wide lists
   adaptQStringList(module,&MODULEHISTORYLIST,HistorySize::g_profiles);
   adaptQStringList(checkoutAs,&CHECKOUTASLIST,HistorySize::g_profiles);
   adaptQStringList(m_localDirectory,&WORKDIRHISTORYLIST,HistorySize::g_workdirs);
   adaptQStringList(user, &cvsUsers, HistorySize::g_profiles);
   adaptQStringList(server, &cvsServers, HistorySize::g_profiles);
   adaptQStringList(repository, &cvsRepositories, HistorySize::g_profiles);
	 
   //set CVSROOT      
   if(mode == SSPI) { //sspi
     //set CVSROOT for sspi mode
     cvsRoot = ":sspi:";
     needcvsroot = true;
   } else if(mode == PSERVER) { //password server
      //set CVSROOT for pserver mode
      cvsRoot = ":pserver:";
      needcvsroot = true;
   } else if(mode == RSH) {//remote shell access
      needcvsroot = true;
      if(rshMode == RSH_EXT) {
         cvsRoot = ":ext:";
      } else {
         cvsRoot = ":server:";
      }
   }
   if (server.find(':') == -1) server += ":";
   if (needcvsroot) cvsRoot += user + "@" + server;
   cvsRoot +=  repository;
   cvsRoot = masqWs(cvsRoot);
	 
   if (tmpRWPermission) {
      files = "-r ";
   }
   files += CvsOptions::cmprStr();

   switch(m_mode) {
      case CHECKOUT: {
         files += " co ";
         files += (CvsOptions::g_bPruneDirs ? "-P " : " ");
         
         if (!tmpRevTagDate.isNull()) {
            files += tmpRevTagDate + " ";
         }
         
         if (!checkoutAs.isEmpty()) {
            m_module =  checkoutAs;
            files += "-d " + masqWs(checkoutAs) + " ";
            files += masqWs(module);
         } else if (!checkoutAsDOption.isEmpty()) {
            m_module =  checkoutAsDOption;
            files += masqWs(module);
         } else {
            m_module =  module;
            files += masqWs(module);
         }
         
         if (CvsOptions::g_bPruneDirs) cmd = CVS_CHECKOUT_CMD;
         else cmd = CVS_CHECKOUT_NO_PRUNE_CMD;
         
         break;
      }
      case EXPORT: {
         files += " export -N ";
         
         if (tmpRevTagDate.isNull()) {
            files += "-r HEAD ";
         } else if (!tmpRevTagDate.isEmpty()) {
            files += tmpRevTagDate + " ";
         }
         
         if (!checkoutAs.isEmpty()) {
            m_module = checkoutAs;
            files += "-d " + masqWs(checkoutAs) + " ";
            files += masqWs(module);
         } else {
            m_module = module;
            files += masqWs(module);
         }
         
         cmd = CVS_EXPORT_CMD;
         
         break;
      }
      default:
      assert(false);
      break;
   }
   
   QString topModule = QString::null;

   callInteractive( topModule, m_localDirectory, cvsRoot,
	 files, cmd, tmpCvsRsh, false);


}

void Checkout::reject() {
   emit deleteObject(this);
}

void Checkout::cvsCallStarted() {
   QApplication::setOverrideCursor(Qt::waitCursor);
}

void Checkout::cvsCallFinished() {
   QApplication::restoreOverrideCursor();
}

void Checkout::afterCall(int cmd,CvsBuffer * buf,bool failed) {
   cvsCallFinished();
   if (failed) {
      reject();
      return;
   }
   switch( cmd) {
      case NOOP: {
         break;
      }
      case CVS_CHECKOUT_CMD:
      case CVS_CHECKOUT_NO_PRUNE_CMD: {
         //check if cvs checkout succeeded
         bool test = false;
         unsigned int len = (*buf).numLines();
         for ( unsigned int i = outputLineOffset;i<len;i++) {
            if ((*buf).textLine(i).startsWith("U ")) {
               test = true;
               break;
            }
         }
         if (!test) {
            emit showWarning(tr("Warning"),tr("Checkout failed"));
            reject();
            return;
         }
         
         //cvs dir is already in workbench?
         QString topProjectDir = "";
         QString subProjectDir = "";
         bool found = false;
         DirBase * myChild = NULL;
         
         QString importDir = m_localDirectory + "/" + m_module;
         int pos = m_module.length();
         do {
            
            pos = m_module.findRev("/",pos-m_module.length()-1);
            importDir.truncate((unsigned int)(m_localDirectory.length()+1+pos));
            QString module = m_module.mid(pos+1);
            
            if ( (myChild = m_workBench->find(importDir)) ) {
               
               if (myChild->isAnalyzed()) {
                  
                  QString dirToAdd = importDir + "/" + module;
                  dirToAdd = dirToAdd.left(dirToAdd.find("/",myChild->fullName().length()+1));
                  DirBase * unanalyzedDir = myChild->addDir(dirToAdd);
                  if (unanalyzedDir && myChild->isOpen()) {//will not be analyzed on open() because it is already open
                     myChild->setOpen(TRUE);
                  }
                  if (myChild->isSelected()) {
                     myChild->activateItem(TRUE);//reread files
                  } else {
                     m_workBench->setSelected(myChild,TRUE);//reread files
                  }
               }
               
               emit showInfo( tr("Checkout Info"), tr("This project subdir has been added.") );
               topProjectDir = myChild->topDir()->shortName();
               DirBase * tmp = m_workBench->find(importDir + "/" + module);
               if (tmp) {
                  subProjectDir = tmp->topControlledDir()->relativeName();
                  projectSettings->set(subProjectDir,SSHACCESS,m_sshAccess);
               }
               found = true;
            }
            
         } while ( !found && (pos > -1));
         
         if ( !found && ((m_module == ".") || (m_module == "..")) ) {
            reject();
            return;//-d option in CVSROOT/modules file
         }
         
         if ( !found) {
            emit addCheckedOutProject(m_localDirectory,m_module.left(m_module.find("/")),m_sshAccess);
         }
         break;
      }
      case CVS_EXPORT_CMD: {
         //check if cvs export succeeded
         bool test = false;
         unsigned int len = (*buf).numLines();
         for ( unsigned int i = outputLineOffset;i<len;i++) {
            if ((*buf).textLine(i).startsWith("U ")) {
               test = true;
               break;
            }
         }
         if (!test) {
            emit showWarning(tr("Warning"),tr("Export failed"));
         } else {
            emit showInfo( tr("Export Info"),
            tr("The export of:")+
            "\n'"+m_module+"'\n"+
            tr("into:")+
            "\n'"+m_localDirectory+"'\n"+
            tr("succeeded") );
         }
         break;
      }
      default: {
         qDebug("Checkout::afterCall: Unknown cmd");
      }
   }
   reject();
}



syntax highlighted by Code2HTML, v. 0.9.1