#include "WapPush.h"
#include "wbxml2\wbxml.h"

#define ASCII(x) ((x >= 0 && x <= 9) ? x += '0' :  x += 'A' - 0x0A)

#define MESSAGE_DELIVERED 0
#define MODEM_NETWORK_ERROR 1
#define FORMAT_ERROR 2
#define ARG_ERROR 3
#define CMDLINE_ERROR 4
#define PIN_ERROR 5
#define SERVICE_ERROR 6
#define MODEM_ERROR 7
#define NETWORK_ERROR 8
#define MODEM_NOT_FOUND_ERROR 9
#define MODEM_NOT_ATTACHED_ERROR 10
#define MSG_TOO_LONG_ERROR 11

WapPush::WapPush() : m_bCOMOpen(FALSE), m_pcWbXml(NULL), m_pcAsciiXml(NULL), m_bStandardSms(FALSE) {
	ZeroMemory(cBCDNumber, sizeof(cBCDNumber));
}

WapPush::~WapPush() {
	Close();

	if (m_pcWbXml != NULL)
		wbxml_free(m_pcWbXml);

	if (m_pcAsciiXml != NULL)
		delete[] m_pcAsciiXml;
}

BOOL WapPush::AutoDiscover() {
	WCHAR wPort[6];

	for (UINT i = 1; i < 30; i++) {
		wsprintf(wPort, L"COM%d", i);

		//wprintf(L"[DEBUG] checking port: COM%d\n", i);

		if (SetPort(wPort) == FALSE) {
			wprintf(L"[DEBUG] Port seems invalid\n");
			continue;
		}

		if (Open() == FALSE) {
			wprintf(L"[DEBUG] Port cannot be opened\n");
			continue;
		}

		if (isZadako() || isSierra() || isUnknownButValid()) {
			Close();
			SetPort(wPort);

			wprintf(L"[INFO] Suitable modem identified on port: %s\n", wPort);
			return TRUE;
		}

		wprintf(L"[DEBUG] Modem found is not suitable for this message category\n");
		Close();
	}

	wprintf(L"[ERROR] Unable to autodiscover a suitable modem\n");
	wprintf(L"[INFO] Please remind: if you're using a Zadako Modem, the \"AirCard Watcher\" utility MUST be _running_)\n");
	wprintf(L"[INFO] Please remind: if you're using a Sierra Modem, the \"Sierra Wireless Discovery Tool\" MUST be *closed*)\n");

	return FALSE;
}

// Ritorna la porta sui cui si trova uno dei nostri modem o -1 se non la trova
INT WapPush::GetAutoDiscovered() {
	WCHAR wPort[6];

	for (UINT i = 1; i < 50; i++) {
		wsprintf(wPort, L"COM%d", i);

		if (SetPort(wPort) == FALSE) {
			continue;
		}

		if (Open() == FALSE) {
			continue;
		}

		if (isZadako() || isSierra() || isUnknownButValid()) {
			Close();

			return (INT)i;
		}
		il
		Close();
	}

	return -1;
}

// Zadako
// AT+CGMI -> Sierra Wireless
// AT+CGMM -> MC8755 o MC8775
// MC8790 -> Non di HT ma usato da CNI
BOOL WapPush::isZadako() {
	string response;

	response = SendCommandAndGet("AT+CGMI\r");

	// REMOVE
	wprintf(L"[DEBUG] Manufacturer: %S\n", response.c_str());

	if (response.find("Sierra Wireless") == string::npos) {
		//wprintf(L"[DEBUG] Not Zadako a, response: %S\n", response.c_str());
		return FALSE;
	}

	response = SendCommandAndGet("AT+CGMM\r");

	// REMOVE
	wprintf(L"[DEBUG] Chipset: %S\n", response.c_str());

	if (response.find("MC8755") == string::npos && response.find("MC8775") == string::npos && response.find("MC8790") == string::npos) {
		//wprintf(L"[DEBUG] Not Zadako b, response: %S\n", response.c_str());
		return FALSE;
	}

	return TRUE;
}

// Unknown model
// AT+CGMI -> ZTE INCORPORATED
// AT+CGMM -> MF626
// MF626 -> Non di HT ma usato da UZC
BOOL WapPush::isUnknownButValid() {
	return FALSE;

	string response;

	response = SendCommandAndGet("AT+CGMI\r");

	// REMOVE
	wprintf(L"[DEBUG] Manufacturer: %S\n", response.c_str());

	if (response.find("ZTE INCORPORATED") == string::npos) {
		//wprintf(L"[DEBUG] Not Zadako a, response: %S\n", response.c_str());
		return FALSE;
	}

	response = SendCommandAndGet("AT+CGMM\r");

	// REMOVE
	wprintf(L"[DEBUG] Chipset: %S\n", response.c_str());

	if (response.find("MF626") == string::npos) {
		//wprintf(L"[DEBUG] Not Zadako b, response: %S\n", response.c_str());
		return FALSE;
	}

	return TRUE;
}

// Sierra Wireless
// AT+CGMI -> WAVECOM
// AT+CGMR -> FXT001 o FXT003
BOOL WapPush::isSierra() {
	string response;

	response = SendCommandAndGet("AT+CGMI\r");

	// REMOVE
	wprintf(L"[DEBUG] Manufacturer: %S\n", response.c_str());

	if (response.find("WAVECOM") == string::npos) {
		//wprintf(L"[DEBUG] Not Sierra a, response: %S\n", response.c_str());
		return FALSE;
	}

	response = SendCommandAndGet("AT+CGMR\r");

	// REMOVE
	wprintf(L"[DEBUG] Chipset: %S\n", response.c_str());

	if (response.find("FXT001") == string::npos && response.find("FXT003") == string::npos) {
		//wprintf(L"[DEBUG] Not Sierra b, response: %S\n", response.c_str());
		return FALSE;
	}

	return TRUE;
}

BOOL WapPush::Open() {
	if (strPort.empty()) {
		wprintf(L"[ERROR] No COM port set\n");
		m_bCOMOpen = FALSE;
		return FALSE;
	}

	if (m_bCOMOpen)
		return TRUE;

	if (serialObj.Open(strPort)) {
		m_bCOMOpen = TRUE;
		return TRUE;
	}

	m_bCOMOpen = FALSE;
	return FALSE;
}

BOOL WapPush::Close() {
	if (m_bCOMOpen == FALSE)
		return TRUE;

	if (serialObj.Close()) {
		m_bCOMOpen = FALSE;
		return TRUE;
	}

	m_bCOMOpen = TRUE;
	return FALSE;
}

BOOL WapPush::SetPort(PWCHAR pwPort) {
	if (pwPort == NULL) {
		wprintf(L"[INFO] Port missing, autodiscovering...\n");
		return FALSE;
	}

	if (wcslen(pwPort) < 4 || wcslen(pwPort) > 6) {
		wprintf(L"[ERROR] Invalid port format, should be: COM1, COM3, COM12 etc...\n");
		return FALSE;
	}

	if (m_bCOMOpen && strPort.empty() == FALSE) {
		// Convert to uppercase
		transform(strPort.begin(), strPort.end(), strPort.begin(), ptr_fun(toupper));
	}

	if (m_bCOMOpen && strPort.empty() == FALSE && strPort.compare(pwPort)) {
		wprintf(L"[INFO] Port changed, closing old connection...\n");
		Close();
	}

	strPort = pwPort;

	// Convert to uppercase
	transform(strPort.begin(), strPort.end(), strPort.begin(), ptr_fun(toupper));

	return TRUE;
}

BOOL WapPush::SetPIN(PWCHAR pwPin) {
	if (pwPin == NULL) {
		strPin.clear();
		return TRUE;
	}

	strPin = pwPin;
	return TRUE;
}

BOOL WapPush::SetNumber(PWCHAR pwNumber) {
	UINT i;

	if (pwNumber == NULL || wcslen(pwNumber) > 15 || wcslen(pwNumber) < 10) {
		wprintf(L"[ERROR] Phone number format is invalid or missing\n");
		return FALSE;
	}

	strNumber = pwNumber;

	wprintf(L"[INFO] Phone number set to: %s\n", pwNumber);

	// We have to convert it in BCD format
	if (wcslen(pwNumber) & 1) {
		// Odd number, padding required
		sprintf_s(cBCDNumber, sizeof(cBCDNumber), "%SF", pwNumber);
	} else {
		sprintf_s(cBCDNumber, sizeof(cBCDNumber), "%S", pwNumber);
	}

	for (i = 0; i < strlen(cBCDNumber) - 1; i += 2) {
		CHAR tmp;

		tmp = cBCDNumber[i];
		cBCDNumber[i] = cBCDNumber[i + 1];
		cBCDNumber[i + 1] = tmp;
	}
 
	return TRUE;
}

BOOL WapPush::SetLink(PWCHAR pwLink) {
	if (pwLink == NULL) {
		wprintf(L"[ERROR] HTTP Link format is invalid or missing\n");
		return FALSE;
	}

	strLink = pwLink;
	return TRUE;
}

// Text can be NULL
BOOL WapPush::SetText(PWCHAR pwText) {
	if (pwText == NULL) {
		strText = L"";
	} else {
		strText = pwText;
	}

	return TRUE;
}

BOOL WapPush::SetService(PWCHAR pwService) {
	if (pwService == NULL) {
		wprintf(L"[ERROR] Service format is invalid or missing\n");
		return FALSE;
	}

	strService = pwService;

	// Convert to lowercase
	transform(strService.begin(), strService.end(), strService.begin(), ptr_fun(tolower));

	// Enforce type ("si" or "sl" or "sms")
	if (strService.compare(L"si") && strService.compare(L"sl") && strService.compare(L"sms")) {
		wprintf(L"[ERROR] Service format is invalid\n");
		return FALSE;
	}

	if (!strService.compare(L"sms")) {
		this->m_bStandardSms = TRUE;
	}

	return TRUE;
}

BOOL WapPush::SetPriority(PWCHAR pwPriority) {
	if (pwPriority == NULL) {
		wprintf(L"[ERROR] Priority format is invalid or missing\n");
		return FALSE;
	}

	strPriority = pwPriority;

	// Convert to lowercase
	transform(strPriority.begin(), strPriority.end(), strPriority.begin(), ptr_fun(tolower));

	// Enforce type
	if (strService.empty() == FALSE) {
		// We have service-type set (SI)
		if (!strService.compare(L"si")) {
			// signal-none, signal-low, signal-medium, signal-high, delete
			if (strPriority.compare(L"signal-none") && strPriority.compare(L"signal-low") && 
				strPriority.compare(L"signal-medium") && strPriority.compare(L"signal-high") && 
				strPriority.compare(L"delete")) {
					wprintf(L"[ERROR] Chosen priority is not compatible with type SI\n");
					return FALSE;
			}
		} else if (!strService.compare(L"sl")) { // SL
			// execute-low, execute-high, cache
			if (strPriority.compare(L"execute-low") && strPriority.compare(L"execute-high") && 
				strPriority.compare(L"cache")) {
					wprintf(L"[ERROR] Chosen priority is not compatible with type SL\n");
					return FALSE;
			}
		} else if (!strService.compare(L"sms")) { // SMS

		} else {
			wprintf(L"[ERROR] Service format is invalid\n");
			return FALSE;
		}
	} else {
		// Service not set, we have to check all types
		if (strPriority.compare(L"signal-none") && strPriority.compare(L"signal-low") && 
			strPriority.compare(L"signal-medium") && strPriority.compare(L"signal-high") && 
			strPriority.compare(L"delete") && strPriority.compare(L"execute-low") && 
			strPriority.compare(L"execute-high") && strPriority.compare(L"cache")) {
				wprintf(L"[ERROR] Chosen priority type is invalid\n");
				return FALSE;
		}
	}

	return TRUE;
}

BOOL WapPush::SetDate(PWCHAR pwDate) {
	if (!strService.compare(L"si") && (pwDate == NULL || wcslen(pwDate) == 0)) {
		wprintf(L"[ERROR] Creation date is mandatory for service type \"si\"\n");
		return FALSE;
	}

	if (pwDate == NULL)
		strDate = L"";
	else
		strDate = pwDate;

	return TRUE;
}

BOOL WapPush::SendMessage(PWCHAR pwPort, PWCHAR pwPIN, PWCHAR pwNumber, PWCHAR pwText, 
						  PWCHAR pwService, PWCHAR pwPriority, PWCHAR pwLink, PWCHAR pwDate) {
	CHAR *pcMsg = NULL;

	if (SetPort(pwPort) == FALSE) {
		if (AutoDiscover() == FALSE) {
			return MODEM_NOT_FOUND_ERROR;
		}
	}

	if (SetNumber(pwNumber) == FALSE)
		return ARG_ERROR;

	if (SetPIN(pwPIN) == FALSE)
		return PIN_ERROR;

	if (SetText(pwText) == FALSE)
		return ARG_ERROR;

	if (SetService(pwService) == FALSE)
		return SERVICE_ERROR;

	INT msgLen = 0;

	if (pwText)
		msgLen += wcslen(pwText);

	if (pwLink)
		msgLen += wcslen(pwLink);

	if (msgLen > 76) {
		wprintf(L"[ERROR] Total text length: %d\n", msgLen);

		return MSG_TOO_LONG_ERROR;
	}

	// Attach, just in case
	SendCommand("AT+CGATT=1\r");

	string resp = SendCommandAndGet("AT+CGATT?\r");

	// If we are not attached, return error
	if (resp.find("0") != string::npos) {
		wprintf(L"[WARNING] Modem is not attached to GPRS network\n");
		wprintf(L"[NOTICE] Trying to attach to the network...\n");
		Sleep(3000);

		resp = SendCommandAndGet("AT+CGATT?\r");

		if (resp.find("0") != string::npos) {
			wprintf(L"[ERROR] Giving up, modem is not attached to GRPS network\n");
			return MODEM_NOT_ATTACHED_ERROR;
		}

		wprintf(L"[NOTICE] Interface now ready\n");
	}

	if (this->m_bStandardSms == FALSE) {
		if (SetPriority(pwPriority) == FALSE)
			return ARG_ERROR;

		if (SetDate(pwDate) == FALSE)
			return ARG_ERROR;
		
		if (SetLink(pwLink) == FALSE)
			return ARG_ERROR;

		// Let's build a "standard" message (XML to WBXML to ASCII)
		if (BuildWBXML() == FALSE) {
			return FORMAT_ERROR;
		}

		wprintf(L"[INFO] WBXML Built\n");
	} else { // Send the SMS
		// AT+CMGS="pwNumber"\r\npwMsg\x1A
		int cmdLen = strlen("AT+CMGS=\"+") + strNumber.length() + strlen("\"\r\n") + strText.length() + strlen("\x1A");

		pcMsg = new(std::nothrow) CHAR[cmdLen + 1];

		if (pcMsg == NULL) {
			wprintf(L"[ERROR] Cannot allocate memory for final message\n");
			return ARG_ERROR;
		}

		ZeroMemory(pcMsg, cmdLen + 1);

		// Enter text mode
		if (SendCommandAndCheck("AT+CMGF=1\r\n", "OK") <= 0) {
			wprintf(L"[ERROR] Cannot enter TEXT mode\n");
		
			// "Reset" commandline
			SendCommand("\x1A");
			return MODEM_ERROR;
		}

		// AT+CMGS=XX
		sprintf_s(pcMsg, cmdLen + 1, "AT+CMGS=\"+%S\"\r\n", strNumber.c_str());

		if (SendCommandAndCheck(pcMsg, ">") <= 0) {
			wprintf(L"[ERROR] Wrong answer from GSM modem\n");
			wprintf(L"[INFO] Please remind: if you're using a Zadako Modem, the \"AirCard Watcher\" utility MUST be _running_)\n");
			wprintf(L"[INFO] Please remind: if you're using a Sierra Modem, the \"Sierra Wireless Discovery Tool\" MUST be *closed*)\n");
			delete[] pcMsg;
			return NETWORK_ERROR;	
		}

		sprintf_s(pcMsg, cmdLen + 1, "%S\x1A", strText.c_str());

		// Response is a bit variable, so we ignore it
		if (SendCommandAndCheck(pcMsg, "OK") <= 0) {
			//wprintf(L"[ERROR] Wrong answer from GSM modem after text\n");
			//delete[] pcMsg;
			//return FALSE;	
		}

		delete[] pcMsg;
		return MESSAGE_DELIVERED;
	}
	
	// Let's add: GSM 03.40 Header, UDHI Length, WDP Layer and WSP Layer
	if (AddGSMHeaders() == FALSE) {
		return ARG_ERROR;
	}

	wprintf(L"[INFO] Final message created\n");

	// Now we can try to send it: let's enter in PDU mode
	if (SendCommandAndCheck("AT+CMGF=0\r\n", "OK") <= 0) {
		wprintf(L"[ERROR] Cannot enter PDU mode\n");
		
		// "Reset" commandline
		SendCommand("\x1A");
		return MODEM_ERROR;
	}

	wprintf(L"[INFO] PDU mode set\n");

	// Better to enable error mode
	if (SendCommandAndCheck("AT+CMEE=1\r\n", "OK") < 0) {
		wprintf(L"[WARNING] Cannot enable ERROR mode\n");
	}

	wprintf(L"[INFO] Sending message...\n");

	// Let's tell the modem we want to send an SMS
	pcMsg = new(std::nothrow) CHAR[m_uAsciiXmlLen + 1 + 1];

	if (pcMsg == NULL) {
		wprintf(L"[ERROR] Cannot allocate memory for final message\n");
		return MODEM_NETWORK_ERROR;
	}

	if ((m_uAsciiXmlLen >> 1) - 1 > 153) {
		wprintf(L"[ERROR] Message too long to be sent\n");
		delete[] pcMsg;
		return FORMAT_ERROR;
	}

	// AT+CMGS=XX
	sprintf_s(pcMsg, m_uAsciiXmlLen + 2, "AT+CMGS=%d\r", (m_uAsciiXmlLen >> 1) - 1);
	
	if (SendCommandAndCheck(pcMsg, ">") <= 0) {
		wprintf(L"[ERROR] Wrong answer from GSM modem\n");
		delete[] pcMsg;
		return NETWORK_ERROR;	
	}

	// Actual payload 0055....
	sprintf_s(pcMsg, m_uAsciiXmlLen + 2, "%s\x1A", m_pcAsciiXml);

	// Let's set a higher timeout for message sending
	serialObj.SetComReadWaitTime(5000);

	if (SendCommandAndCheck(pcMsg, "OK") <= 0) {
		wprintf(L"[ERROR] Cannot send message\n");
		wprintf(L"[INFO] Please remind: if you're using a Zadako Modem, the \"AirCard Watcher\" utility MUST be _running_)\n");
		wprintf(L"[INFO] Please remind: if you're using a Sierra Modem, the \"Sierra Wireless Discovery Tool\" MUST be *closed*)\n");
		
		// Modem reset
		pcMsg[0] = 0x1A;
		pcMsg[1] = 0;

		SendCommand(pcMsg);
		serialObj.SetComReadWaitTime(200);

		delete[] pcMsg;
		return MODEM_NETWORK_ERROR;
	}

	wprintf(L"[INFO] Message delivered\n");

	delete[] pcMsg;
	serialObj.SetComReadWaitTime(200);
	return MESSAGE_DELIVERED;
}

INT WapPush::SendMessage(PWCHAR pwPort, PWCHAR pwPIN, PWCHAR pwNumber, PCHAR pcXml) {
	wprintf(L"[ERROR] NOT IMPLEMENTED\n");
	return FALSE;
}

// Text (optional) and Link must be already set at this point
BOOL WapPush::BuildWBXML() {
	CHAR *xml_exp = NULL;
	UINT xml_len = 0;
	WBXMLGenWBXMLParams params;
	WBXMLError ret = WBXML_OK;

	/*
	 * - Service Loading
	 * Possible signals:
	 * execute-low, execute-high, cache
	 */	
	CHAR xml_sl[] =
		"<?xml version=\"1.0\"?>"
		"<!DOCTYPE sl PUBLIC \"-//WAPFORUM//DTD SL 1.0//EN\" \"http://www.wapforum.org/DTD/sl.dtd\">"
		"<sl href=\"%S\" action=\"%S\" />"; // Link - Priority

	/*
	 * - Service Indication
	 * Possible signals:
	 * signal-none, signal-low, signal-medium, signal-high, delete
	 */	
	CHAR xml_si[] = 
		"<?xml version=\"1.0\"?>"
		"<!DOCTYPE si PUBLIC \"-//WAPFORUM//DTD SI 1.0//EN\" \"http://www.wapforum.org/DTD/si.dtd\">"
		"<si>"
		"<indication href=\"%S\" action=\"%S\" created=\"%SZ\" si-id=\"1101\">" // Link - Priority - Date
		"%S" // Text
		"</indication>"
		"</si>";

	if (strNumber.empty()) {
		wprintf(L"[ERROR] Number not set cannot build XML\n");
		return FALSE;
	}

	if (strLink.empty()) {
		wprintf(L"[ERROR] Link not set, cannot build XML\n");
		return FALSE;
	}

	if (strService.empty()) {
		wprintf(L"[ERROR] Service not set, cannot build XML\n");
		return FALSE;
	}

	if (strPriority.empty()) {
		wprintf(L"[ERROR] Priority not set, cannot build XML\n");
		return FALSE;
	}

	if (strDate.empty() && !strService.compare(L"si")) {
		wprintf(L"[ERROR] Creation date not set, cannot build XML\n");
		return FALSE;
	}

	// SI Xml
	if (!strService.compare(L"sl")) {
		xml_len = strlen(xml_sl) + strLink.size() + strPriority.size() + 1;
		xml_exp = new(std::nothrow) CHAR[xml_len];

		if (xml_exp == NULL) {
			wprintf(L"[ERROR] Cannot allocate memory for XML\n");
			return FALSE;
		}

		sprintf_s(xml_exp, xml_len, xml_sl, strLink.c_str(), strPriority.c_str());
	} else if (!strService.compare(L"si")) { // SL Xml
		xml_len = strlen(xml_si) + strLink.size() + strPriority.size() + strDate.size() + strText.size() + 1;
		xml_exp = new(std::nothrow) CHAR[xml_len];

		if (xml_exp == NULL) {
			wprintf(L"[ERROR] Cannot allocate memory for XML\n");
			return FALSE;
		}

		sprintf_s(xml_exp, xml_len, xml_si, strLink.c_str(), strPriority.c_str(), strDate.c_str(), strText.c_str());
	} else {
		wprintf(L"[ERROR] Unknown service type\n");
		return FALSE;
	}

	xml_len = strlen(xml_exp);

	params.wbxml_version = WBXML_VERSION_13;
	params.use_strtbl = TRUE;
	params.keep_ignorable_ws = FALSE;

	if (m_pcWbXml != NULL) {
		wbxml_free(m_pcWbXml);
		m_pcWbXml = NULL;
		m_uWbXmlLen = 0;
	}

	ret = wbxml_conv_xml2wbxml_withlen((PUCHAR)xml_exp, xml_len, (UCHAR **)&m_pcWbXml, &m_uWbXmlLen, &params);
	
	delete[] xml_exp;

	if (ret != WBXML_OK) {
		wprintf(L"[ERROR] %s\n", wbxml_errors_string(ret));
		return FALSE;
	} 

	if (WBXMLToAscii() == FALSE) {
		wprintf(L"[ERROR] Error converting WBXML to ASCII\n");
		return FALSE;
	}

	return TRUE;
}

BOOL WapPush::AddGSMHeaders() {
	// Service Loading
	CHAR cHeaders_sl[] =
		"0041000%X91%s0004%02X"		// --> GSM 03.40 Header (length and number in BCD), payload length
		"0B"						// --> UDHI Length
		"05040B84C0020003F00101"	// --> WDP Layer
		"0A060403B081EA"			// --> WSP Layer
		"%s";						// --> WBXML Message

	// Service Indication
	CHAR cHeaders_si[] =
		"0055010%X91%s00F5A7%02X"	// --> GSM 03.40 Header (length and number in BCD), payload length
		"06"						// --> UDHI Length
		"05040B8423F0"				// --> WDP Layer
		"01060403AE81EA"			// --> WSP Layer
		"%s";						// --> WBXML Message

	CHAR *pcMsg;
	UINT uHeaderLen;
	UINT uPayloadLen;

	if (!strService.compare(L"sl")) {
		uHeaderLen = strlen(cHeaders_sl) + 1 + 15 + strlen(m_pcAsciiXml) + 1;
	} else if (!strService.compare(L"si")) {
		uHeaderLen = strlen(cHeaders_si) + 1 + 15 + strlen(m_pcAsciiXml) + 1;
	} else {
		wprintf(L"[ERROR] Unknown service type\n");
		return FALSE;
	}

	pcMsg = new(std::nothrow) CHAR[uHeaderLen];
	ZeroMemory(pcMsg, uHeaderLen);

	if (pcMsg == NULL) {
		wprintf(L"[ERROR] Error allocating memory for GSM headers\n");
		return FALSE;
	}

	// Calculate payload length
	if (!strService.compare(L"sl")) {
		// Service Loading
		uPayloadLen = strlen("0B") + strlen("05040B84C0020003F00101") + strlen("0A060403B081EA") + 
						strlen(m_pcAsciiXml);

		uPayloadLen >>= 1;
		sprintf_s(pcMsg, uHeaderLen, cHeaders_sl, strNumber.size(), cBCDNumber, uPayloadLen, m_pcAsciiXml);

	} else {
		// Service Indication
		uPayloadLen = strlen("06") + strlen("05040B8423F0") + strlen("01060403AE81EA") + strlen(m_pcAsciiXml);
		uPayloadLen >>= 1;

		sprintf_s(pcMsg, uHeaderLen, cHeaders_si, strNumber.size(), cBCDNumber, uPayloadLen, m_pcAsciiXml);
	}

	uHeaderLen--; // Real length

	// Clean-up old ASCII-XML and replace it with the expanded one
	delete[] m_pcAsciiXml;
	m_pcAsciiXml = pcMsg;
	m_uAsciiXmlLen = strlen(m_pcAsciiXml);

	return TRUE;
}

BOOL WapPush::WBXMLToAscii() {
	UINT i, j;

	if (m_pcWbXml == NULL || m_uWbXmlLen == 0) {
		wprintf(L"[ERROR] There is no WBXML to convert\n");
		return FALSE;
	}

	if (m_pcAsciiXml != NULL) {
		delete[] m_pcAsciiXml;
		m_pcAsciiXml = NULL;
		m_uAsciiXmlLen = 0;
	}

	m_uAsciiXmlLen = m_uWbXmlLen * 2 + 1;
	m_pcAsciiXml = new(std::nothrow) CHAR[m_uAsciiXmlLen];

	if (m_pcAsciiXml == NULL) {
		wprintf(L"[ERROR] Error allocating WBXML-ASCII array\n");
		return FALSE;
	}

	m_uAsciiXmlLen--; // Real length
	m_pcAsciiXml[m_uAsciiXmlLen] = 0;

	// Hex to ASCII
	for (i = 0, j = 0; i < m_uWbXmlLen; i++, j += 2) {
		CHAR tmp = (m_pcWbXml[i] & 0xF0) >> 4;
		m_pcAsciiXml[j] = ASCII(tmp);

		tmp = m_pcWbXml[i] & 0x0F;
		m_pcAsciiXml[j + 1] = ASCII(tmp);
	}

	// Cleanup of m_cWbXML buffer
	if (m_pcWbXml != NULL) {
		wbxml_free(m_pcWbXml);
		m_pcWbXml = NULL;
		m_uWbXmlLen = 0;
	}

	return TRUE;
}

BOOL WapPush::CheckModem(PWCHAR pwPort, PWCHAR pwPIN) {
	INT iCheck;

	if (SetPort(pwPort) == FALSE)
		return FALSE;

	if (SetPIN(pwPIN) == FALSE)
		return FALSE;

	// Check AT
	iCheck = SendCommandAndCheck("AT\r", "OK\r");
	
	if (iCheck == 0) {
		wprintf(L"[ERROR] Cannot send command to GSM modem\n");
		return FALSE;	
	}

	if (iCheck < 0) {
		wprintf(L"[ERROR] AT interface is not ready\n");
		return FALSE;	
	}

	wprintf(L"[CHECK] AT interface is ready\n");

	// Let's see if we need a PIN
	iCheck = SendCommandAndCheck("AT+CPIN?\r", "READY");

	if (iCheck == 0) {
		wprintf(L"[ERROR] Cannot send command to GSM modem\n");
		return FALSE;	
	}

	// In case we insert the provided PIN
	if (iCheck < 0) {
		wprintf(L"[INFO] A PIN code is required to operate the SIM card\n");

		if (strPin.empty()) {
			wprintf(L"[ERROR] No PIN has been provided, exiting...\n");
		}

		char cPinBuf[30];
		sprintf_s(cPinBuf, 30, "AT+CPIN=\"%S\"\r", strPin.c_str());

		iCheck = SendCommandAndCheck(cPinBuf, "OK");

		if (iCheck < 0) {
			wprintf(L"[ERROR] Wrong PIN used\n");
			return FALSE;
		}

		if (iCheck == 0) {
			wprintf(L"[ERROR] Error sending PIN\n");
			return FALSE;
		}
	} else {
		wprintf(L"[INFO] No PIN code required to operate SIM card\n");
	}

	SendCommandAndCheck("AT+CPIN?\r", "waiting response");		// PIN status
	SendCommandAndCheck("AT+CREG?\r", "waiting response");		// Network registration status
	SendCommandAndCheck("AT+CSQ\r", "waiting response");		// Signal strength
	SendCommandAndCheck("AT+CGMM\r", "waiting response");		// Producer identification
	SendCommandAndCheck("AT+CGMI\r", "waiting response");		// Model identification
	SendCommandAndCheck("AT+CGMR\r", "waiting response");		// Revision information
	SendCommandAndCheck("AT+CGSN\r", "waiting response");		// Serial number
	SendCommandAndCheck("AT+CIMI\r", "waiting response");		// IMSI
	SendCommandAndCheck("AT+CCID\r", "waiting response");		// SIM EF-CCID
	SendCommandAndCheck("AT+GCAP\r", "waiting response");		// Device capabilities
	SendCommandAndCheck("AT+CPAS\r", "waiting response");		// Mobile equipment activity status
	SendCommandAndCheck("AT+CGCLASS?\r", "waiting response");	// GPRS mobile class
	SendCommandAndCheck("AT+CGATT?\r", "waiting response");		// MT attachment status
	SendCommandAndCheck("AT+CSCA?\r", "waiting response");		// SMSC number

	// Let's check to see if we have a SMSC number set
	iCheck = SendCommandAndCheck("AT+CSCA?\r", "+CSCA: ");
		
	if (iCheck < 0) {
		wprintf(L"[ERROR] SMSC found...\n");
		return FALSE;
	}

	if (iCheck == 0) {
		wprintf(L"[ERROR] Error checking for carrier\n");
		return FALSE;
	}

	wprintf(L"[INFO] SMSC number correctly set\n");

	return TRUE;
}

BOOL WapPush::CheckCOM(PWCHAR pwPort) {
	INT iCheck;

	if (SetPort(pwPort) == FALSE)
		return FALSE;

	if (m_bCOMOpen)
		return TRUE;

	iCheck = SendCommandAndCheck("AT\r", "OK\r");

	if (iCheck == 0) {
		wprintf(L"[ERROR] Cannot send command to GSM modem\n");
		return FALSE;	
	}

	if (iCheck < 0) {
		wprintf(L"[ERROR] AT interface is not ready\n");
		return FALSE;	
	}

	wprintf(L"[CHECK] AT interface is ready\n");
	return TRUE;
}

INT WapPush::SendCommandAndCheck(PCHAR pcCommand, PCHAR pcCheck) {
#define READBUF_SIZE 1024

	INT iWritten = 0, iRead = 0, iCommLen, iCheckLen;
	BYTE *pBuff = NULL;

	if (m_bCOMOpen == FALSE) {
		if (Open() == FALSE)
			return FALSE;
	}

	if (pcCommand == NULL || pcCheck == NULL) {
		wprintf(L"[ERROR] Command or Check incorrectly set\n");
		return FALSE;
	}

	iCommLen = strlen(pcCommand);
	iCheckLen = strlen(pcCheck);

	if (iCommLen == 0 || iCheckLen == 0) {
		wprintf(L"[ERROR] Command or Check cannot have 0 length\n");
		return FALSE;
	}

	iWritten = serialObj.Write((PBYTE)pcCommand, iCommLen);

	if (iWritten < 0) {
		wprintf(L"[ERROR] Command not sent\n");
		return FALSE;
	}

	pBuff = new(std::nothrow) BYTE[READBUF_SIZE];

	if (pBuff == NULL) {
		wprintf(L"[ERROR] Cannot allocate memory for receiving buffer\n");
		return FALSE;
	}

	ZeroMemory(pBuff, READBUF_SIZE);

	iRead = serialObj.Read(pBuff, READBUF_SIZE);

	if (iRead < 0) {
		wprintf(L"[ERROR] Cannot read response\n");
		delete[] pBuff;
		return FALSE;
	}

	pBuff[READBUF_SIZE - 1] = 0;

	// Checking response
	if (strstr((PCHAR)pBuff, pcCheck) == NULL) {
		wprintf(L"[WARNING] Response received: %S\n", pBuff);
		delete[] pBuff;
		return -1;
	}

	delete[] pBuff;
	return TRUE;
}

string WapPush::SendCommandAndGet(PCHAR pcCommand) {
#define READBUF_SIZE 1024

	INT iWritten = 0, iRead = 0, iCommLen;
	BYTE *pBuff = NULL;
	string response = "";

	if (m_bCOMOpen == FALSE) {
		if (Open() == FALSE)
			return response;
	}

	if (pcCommand == NULL) {
		wprintf(L"[ERROR] Command or Check incorrectly set\n");
		return response;
	}

	iCommLen = strlen(pcCommand);

	if (iCommLen == 0) {
		wprintf(L"[ERROR] Command or Check cannot have 0 length\n");
		return response;
	}

	iWritten = serialObj.Write((PBYTE)pcCommand, iCommLen);

	if (iWritten < 0) {
		wprintf(L"[ERROR] Command not sent\n");
		return response;
	}

	pBuff = new(std::nothrow) BYTE[READBUF_SIZE];

	if (pBuff == NULL) {
		wprintf(L"[ERROR] Cannot allocate memory for receiving buffer\n");
		return response;
	}

	ZeroMemory(pBuff, READBUF_SIZE);

	iRead = serialObj.Read(pBuff, READBUF_SIZE);

	if (iRead < 0) {
		wprintf(L"[ERROR] Cannot read response\n");
		delete[] pBuff;
		return response;
	}

	pBuff[READBUF_SIZE - 1] = 0;

	response = (CHAR *)pBuff;

	delete[] pBuff;
	return response;
}

INT WapPush::SendCommand(PCHAR pcCommand) {
	INT iWritten = 0, iCommLen;

	if (m_bCOMOpen == FALSE) {
		if (Open() == FALSE)
			return FALSE;
	}

	if (pcCommand == NULL) {
		wprintf(L"[ERROR] Command or Check incorrectly set\n");
		return FALSE;
	}

	iCommLen = strlen(pcCommand);

	if (iCommLen == 0) {
		wprintf(L"[ERROR] Command or Check cannot have 0 length\n");
		return FALSE;
	}

	iWritten = serialObj.Write((PBYTE)pcCommand, iCommLen);

	if (iWritten < 0) {
		wprintf(L"[ERROR] Command not sent\n");
		return FALSE;
	}

	return TRUE;
}