//
// 
// Copyright 1999,2001 Perforce Software.  All rights reserved.
//
// This file is part of Perforce - the FAST SCM System.
//
//

// Cmd_Depots.cpp

#include "stdafx.h"
#include "p4win.h"
#include "cmd_depots.h"
#include "cmd_describe.h"
#include "tokenstring.h"

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



IMPLEMENT_DYNCREATE(CCmd_Depots, CP4Command)


CCmd_Depots::CCmd_Depots(CGuiClient *client) : CP4Command(client)
{
	m_ReplyMsg = WM_P4DEPOTS;
	m_TaskName = _T("Depots");
	m_GotDepot = FALSE;
}


//-------------------------------------------------------------------

CCmd_Depots::~CCmd_Depots()
{
	
}


//-------------------------------------------------------------------

BOOL CCmd_Depots::Run( )											
{
	ClearArgs( );
	AddArg( _T("depots") );

	return CP4Command::Run( );
}


//-------------------------------------------------------------------

BOOL CCmd_Depots::HandledCmdSpecificError( LPCTSTR errBuf, LPCTSTR errMsg )
{
	if ( StrStr(errBuf, _T("no such file") ) )
	{
		TheApp()->StatusAdd( LoadStringResource(IDS_NO_SUBDIR_UNDER_FOLDER), SV_DEBUG );  
		return TRUE ; 
	}
	
	BOOL b = (StrStr(errBuf, _T("up-to-date.") ) != 0);

	if (!b && !m_GotDepot)
		m_FatalError = TRUE;	// have to set this in case error comes from the broker
								// else we hang.
	return ( b );
}


//-------------------------------------------------------------------
//	One depot at a time comes back from the server, something like:  
//
//	Depot beyond 1999/01/21 remote beyond:1666 //... 'Created by laura. '
//	Depot depot 1999/01/18 local subdir depot/... 'Created by seiwald. '
//	Depot usr 1999/01/26 remote perforce:1951 //usr/... 'Created by seiwald. '
//-------------------------------------------------------------------

void CCmd_Depots::OnOutputInfo( char level, LPCTSTR data, LPCTSTR msg )
{
	if( APP_ABORTING( ) && m_Asynchronous )
    {
		ReleaseServerLock();	
        ExitThread(0);
    }

	if ( StrNCmp(data, _T("Depot "), 6) ==0 )
		m_GotDepot = TRUE;
	else
		ASSERT(0);
	CTokenString str;
	str.Create( data + 6);

	CString depotName;
	depotName.Format(_T("//%s"), str.GetToken()); 
	CString date= str.GetToken();
	ASSERT(StrStr(date, _T("/")));
	
	CString depotType= str.GetToken();
	if( depotType.CompareNoCase(_T("local")) == 0)
		m_LocalDepotList.AddHead( depotName );
	else if( depotType.CompareNoCase(_T("remote")) == 0)
		m_RemoteDepotList.AddHead( depotName );
	else if( depotType.CompareNoCase(_T("spec")) == 0)
	{
		if (GET_SERVERLEVEL() >= 18)
			m_RemoteDepotList.AddHead( depotName );
	}
	else
		ASSERT(0);
}

//-------------------------------------------------------------------

static BOOL IsLFonly(LPCTSTR desc)
{
	CString lineend= TheApp()->GetClientSpecField( _T("LineEnd"), desc );
	if (!lineend.IsEmpty())
		return lineend == _T("unix");
	CString options= TheApp()->GetClientSpecField( _T("Options"), desc );
	return ( options.Find(_T("nocrlf")) != -1 ) ? TRUE : FALSE;
}

//-------------------------------------------------------------------
// extract depot names from client spec text, and put into depotList
// formatted as '//depot/ ', and separated by newlines

static BOOL GetViewDepots( LPCTSTR spectext, CString &depotList)
{
	CString line;
	BOOL hasPlusMapping= FALSE;
    BOOL inView= FALSE;
    int start, end, i;

    CString spec= spectext;
    
    for( start=end=0; end < spec.GetLength()-2; end++)
    {
        if( spec[end] == '\n' )
        {
			line= spec.Mid( start, end-start+1 );
			start= end+1;

			if( line.Find(_T("View")) == 0 )
				inView= TRUE;
			else if( inView )
			{
				line.TrimLeft(_T(" \t\""));	// space, TAB & dbl-quote
				if (line.GetAt(0) == _T('+'))
					hasPlusMapping = TRUE;
				line.TrimLeft(_T(" \t\"+"));	// space, TAB, dbl-quote & plus-sign
				if ( (i = line.Find(_T("//"))) == 0 )
				{
					for(i+=2; i < line.GetLength(); i++)
					{
						if( line[i] == _T('/') )
						{
							line= line.Left( i+1 ) + _T(" ");
							line.TrimLeft(_T('\"'));
							break;
						}
					}
					if( depotList.Find(line) == -1 )
						depotList += line;
				}
				else if (((i=line.Find(_T("-"))) == 0) || ((i==1) && (line.GetAt(0) == _T('\"'))))
				{
				}
				else
					inView= FALSE;
			}
		}
	}

	if( inView && start < spec.GetLength() -1 )
	{
		// Catch a straggler in case there is no trailing '\n'
		line= spec.Mid( start );
		line.TrimLeft(_T(" \t\""));	// space, TAB & dbl-quote
		if (line.GetAt(0) == _T('+'))
			hasPlusMapping = TRUE;
		line.TrimLeft(_T(" \t\"+"));	// space, TAB, dbl-quote & plus-sign
		if ( ((i = line.Find(_T("//"))) == 0) || ((i == 1) && (line[0] == _T('\"'))) )
		{
			for(i+=2; i < line.GetLength(); i++)
			{
				if( line[i] == _T('/') )
				{
					line= line.Left( i+1 ) + _T(" ");
					line.TrimLeft(_T('\"'));
					break;
				}
			}
			
			if( depotList.Find(line) == -1 )
				depotList += line;
		}
	}
	return hasPlusMapping;
}

// Check to see if client view has plus mappings
static BOOL DoesClientViewHavePlusMapping(LPCTSTR spectext)
{
	BOOL inView= FALSE;
	int start, end;
	CString line;
	CString spec= spectext;
	for( start=end=0; end < spec.GetLength()-2; end++)
	{
		if( spec[end] == '\n' )
		{
			 line= spec.Mid( start, end-start+1 );
			start= end+1;

			if( line.Find(_T("View")) == 0 )
				inView= TRUE;
			else if( inView )
			{
				line.TrimLeft(_T(" \t\""));	// space, TAB & dbl-quote
				if (line.GetAt(0) == _T('+'))
				{
					return TRUE;
					break;
				}
			}
		}
	}
	if( inView && start < spec.GetLength()-1 )
	{
		line= spec.Mid( start );
		line.TrimLeft(_T(" \t\""));	// space, TAB & dbl-quote
		if (line.GetAt(0) == _T('+'))
			return TRUE;
	}
	return FALSE;
}

//-------------------------------------------------------------------

void CCmd_Depots::PostProcess()
{
    // The 'p4 depots' command may produce no output, in which case 
    // GET_SERVERLEVEL() will come up dry.  So the first order of 
    // business is to run ANY command that is guaranteed to produce 
    // output.  I selected 'p4 client' cuz we already have the header 
    // included and unlike commands like 'p4 info', it wont take a year 
    // and a day on a slow network

	BOOL hasPlusMapping = FALSE;

    CCmd_Describe cmd(m_pClient);
    cmd.Init( NULL, RUN_SYNC );
    BOOL cmdStarted= cmd.Run( P4CLIENT_SPEC, GET_P4REGPTR()->GetP4Client() );
    if(cmdStarted && !cmd.GetError())
    {
        // get client spec, and set NOCRLF flag and Client root in app
        TheApp()->m_bNoCRLF = IsLFonly(cmd.GetDescription());
        TheApp()->Set_m_ClientRoot(TheApp()->GetClientSpecField( _T("Root"), cmd.GetDescription()));
		if (GET_SERVERLEVEL() >= 22)
	        TheApp()->Set_m_ClientSubOpts(TheApp()->GetClientSpecField( _T("SubmitOptions"), cmd.GetDescription()));
    }
    else
    {
		TheApp()->m_ClientRoot.Empty();
        m_ErrorTxt= LoadStringResource(IDS_UNABLE_TO_GET_CLIENT_DESCRIPTION);
        m_FatalError=TRUE;
		return;
    }

    int depotCount= m_LocalDepotList.GetCount() + m_RemoteDepotList.GetCount(); 

	if (GET_P4REGPTR()->ShowEntireDepot() > SDF_DEPOT)
	{
		m_LocalDepotList.RemoveAll();
		m_LocalDepotList.AddHead(TheApp()->m_ClientRoot);
		hasPlusMapping = DoesClientViewHavePlusMapping(cmd.GetDescription());
	}
    // If more than one depot returned and in client-view-only mode,
    // get the client spec and remove any depots that are outside the clientview
    //
    else if( depotCount > 1 && !(GET_P4REGPTR()->ShowEntireDepot() == SDF_DEPOT) )
    {
        if(!m_FatalError)
        {
            CString depot;
            CString view;
            hasPlusMapping = GetViewDepots( cmd.GetDescription(), view );
            if(IS_NOCASE())
                view.MakeLower();

            POSITION pos1, pos2;
            for( pos1= m_LocalDepotList.GetHeadPosition(); (pos2=pos1) != NULL; )
            {
                depot.Format(_T("%s/"), m_LocalDepotList.GetNext(pos1));
                if(IS_NOCASE())
                    depot.MakeLower();
                if( view.Find( depot ) == -1 )
                {
                    XTRACE(_T("Removed local depot %s because it is outside client view"), depot );
                    m_LocalDepotList.RemoveAt( pos2 );
                }
			}
			for( pos1= m_RemoteDepotList.GetHeadPosition(); (pos2=pos1) != NULL; )
			{
				depot.Format(_T("%s/"), m_RemoteDepotList.GetNext(pos1));
				if(IS_NOCASE())
					depot.MakeLower();
				if( view.Find( depot ) == -1 )
				{
					XTRACE(_T("Removed remote depot %s because it is outside client view"), depot );
					m_RemoteDepotList.RemoveAt( pos2 );
				}
			}
		}
	}
	// Else if no depots returned, just add the single remote depot 'depot' 
	// since there is ALWAYS a default depot called 'depot' when no depots have
	// been defined
	//
	else
	{
		hasPlusMapping = DoesClientViewHavePlusMapping(cmd.GetDescription());
		if ( depotCount == 0 )
			m_LocalDepotList.AddHead( _T("//depot") );
	}

	TheApp()->m_HasPlusMapping = hasPlusMapping;
}