#ifndef __PostScript_Support_hpp__
#define __PostScript_Support_hpp__ 1

// =================================================================================================
// Copyright Adobe
// Copyright 2012 Adobe
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
// of the Adobe license agreement accompanying it. 
// =================================================================================================

#include "public/include/XMP_Environment.h"	

#include "public/include/XMP_Const.h"
#include "public/include/XMP_IO.hpp"

#include "XMPFiles/source/XMPFiles_Impl.hpp"
#include "source/XMPFiles_IO.hpp"
#include "source/XIO.hpp"


#define MAX_NO_MARK 100
#define IsNumeric( ch ) ( ch >='0' && ch<='9' )
#define Uns8Ptr(p) ((XMP_Uns8 *) (p))
#define IsPlusMinusSign(ch)  ( ch =='+' || ch=='-' )
#define IsDateDelimiter( ch ) ( ((ch) == '/') || ((ch) == '-') || ((ch) == '.') )
#define IsTimeDelimiter( ch ) ( ((ch) == ':') )
#define IsDelimiter(ch) (IsDateDelimiter( ch ) || IsTimeDelimiter( ch ))
#define IsAlpha(ch) ((ch>=97 &&ch <=122) || (ch>=65 && ch<=91))


enum {
	kPSHint_NoMarker  = 0,
	kPSHint_NoMain    = 1,
	kPSHint_MainFirst = 2,
	kPSHint_MainLast  = 3
};

enum UpdateMethod{
	kPS_None			= 0,
	kPS_Inplace			= 1,
	kPS_ExpandSFDFilter = 2,
	kPS_InjectNew		= 3
};

enum TokenFlag{
	// --------------------
	// Flags for native Metadata  and DSC commnents in EPS format

	/// No Native MetaData
	kPS_NoData			=   0x00000001,
	/// Document Creator tool
	kPS_CreatorTool		=   0x00000002,
	/// Document Creation Date
	kPS_CreateDate		=   0x00000004,
	/// Document Modify Date
	kPS_ModifyDate		=   0x00000008,
	/// Document Creator/Author
	kPS_Creator			=   0x00000010,
	/// Document Title
	kPS_Title			=   0x00000020,
	/// Document Desciption
	kPS_Description		=   0x00000040,
	/// Document Subject/Keywords
	kPS_Subject			=   0x00000080,
	/// ADO_ContainsXMP hint
	kPS_ADOContainsXMP	=   0x00000100,
	/// End Comments
	kPS_EndComments		=   0x00000200,
	/// Begin Prolog
	kPS_BeginProlog		=   0x00000400,
	/// End Prolog
	kPS_EndProlog	    =   0x00000800,
	/// Begin Setup
	kPS_BeginSetup	    =   0x00001000,
	/// End Setup
	kPS_EndSetup	    =   0x00002000,
	/// Page
	kPS_Page	        =   0x00004000,
	/// End Page Comments
	kPS_EndPageComments	=   0x00008000,
	/// Begin Page SetUp
	kPS_BeginPageSetup	=   0x00010000,
	/// End Page SetUp
	kPS_EndPageSetup	=   0x00020000,
	/// Trailer
	kPS_Trailer			=   0x00040000,
	/// EOF
	kPS_EOF				=   0x00080000,
	/// End PostScript
	kPS_EndPostScript	=   0x00100000,
	/// Max Token
	kPS_MaxToken		=   0x00200000
};

enum NativeMetadataIndex{
	// --------------------
	// Index native Metadata ina PS file
	kPS_dscCreator			= 0, 
	kPS_dscCreateDate		= 1,
	kPS_dscFor				= 2,
	kPS_dscTitle			= 3,
	kPS_docInfoCreator		= 4,
	kPS_docInfoCreateDate	= 5,
	kPS_docInfoModDate		= 6,
	kPS_docInfoAuthor		= 7,
	kPS_docInfoTitle		= 8,
	kPS_docInfoSubject		= 9,
	kPS_docInfoKeywords		= 10,
	kPS_MaxNativeIndexValue 
};

static XMP_Uns64 nativeIndextoFlag[]={	kPS_CreatorTool,
										kPS_CreateDate,
										kPS_Creator,
										kPS_Title,
										kPS_CreatorTool,
										kPS_CreateDate,
										kPS_ModifyDate,
										kPS_Creator,
										kPS_Title,
										kPS_Description,
										kPS_Subject
									};

static const std::string kPSFileTag    = "%!PS-Adobe-";
static const std::string kPSContainsXMPString = "%ADO_ContainsXMP:";
static const std::string kPSContainsBBoxString = "%%BoundingBox:";
static const std::string kPSContainsBeginDocString = "%%BeginDocument:";
static const std::string kPSContainsEndDocString = "%%EndDocument";
static const std::string kPSContainsTrailerString = "%%Trailer";
static const std::string kPSContainsCreatorString = "%%Creator:";
static const std::string kPSContainsCreateDateString = "%%CreationDate:";
static const std::string kPSContainsForString = "%%For:";
static const std::string kPSContainsTitleString = "%%Title:";
static const std::string kPSContainsAtendString = "(atend)";
static const std::string kPSEndCommentString  = "%%EndComments";	// ! Assumed shorter than kPSContainsXMPString.
static const std::string kPSContainsDocInfoString  = "/DOCINFO";	
static const std::string kPSContainsPdfmarkString  = "pdfmark";	
static const std::string kPS_XMPHintMainFirst="%ADO_ContainsXMP: MainFirst\n";
static const std::string kPS_XMPHintMainLast="%ADO_ContainsXMP: MainLast\n";

// For new xpacket injection into the EPS file is done in Postscript using the pdfmark operator
// There are different conventions described for EPS and PS files in XMP Spec part 3.
// The tokens kEPS_Injectdata1, kEPS_Injectdata2 and kEPS_Injectdata3 are used to
// embedd xpacket in EPS files.the xpacket is written inbetween kEPS_Injectdata1 and kEPS_Injectdata2.
// The tokens kPS_Injectdata1 and kPS_Injectdata2 are used to embedd xpacket in DSC compliant PS files
// The code inside the tokens is taken from examples in XMP Spec part 3 
// section 2.6.2 PS, EPS (PostScript and Encapsulated PostScript)
static const std::string kEPS_Injectdata1="\n/currentdistillerparams where\n"
"{pop currentdistillerparams /CoreDistVersion get 5000 lt} {true} ifelse\n"
"{userdict /EPSHandler1_pdfmark /cleartomark load put\n"
"userdict /EPSHandler1_ReadMetadata_pdfmark {flushfile cleartomark} bind put}\n"
"{ userdict /EPSHandler1_pdfmark /pdfmark load put\n"
"userdict /EPSHandler1_ReadMetadata_pdfmark {/PUT pdfmark} bind put } ifelse\n"
"[/NamespacePush EPSHandler1_pdfmark\n"
"[/_objdef {eps_metadata_stream} /type /stream /OBJ EPSHandler1_pdfmark\n"
"[{eps_metadata_stream} 2 dict begin\n"
"/Type /Metadata def /Subtype /XML def currentdict end /PUT EPSHandler1_pdfmark\n"
"[{eps_metadata_stream}\n"
"currentfile 0 (% &&end EPS XMP packet marker&&)\n"
"/SubFileDecode filter EPSHandler1_ReadMetadata_pdfmark\n";

static const std::string kPS_Injectdata1="\n/currentdistillerparams where\n"
"{pop currentdistillerparams /CoreDistVersion get 5000 lt} {true} ifelse\n"
"{userdict /PSHandler1_pdfmark /cleartomark load put\n"
"userdict /PSHandler1_ReadMetadata_pdfmark {flushfile cleartomark} bind put}\n"
"{ userdict /PSHandler1_pdfmark /pdfmark load put\n"
"userdict /PSHandler1_ReadMetadata_pdfmark {/PUT pdfmark} bind put } ifelse\n"
"[/NamespacePush PSHandler1_pdfmark\n"
"[/_objdef {ps_metadata_stream} /type /stream /OBJ PSHandler1_pdfmark\n"
"[{ps_metadata_stream} 2 dict begin\n"
"/Type /Metadata def /Subtype /XML def currentdict end /PUT PSHandler1_pdfmark\n"
"[{ps_metadata_stream}\n"
"currentfile 0 (% &&end PS XMP packet marker&&)\n"
"/SubFileDecode filter PSHandler1_ReadMetadata_pdfmark\n";

static const std::string kEPS_Injectdata2="\n% &&end EPS XMP packet marker&&\n"
"[/Document\n"
"1 dict begin /Metadata {eps_metadata_stream} def\n"
"currentdict end /BDC EPSHandler1_pdfmark\n"
"[/NamespacePop EPSHandler1_pdfmark\n";


static const std::string kPS_Injectdata2="\n% &&end PS XMP packet marker&&\n"
"[{Catalog} {ps_metadata_stream} /Metadata PSHandler1_pdfmark\n"
"[/NamespacePop PSHandler1_pdfmark\n";

static const std::string  kEPS_Injectdata3="\n/currentdistillerparams where\n"
	"{pop currentdistillerparams /CoreDistVersion get 5000 lt} {true} ifelse\n"
	"{userdict /EPSHandler1_pdfmark /cleartomark load put}\n"
	"{ userdict /EPSHandler1_pdfmark /pdfmark load put} ifelse\n"
	"[/EMC EPSHandler1_pdfmark\n";


namespace PostScript_Support
{
	struct Date
	{
		short day;
		short month;
		short year;
		short hours;
		short minutes;
		short seconds;
		bool containsOffset;
		char offsetSign;
		short offsetHour;
		short offsetMin;
		Date(short pday=1,short pmonth=1,short pyear=1900,short phours=0,
			short pminutes=0,short pseconds=0):day(pday),month(pmonth),
			year(pyear),hours(phours),minutes(pminutes),seconds(pseconds),
			containsOffset(false),offsetSign('+'),offsetHour(0),offsetMin(0)
		{
		}
		bool operator==(const Date &a)
		{
			return this->day==a.day &&
				this->month==a.month &&
				this->year==a.year &&
				this->hours==a.hours &&
				this->minutes==a.minutes &&
				this->seconds==a.seconds &&
				this->containsOffset==a.containsOffset &&
				this->offsetSign==a.offsetSign &&
				this->offsetHour==a.offsetHour &&
				this->offsetMin==a.offsetMin;
		}
	};
	struct DateTimeTokens
	{
		std::string token;
		short noOfDelimiter;
		char delimiter;
		DateTimeTokens(std::string ptoken="",short pnoOfDelimiter=0,char pdelimiter=0):
		token(ptoken),noOfDelimiter(pnoOfDelimiter),delimiter(pdelimiter)
		{
		}
	};

	//function to parse strings and get date out of it
	std::string ConvertToDate(const char* inString);
	// These helpers are similar to RefillBuffer and CheckFileSpace with the difference that the it traverses
	// the file stream in reverse order
	void RevRefillBuffer ( XMP_IO* fileRef, IOBuffer* ioBuf );
	bool RevCheckFileSpace ( XMP_IO* fileRef, IOBuffer* ioBuf, size_t neededLen );

	// function to detect character codes greater than 127 in a string
	bool HasCodesGT127(const std::string & value);
	
	// function moves the file pointer ahead such that it skips all tabs and spaces 
	bool  SkipTabsAndSpaces(XMP_IO* file,IOBuffer& ioBuf);
	
	// function moves the file pointer ahead such that it skips all characters until a newline 
	bool SkipUntilNewline(XMP_IO* file,IOBuffer& ioBuf);
	
	// function to detect character codes greater than 127 in a string
	bool IsValidPSFile(XMP_IO*    fileRef,XMP_FileFormat &format);
	
	// Determines Whether the metadata is embedded using the Sub-FileDecode Approach or no
	bool IsSFDFilterUsed(XMP_IO* &fileRef, XMP_Int64 xpacketOffset);

} // namespace PostScript_Support

#endif	// __PostScript_Support_hpp__
