#pragma once
/*******************************************************************************

Copyright (c) 2010, Perforce Software, Inc.  All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1.  Redistributions of source code must retain the above copyright
	notice, this list of conditions and the following disclaimer.

2.  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.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 PERFORCE SOFTWARE, INC. 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.

*******************************************************************************/

#include <vector>
#include <clientprog.h>

using std::vector;

/*******************************************************************************
 * Name		: P4BridgeClient.h
 *
 * Author	: dbb
 *
 * Description	:  P4BridgeClient is a class derived from the ClientUser in the 
 *  p4api. It provides the "UI" hooks for the p4api to return send output to the
 *  client app. It collects the output as a command is run so that the entire
 *  output can be retrieved when the command completes. Optionally, the client
 *  the client can register call back function to receive any or all of the 
 *  output as it is generated by the p4api.
 *
 ******************************************************************************/

// Forward ref.
class StrDict;
class StrBufDict;
class P4ClientMerge;
class P4ClientResolve;
class P4BridgeServer;
class P4Connection;

#ifndef STDCALL
#if defined OS_NT
#define STDCALL __stdcall
#else
#define STDCALL
#endif
#endif

/*******************************************************************************
 *
 *  These are the function prototypes for the call backs used to return data to
 *      the client as it is received from the p4api.
 *
 ******************************************************************************/
typedef void STDCALL BinaryCallbackFn( int, void *, int );
typedef void STDCALL TextCallbackFn(int, const char*);
typedef void STDCALL IntIntIntTextCallbackFn(int, int, int, const char*);
typedef void STDCALL IntIntTextCallbackFn(int, int, const char*);
typedef void STDCALL IntTextTextCallbackFn(int, int, const char*, const char*);

typedef void STDCALL PromptCallbackFn( int, const char *, char *, int, int);

// original from the P4 API:
// ClientApi* client, ClientUser *ui, const char *cmd, StrArray &args, StrDict &pVars, int threads, Error *e
// current for us: server pointer, cmd, arg list (IntPtr[] + count), dict iterator, thread count
typedef int STDCALL ParallelTransferCallbackFn(int*, const char*, const char**, int, int*, int);

typedef int STDCALL ResolveCallbackFn( int, P4ClientMerge *);
typedef int STDCALL ResolveACallbackFn( int, P4ClientResolve *, int preview);
/*******************************************************************************
 *
 *  KeyValuePair
 *
 *  This simple class is used to return the data elements of a StrDict used to 
 *      return tagged data.
 *
 ******************************************************************************/

class KeyValuePair : p4base
{
public:
	const int keyLength;
	string key;
	const int valLength;
	string value;

	KeyValuePair(const char * k, int klnth, const char * v, int vlnth);
	virtual ~KeyValuePair();

	virtual int Type(void) { return tKeyValuePair; }
};

/*******************************************************************************
 *
 *  StrDictList
 *
 *  Used to maintain a linked list of StrByDict objects for when more than one
 *      such object is returned by a command.
 *
 ******************************************************************************/

class StrDictList : p4base
{
public:
	StrDictList();
	StrDictList* Next() { return pNext; }
	void Next(StrDictList* pNew) { pNext = pNew; }

	StrDict* Data() { return pStrDict; }

	virtual ~StrDictList();

	virtual int Type(void) { return tStrDictList; }

private:
	StrDict* pStrDict;
	StrDictList* pNext;
};

/*******************************************************************************
 *
 *  StrDictListIterator
 *
 *  Provides the ability to iterate over a list of items (StrDicts ) 
 *      and their data produced by a command using tagged output
 *
 ******************************************************************************/

class StrDictListIterator : p4base
{
public:
	StrDictListIterator(StrDictList* ndict);
	virtual ~StrDictListIterator();
	int Init(StrDictList* ndict);

	// Iterate over the items in the list
	StrDictList* GetNextItem();

	// Iterate the entries in the current item (StrDict)
	KeyValuePair* GetEntry(int idx);
	KeyValuePair* GetNextEntry();

	// free any data held by the iterator and reset to star again
	void Reset();

	virtual int Type(void) { return tStrDictListIterator; }
private:
	StrDictListIterator();

	int idx;
	StrDictList* curItem;
	KeyValuePair* curEntry;
	StrDictList* dict; // head of the list to iterate
};

/*******************************************************************************
 *
 *  P4ClientError
 *
 *  This simple class is used to return the data elements of an error message.
 *
 ******************************************************************************/

class P4ClientError : p4base
{
public:
	int Severity;
	int	ErrorCode;
	string Message;

	P4ClientError * Next;
	P4ClientError(int severity, int	errorCode, const char * msg);
	P4ClientError(P4ClientError * err);
	virtual ~P4ClientError();

	int MaxSeverity();

	virtual int Type(void) { return tP4ClientError; }
};

/*******************************************************************************
 *
 *  P4ClientInfoMsg
 *
 *  This simple class is used to return the data elements of an info message.
 *
 ******************************************************************************/

class P4ClientInfoMsg : p4base
{
public:
	char Level;
	int	MsgCode;
	string Message;

	P4ClientInfoMsg * Next;
	P4ClientInfoMsg(int	errorCode, char level, const char * msg);
	P4ClientInfoMsg(P4ClientInfoMsg * err);
	virtual ~P4ClientInfoMsg();
	virtual int Type(void) { return tP4ClientInfoMsg; }
};

/*******************************************************************************
 *
 *  P4ClientMerge
 *
 *  This simple class is a ClientMerge object.
 *
 ******************************************************************************/

class P4ClientMerge : p4base
{
public:
	P4ClientMerge(ClientMerge * merger);
	~P4ClientMerge();

	P4ClientError *GetLastError();

	MergeStatus AutoResolve( MergeForce forceMerge );
	MergeStatus Resolve();
	MergeStatus DetectResolve();

	int	IsAcceptable();

	StrPtr *GetBaseFile();
	StrPtr *GetYourFile();
	StrPtr *GetTheirFile();
	StrPtr *GetResultFile();

	int	GetYourChunks();
	int	GetTheirChunks();
	int	GetBothChunks();
	int	GetConflictChunks();

	const StrPtr *GetMergeDigest();
	const StrPtr *GetYourDigest();
	const StrPtr *GetTheirDigest();

	virtual int Type(void) { return tP4ClientMerge; }

private:
	ClientMerge * Merger;

	Error err;

	P4ClientError *lastError;
};

/*******************************************************************************
 *
 *  P4ClientMerge
 *
 *  This simple class is a wrapper for ClientMerge object.
 *
 ******************************************************************************/

class P4ClientResolve : p4base
{
private:
	ClientResolveA * Resolver;

	static const Error _INVALID_API_REFERENCE;

	Error err;

	P4ClientError *lastError;

	StrBuf type;

	StrBuf mergeAction;
	StrBuf yoursAction;
	StrBuf theirAction;

	StrBuf mergePrompt;
	StrBuf yoursPrompt;
	StrBuf theirPrompt;

	StrBuf mergeOpt;
	StrBuf yoursOpt;
	StrBuf theirOpt;
	StrBuf skipOpt;
	StrBuf helpOpt;
	StrBuf autoOpt;

	StrBuf prompt;
	StrBuf typePrompt;
	StrBuf usageError;
	StrBuf help;

public:
	P4ClientResolve(ClientResolveA * resolver, int isUnicode);

	~P4ClientResolve();

	P4ClientError *GetLastError();

	MergeStatus AutoResolve( MergeForce force ) const;
	MergeStatus Resolve( int preview );

	StrBuf &GetType() {return type;}

	StrBuf &GetMergeAction() {return mergeAction;}
	StrBuf &GetYoursAction() {return yoursAction;}
	StrBuf &GetTheirAction() {return theirAction;}

	// For the CLI interface, probably not of interest to others

	StrBuf &GetMergePrompt() {return mergePrompt;}
	StrBuf &GetYoursPrompt() {return yoursPrompt;}
	StrBuf &GetTheirPrompt() {return theirPrompt;}

	StrBuf &GetMergeOpt() {return mergeOpt;}
	StrBuf &GetYoursOpt() {return yoursOpt;}
	StrBuf &GetTheirOpt() {return theirOpt;}
	StrBuf &GetSkipOpt() {return skipOpt;}
	StrBuf &GetHelpOpt() {return helpOpt;}
	StrBuf &GetAutoOpt() {return autoOpt;}

	StrBuf &GetPrompt() {return prompt;}
	StrBuf &GetTypePrompt() {return typePrompt;}
	StrBuf &GetUsageError() {return usageError;}
	StrBuf &GetHelp() {return help;}

	virtual int Type(void) { return tP4ClientResolve; }
};
/*******************************************************************************
 *
 *  ProgressCallbackSet
 *
 *  This struct holds function pointers for progress reporting callbacks.
 *  The client can register these to receive progress events as commands run.
 *
 ******************************************************************************/
typedef void STDCALL ProgressInitCallback(int type);
typedef void STDCALL ProgressDescriptionCallback(const char* desc, int units);
typedef void STDCALL ProgressTotalCallback(long total);
typedef void STDCALL ProgressUpdateCallback(long update);
typedef void STDCALL ProgressDoneCallback(int failed);

struct ProgressCallbackSet
{
    ProgressInitCallback* Init;
    ProgressDescriptionCallback* Description;
    ProgressTotalCallback* Total;
    ProgressUpdateCallback* Update;
    ProgressDoneCallback* Done;
};

/*******************************************************************************
 *
 *  P4BridgeClient
 *
 *  Class derived from the ClientUser in the p4api. It provides the "UI" hooks 
 *      for the p4api to return send output to the  *  client app. It collects 
 *      the output as a command is run so that the entire  *  output can be 
 *      retrieved when the command completes. Optionally, the client the client 
 *      can register call back function to receive any or all of the output as 
 *      it is generated by the p4api.
 *
 ******************************************************************************/
extern bool handleUrl;

class P4BridgeClient :	public ClientUser, p4base
{
private:
	// Callback function used to send tagged output to the client. Tagged or
	// data is sent from the api as one or more StrDict objects representing
	// one or more data objects. Each dictionary represents one data objects.
	// I.e. for a list of file stats, each dictionary object will represent the
	// data for one file. The data is stored as Key:Value pairs, i.e 
	// Filename:"MyCode.cpp"
	//
	// The function prototype is:
	//
	//  void IntTextTextCallbackFn(int, const char*, const char*);
	//      
	// The first parameter is an object ID, a given command can return 
	//      multiple objects. ID's will start at 0 and increment for each
	//      successive object.
	// The second parameter is the text 'key'
	// The third parameter is the text value
	// 
	// The client will receive the call back multiple times, once for each
	// Key:Value pair in all of the dictionaries. For m data objects each
	// with n Key:Value pairs, the client will receive m * n call backs. The
	// object id can be used to group the data with there correct objects.
	//
	int objId;

	// Simple linked list to hold the StrDict data returned as tagged output
	// for a command.
	StrDictList * results_dictionary_head;
	StrDictList * results_dictionary_tail;
	// how many entries in the list
	int results_dictionary_count;

	// Linked list to hold the errors (if any) returned by a command.
	P4ClientError  *pFirstError;
	P4ClientError  *pLastError;

	// Linked list to hold the info output (if any) returned by a command.
	P4ClientInfoMsg  *pFirstInfo;
	P4ClientInfoMsg  *pLastInfo;
	// how many entries in the list
	int info_results_count;

	// Store the concatenated text output received in the execution of a 
	//      command
	StrBuf text_results;

	// Store the concatenated binary output received in the execution of a 
	//      command as well as its size in bytes.
	vector<unsigned char> Binary_results;

	// Store the data for a command. Some commands, such as those which use
	//  spec data will use this override to obtain the data needed by the 
	//  command. The data must be set before the command is run.
	StrBuf *data_set;

	// Save the error from an exception to be reported in the exception handler block
	// to prevent possible recursion if it happens when reporting an error.
	StrBuf * ExceptionError;

	PromptCallbackFn* pPromptCallbackFn;
	// Progress callback set for reporting progress events
	ProgressCallbackSet progressCallbacks;
	P4Connection* pCon;

	// Construct + Destructor
	P4BridgeClient(P4BridgeServer* pServer, P4Connection* pcon);

	virtual ~P4BridgeClient(void);
	friend class P4Connection;



public:
	virtual int Type(void) override  { return tP4BridgeClient; }

	// Intercept and decode the messages received from the p4api.
	//  Based on there level, the will be processed as information or
	//  error messages.
	virtual void Message( Error *err ) override;

	//These methods implements the ClientUser and ClientProgress interfaces for progress reporting
	//They allow client to receive progress events as commands are run.
	void SetProgressCallbacks(const ProgressCallbackSet& callbacks);
    const ProgressCallbackSet* GetProgressCallbacks() const { return &progressCallbacks; }

	ClientProgress* CreateProgress(int type, P4INT64 totalSize) override;
	ClientProgress* CreateProgress(int type) override;
	int ProgressIndicator() override;
	int CanParallelProgress() override { return 1; }
	// These are the ClientUser overrides to receive the data back from the
	//  P4 server.
	virtual void OutputBinary( const char *data, int length ) override;

	virtual void OutputText( const char *data, int length ) override;
	virtual void OutputStat( StrDict *dict ) override;
	virtual void InputData( StrBuf *buf, Error *err ) override;
	virtual void HandleError( Error *err ) override;
	virtual void OutputError( const char *err ) override; // For broken servers
	virtual void Diff( FileSys *f1, FileSys *f2, int doPage, 
				char *diffFlags, Error *e ) override;

	void HandleError( P4ClientError * pNewError );
	void HandleError( int severity, int	errorCode, const char *errMsg );

	void HandleInfoMsg( int msgCode, char level, const char *infMsg );
	void HandleInfoMsg( P4ClientInfoMsg * pNewMsg );

	void HandleUrl(const StrPtr* url) override;

	// Put the calls to the callback in Structured Exception Handlers to catch
	//  any problems in the call like bad function pointers.
	void CallTextResultsCallbackFn( const char *data) ;
	void CallInfoResultsCallbackFn( int msgID, char level, const char *data );
	void CallTaggedOutputCallbackFn( int objId, const char *pKey, const char * pVal );
	void CallErrorCallbackFn( int severity, int errorId, const char * errMsg );
	void CallBinaryResultsCallbackFn(void * data, int length );

	// Clear the results after a command completes and the results have been
	//  gathered by the client
	void clear_results();

	//  StrDictListIterator to iterate through the resulting objects
	//  and Key:Value pairs
	StrDictListIterator* GetTaggedOutput(  );
	int GetTaggedOutputCount( ) {return results_dictionary_count;}

	// Get the error output after a command completes
	P4ClientError * GetErrorResults();


	// Set the data for a command. Some commands, such as those which use spec
	//  data will use this override to obtain the data needed by the command.
	//  The data must be set before the command is run.
	void SetDataSet(const char * data);
	StrPtr * GetDataSet( void );

	void Prompt( const StrPtr &msg, StrBuf &rsp, 
				int noEcho, Error *e ) override;

	// Get the information output after a command completes
	// Get the error output after a command completes
	P4ClientInfoMsg * GetInfoResults();
	int GetInfoResultsCount();

	// Get the text output after a command completes
	const char* GetTextResults();

	// Get the binary output and its size after a command completes
	size_t GetBinaryResultsCount() { return Binary_results.size(); }
	const unsigned char* GetBinaryResults();

	// Callbacks for handling interactive resolve
	int	Resolve( ClientMerge *m, Error *e ) override;
	int	Resolve( ClientResolveA *r, int preview, Error *e ) override;

private:
	P4BridgeServer* pServer;

	int Resolve_int( P4ClientMerge *merger);
	int Resolve_int( P4ClientResolve *resolver, int preview, Error *e);

	void clear_error_list(P4ClientError* list);
	void clear_info_list(P4ClientInfoMsg* list);
};

/*******************************************************************************
 *
 *  P4BridgeClientProgress
 *  
 *  Implements the ClientProgress interface and invokes the registered progress
 *  callbacks in P4BridgeClient as progress events occur.
 *
 ******************************************************************************/
class P4BridgeClientProgress : public ClientProgress
{
public:
    P4BridgeClientProgress(const ProgressCallbackSet* cbSet, int t);
    virtual ~P4BridgeClientProgress();

    void Description(const StrPtr* d, int u) override;
    void Total(long t) override;
    int Update(long update) override;
    void Done(int f) override;

private:
    const ProgressCallbackSet* callbacks;
    int type;
};