function GetInfo(infoObject) {
    infoObject.Name = "React Native BLE Quick SDK for Client";
    infoObject.Description = "To auto generate react native BLE API services source from the BLE profile";
    infoObject.Author = "Eric";
    infoObject.Version = "0.0.3";
    infoObject.IsClient = true;
    return infoObject;
}

function RunPlugin(profileData) {

	log("Start React Native BLE Client");

	generateBLEServicesFactorySrc(profileData);
	generateDeviceEventTypesSrc(profileData);
	generateBLEServicesSrc(profileData);
	generateBLEPayloadParser(profileData);
	generateHelloBLESample(profileData);

}

///////////////// source generators ///////////////////////////////////////////

function generateBLEPayloadParser(profileData) {
	var outputSrc = "";

	log(" ***************************************************************************");
	log("    Generating JavaScript source file for BLE Payload parser "  );
	log(" ***************************************************************************");

	outputSrc =   outputSrc +  addLicenseText();

	outputSrc =   outputSrc +
	"//" + "\r\n" +
	"//  PayloadParser.js" + "\r\n" +
	"//" + "\r\n" +
	"//  Autogenerated source" + "\r\n" +
	"//" + "\r\n" +
	"// ---------------------------------" + "\r\n" +
	"//  BLE Payload parser " + "\r\n" +
	"// ---------------------------------" + "\r\n" + "\r\n" +
" 'use strict'" + "\r\n" +

" var Int64LE = require('int64-buffer').Int64LE; " + "\r\n" +

" import DLog from '../common/DLog'; " + "\r\n" +
" const dp =require('./PayloadDefs'); "  + "\r\n" + "\r\n";

for (var x = 0; x < profileData.Services.length; x++)
{
	var service = profileData.Services[x];
	var serviceNameStr =  prepareSrcString(service.Name);
	var svcNameSnakeStr =  prepareSrcStringAsSnakeCase(service.Name);

		for( var y = 0; y < service.Characteristics.length; y++)
        {
			var characteristic = service.Characteristics[y];
			var characteristicNameStr =  prepareSrcString(characteristic.Name);
			var characteristicNameSnakeStr =  prepareSrcStringAsSnakeCase(characteristic.Name);
			var chr_json_filename = "chr_" + characteristicNameSnakeStr;

			outputSrc = outputSrc +
			"  var " + chr_json_filename + "_JSON = require('../device_services/profile/chr/" + chr_json_filename + "'); " + "\r\n" +
			"  var " + chr_json_filename + "_uuid = " + chr_json_filename + "_JSON.Characteristic.uuid; "  + "\r\n" +
			"  if(" + chr_json_filename + "_JSON.Characteristic.Value != undefined ){  "  + "\r\n" +
			"  		var " + chr_json_filename + "_format = " + chr_json_filename + "_JSON.Characteristic.Value.Field.Format; "  + "\r\n" +
			"  } " + "\r\n";

		}
}

	outputSrc =   outputSrc +   "\r\n" +
	" var nPayloadParserObjCnt = 0; " + "\r\n" +
	" var instancePayloadParser = null; " + "\r\n" +

	" const BLE_CHARC_VALUE_MAX_LEN = 20; " + "\r\n" +   "\r\n" +

	" export function getPayloadParserInstance() " + "\r\n" +
	" { " + "\r\n" +

	"     if(nPayloadParserObjCnt > 0 ) " + "\r\n" +
	"     { " + "\r\n" +
	"          DLog.printDebugMsg('Its a singleton class, returning existing instance of PayloadParser class '); " + "\r\n" +
	"         return instancePayloadParser; " + "\r\n" +
	"     } " + "\r\n"  +

	"      nPayloadParserObjCnt++; " + "\r\n" +
	"      DLog.printDebugMsg('PayloadParser Object Count is ' + nPayloadParserObjCnt); " + "\r\n" +

	"     instancePayloadParser = new PayloadParser(); " + "\r\n" +

	"     return instancePayloadParser; " + "\r\n" +

	" } " + "\r\n" +   "\r\n" +

	" class PayloadParser  { " + "\r\n" +

	"     constructor(props){ " + "\r\n" +

	"     } " + "\r\n" +   "\r\n" +

	"     parse(svc_class_name,args) " + "\r\n" +
	"     { " + "\r\n" +
	"         switch(args.characteristic) " + "\r\n" +
	"         {  " + "\r\n";

	for (var x = 0; x < profileData.Services.length; x++)
	{
		var service = profileData.Services[x];
		var serviceNameStr =  prepareSrcString(service.Name);
		var svcNameSnakeStr =  prepareSrcStringAsSnakeCase(service.Name);

			for( var y = 0; y < service.Characteristics.length; y++)
			{
				var characteristic = service.Characteristics[y];
				var characteristicNameStr =  prepareSrcString(characteristic.Name);
				var characteristicNameSnakeStr =  prepareSrcStringAsSnakeCase(characteristic.Name);
				var chr_json_filename = "chr_" + characteristicNameSnakeStr;
				var pkt_chr_name = "pkt_" + characteristicNameSnakeStr;

				outputSrc = outputSrc +
	"			  case "+ chr_json_filename +"_uuid.toUpperCase() : " + "\r\n" +
	"                 DLog.printDebug(this,' Characteristics format is = '+ " +  chr_json_filename + "_format );" + "\r\n" +
	"                 if( " + chr_json_filename + "_format == 'uint8' || " + "\r\n" +
	"                    " + chr_json_filename + "_format == 'uint16' || " + "\r\n" +
	"                    " + chr_json_filename + "_format == 'uint32' || " + "\r\n" +
	"                    " + chr_json_filename + "_format == 'uint64') " + "\r\n" +
	"                 {" + "\r\n" +

	"                     let " + pkt_chr_name + " = {" + "\r\n" +
	"                         "+  characteristicNameStr +" : {" + "\r\n" +
	"                             'FMT':' '," + "\r\n" +
	"                         }" + "\r\n" +
	"                     }" + "\r\n" +

	"                     " + pkt_chr_name + "."+ characteristicNameStr +".FMT = " + chr_json_filename + "_format;" + "\r\n" +

	"                     return this.parseDatapayloadPkt(args.value, " + pkt_chr_name + ");" + "\r\n" +
	"                 }" + "\r\n" +
	"                 else" + "\r\n" +
	"                 {" + "\r\n" +
	"                     if(dp." + pkt_chr_name + " != undefined) {" + "\r\n" +
	"                         return this.parseDatapayloadPkt(args.value,dp." + pkt_chr_name + ");" + "\r\n" +
	"                     }" + "\r\n" +
	"                     break;" + "\r\n" +
	"                 } " + "\r\n";

			}
	}

	outputSrc =   outputSrc +
	"             default : " + "\r\n" +
	"                 return null; " + "\r\n" +

	"         } " + "\r\n" +

	"         return null; " + "\r\n" +
	"     } " + "\r\n" + "\r\n" +

	"     parseDatapayloadPkt(packetArr, datapayloadDef)" + "\r\n" +
	"     { " + "\r\n" +

	"         var packetBytBuf  = this.byteArray2DVByteBuffer(packetArr); " + "\r\n" +
	"         if(packetBytBuf === null) " + "\r\n" +
	"         { " + "\r\n" +
	"             DLog.printDebug(this,'packetBytBuf is NUll '); " + "\r\n" +
	"             return 'parse_error'; " + "\r\n" +
	"         } " + "\r\n" + "\r\n" +

	"         var datapayloadStruct =  {}; " + "\r\n" +
	"         var totaldatapayloadStructKeys =  Object.keys(datapayloadStruct).length; " + "\r\n" +
	"         var totaldatapayloadStructValues =  Object.values(datapayloadStruct).length; " + "\r\n" +

	"         var totalFields = Object.keys(datapayloadDef).length; " + "\r\n" + "\r\n" +
	"         DLog.printDebug(this,'total datapayload Fields =  ' + totalFields + " + "\r\n" +
	"                         ' /totaldatapayloadStructKeys =  ' + totaldatapayloadStructKeys  + " + "\r\n" +
	"                         ' /totaldatapayloadStructValues =  ' + totaldatapayloadStructValues ); " + "\r\n" + "\r\n" +
	"         for (var [keyFieldName, valueFieldDef] of Object.entries(datapayloadDef)) { " + "\r\n" +

	"             datapayloadStruct[keyFieldName] = this.extractData(packetBytBuf, valueFieldDef); " + "\r\n" +
	"         } " + "\r\n" + "\r\n" +

	"         totaldatapayloadStructKeys =  Object.keys(datapayloadStruct).length " + "\r\n" +
	"         totaldatapayloadStructValues =  Object.values(datapayloadStruct).length; " + "\r\n" + "\r\n" +
	"         DLog.printDebug(this,'/totaldatapayloadStructKeys =  ' + totaldatapayloadStructKeys  +  " + "\r\n" +
	"                         ' /totaldatapayloadStructValues =  ' + totaldatapayloadStructValues );  " + "\r\n" +

	"         DLog.printDebug(this,datapayloadStruct);  " + "\r\n" + "\r\n" +
	"         return datapayloadStruct;  " + "\r\n" +
	"     } " + "\r\n" + "\r\n" +
	"     extractData(payloadDataBytBuf, payloadDataFieldDef) " + "\r\n" +
	"     { " + "\r\n" +
	"         let dataPos = payloadDataFieldDef.POS || 0; " + "\r\n" +
	"         let dataType = payloadDataFieldDef.FMT || 'uint8'; " + "\r\n" +
	"         let dataLenInBytes = payloadDataFieldDef.LEN || 1; " + "\r\n" + "\r\n" +
	"         DLog.printDebug(this,' payloadDataFieldDef =  ' + dataPos + '/' + dataType + '/' + dataLenInBytes); " + "\r\n" + "\r\n" +

	"         switch (dataType) { " + "\r\n" +
	"             case 'uint8': " + "\r\n" +
	"                 return payloadDataBytBuf.getUint8(dataPos, true); // LITTLE_ENDIAN  " + "\r\n" +
	"             case 'uint16': " + "\r\n" +
	"                 return payloadDataBytBuf.getUint16(dataPos, true); " + "\r\n" +
	"             case 'uint32': " + "\r\n" +
	"                 return payloadDataBytBuf.getUint32(dataPos, true); " + "\r\n" +
	"             case 'uint64': " + "\r\n" +
	"                 return null; " + "\r\n" +
	"             case 'string_ascii': " + "\r\n" +
	"                 return this.extractStringData(payloadDataBytBuf, dataPos, dataLenInBytes) " + "\r\n" +
	"             default: " + "\r\n" +
	"                 return null; " + "\r\n" +
	"         } " + "\r\n" +
	"     } " + "\r\n" + "\r\n" +
	"     extractStringData(payloadDataBytBuf, keyvalueSeekPos, keyvalueLen) { " + "\r\n" +
	"         var keyvalueStr = ''; " + "\r\n" +

	"         if(keyvalueLen > BLE_CHARC_VALUE_MAX_LEN) " + "\r\n" +
	"         { " + "\r\n" +
	"             keyvalueLen = BLE_CHARC_VALUE_MAX_LEN; " + "\r\n" +
	"         } " + "\r\n" + "\r\n" +

	"         var keyvalueSeekPos = 0; " + "\r\n" +
	"         for(var m=0;m<keyvalueLen;m++) " + "\r\n" +
	"         { " + "\r\n" +

	"              var keyvaluebyte     = payloadDataBytBuf.getUint8(keyvalueSeekPos, true); " + "\r\n" +
	"              keyvalueStr = keyvalueStr + String.fromCharCode(keyvaluebyte); " + "\r\n" +

	"              DLog.printDebug(this, 'keyvalueStr= ' + keyvalueStr); " + "\r\n" +
	"              keyvalueSeekPos++; " + "\r\n" +
	"         } " + "\r\n" +
	"         DLog.printDebug(this, 'keyvalueStr= ' + keyvalueStr); " + "\r\n" +

	"         return keyvalueStr; " + "\r\n" +

	"      }" + "\r\n" +  "\r\n" +

	"     byteArray2DVByteBuffer(byteArray) " + "\r\n" +
	"     { " + "\r\n" +
	"         var byteArrayLen = byteArray.length; " + "\r\n" + "\r\n" +
	"         if(byteArrayLen < 1) " + "\r\n" +
	"         { " + "\r\n" +
	"             DLog.printDebug(this,'packet byte arr size is zero = ' + byteArrayLen); " + "\r\n" +
	"             return null; " + "\r\n" +
	"         } " + "\r\n" + "\r\n" +

	"         DLog.printDebug(this,'packet byte arr size = ' + byteArrayLen); " + "\r\n" + "\r\n" +

	"         var objUint8Array   = new Uint8Array(new ArrayBuffer(byteArrayLen)); " + "\r\n" +

	"         objUint8Array.set(byteArray); " + "\r\n" + "\r\n" +

	"         var dvByteBuf  = new DataView(objUint8Array.buffer); " + "\r\n" +

	"         for(var m=0;m<byteArrayLen;m++) " + "\r\n" +
	"         { " + "\r\n" +

	"             DLog.printDebug(this,'DVByteBuf = ' + dvByteBuf.getUint8(m)); " + "\r\n" +
	"         } " + "\r\n" +

	"         return dvByteBuf; " + "\r\n" +
	"     } " + "\r\n" +

	"   } " + "\r\n";

	FileManager.CreateFile(".\\protocols\\PayloadParser.js", outputSrc);

	log("PayloadParser.js generated sucessfully	" );
	log(" ---------------------------------------------------------------------");

}

function generateBLEServicesFactorySrc(profileData) {
	var outputSrc = "";

	log(" ***************************************************************************");
	log("    Generating JavaScript source file for all BLE service factory for " + profileData.CustomNameSpace );
	log(" ***************************************************************************");

    outputSrc =   outputSrc +  addLicenseText();

	outputSrc =   outputSrc +
	"'use strict'" + "\r\n" +
	"import DLog from '../common/DLog';" + "\r\n";

	for (var x = 0; x < profileData.Services.length; x++)
	{
			var service = profileData.Services[x];
			var serviceObjNameStr =  prepareSrcString(service.Name) + "SvcObj";

			outputSrc = outputSrc + "import " + serviceObjNameStr + " from '../device_services/" + serviceObjNameStr + "';" + "\r\n";

	}
	outputSrc = outputSrc + "\r\n" + "\r\n" + "\r\n";

	outputSrc =   outputSrc +
	" export default class DevServiceFactory  { " + "\r\n" + "\r\n" +
	"    constructor (props ){" + "\r\n" + "\r\n" +
	"    }" + "\r\n" + "\r\n" +
	" static createService (nSvcTpye, objBLECommMgr, connectedPeripheralID  ) {" + "\r\n" + "\r\n" +
	"    DLog.printDebug (this, 'createService: ' + nSvcTpye + ' for connectedPeripheralID = ' + connectedPeripheralID );" + "\r\n" + "\r\n" +
	"    switch  (nSvcTpye ) {" + "\r\n";

	for (var x = 0; x < profileData.Services.length; x++)
	{
			var service = profileData.Services[x];
			var serviceNameStr =  prepareSrcString(service.Name);
			var serviceObjNameStr =  serviceNameStr + "SvcObj";

			outputSrc =   outputSrc +
			"		case '" + serviceNameStr + "':" + "\r\n" +
			"			" + serviceObjNameStr + ".initSvc (objBLECommMgr,connectedPeripheralID );" + "\r\n" +
			"			return " +  serviceObjNameStr + ";" + "\r\n";

	}

	outputSrc =   outputSrc +
	" 		default:" + "\r\n" +
	"    			return null;" + "\r\n" +
	" 		} " + "\r\n" +

	" 	}" + "\r\n" +

	" }" + "\r\n";

	FileManager.CreateFile(".\\sdk_api\\DevServiceFactory.js", outputSrc);

	log("DevServiceFactory.js generated sucessfully	" );
	log(" ---------------------------------------------------------------------");
}

function generateDeviceEventTypesSrc(profileData) {
	var outputSrc = "";

	log(" ***************************************************************************");
	log("    Generating JavaScript source file for all BLE device events for " + profileData.CustomNameSpace );
	log(" ***************************************************************************");

    outputSrc =   outputSrc +  addLicenseText();

	outputSrc =   outputSrc +
	"//" + "\r\n" +
	"//  DeviceEventTypes.js" + "\r\n" +
	"//" + "\r\n" +
	"//  Autogenerated source" + "\r\n" +
	"//" + "\r\n" +
	"// ---------------------------------" + "\r\n" +
	"// Device Event Types " + "\r\n" +
	"// ---------------------------------" + "\r\n" + "\r\n" +

	"const DeviceEventTypes = { " + "\r\n" +
	"	kDeviceFound  				: 'TBleAdminDeviceFound'," + "\r\n" +
	"	kBleConnectionStateChanged  : 'TBleAdminConnectionStateChanged'," + "\r\n" +
	"	kBleConnectionLost  		: 'TBleAdminConnectionLost'," + "\r\n" +
	"	kScanFinished  				: 'TBleAdminScanFinished'," + "\r\n" +
	"	kNativeLibLoad 				: 'TNativeLibLoad'," + "\r\n" +
	"	kRSSIValue  				: 'TRSSIValue'," + "\r\n";

	for (var x = 0; x < profileData.Services.length; x++)
	{
		var service = profileData.Services[x]
		for( var y = 0; y < service.Characteristics.length; y++)
        {
            var characteristic = service.Characteristics[y];
			var characteristicNameStr =  prepareSrcString(characteristic.Name);

			outputSrc = outputSrc + "	k" + characteristicNameStr + "                :  'T" + characteristicNameStr+ "'," + "\r\n";

		}
	}

	outputSrc = outputSrc +
	"}" + "\r\n" + "\r\n" +
	"export default DeviceEventTypes;"

	FileManager.CreateFile(".\\sdk_api\\DeviceEventTypes.js", outputSrc);

	log("DeviceEventTypes.js generated sucessfully	" );
	log(" ---------------------------------------------------------------------");
}

function generateBLEServicesSrc(profileData) {
	var outputSrc = "";

	log(" ***************************************************************************");
	log("     Generating JavaScript source file for all BLE services file for " + profileData.CustomNameSpace );
	log(" ***************************************************************************");

for (var x = 0; x < profileData.Services.length; x++)
{
	var service = profileData.Services[x];
	var serviceNameStr =  prepareSrcString(service.Name);
	var serviceObjNameStr =  serviceNameStr + "SvcObj";
	var svcNameSnakeStr =  prepareSrcStringAsSnakeCase(service.Name);
	var svc_json_filename = "svc_" + svcNameSnakeStr;

	outputSrc = "";
	outputSrc =   outputSrc +  addLicenseText();

		outputSrc =   outputSrc +
		"//" + "\r\n" +
		"//  " + serviceObjNameStr + ".js" + "\r\n" +
		"//" + "\r\n" +
		"//  Autogenerated source" + "\r\n" +
		"//" + "\r\n" +
		"// ---------------------------------" + "\r\n" +
		"// " + serviceNameStr + " Service functions " + "\r\n" +
		"// ---------------------------------" + "\r\n" + "\r\n" +

		"  'use strict' " + "\r\n" +

		"  import DLog from '../common/DLog'; " + "\r\n" +
		"  import AExecutor from '../common/AsyncExecutorObj'; " + "\r\n" +
		"  import { getSDKServiceMgrInstance} from '../sdk_api/SDKServiceMgr'; " + "\r\n" +
		"  import { DeviceEvents } from '../sdk_api/DevServiceMgr'; " + "\r\n" +
		"  import { getUtilsInstance} from '../common/utils/Utils'; " + "\r\n" +
		"  import { NativeAppEventEmitter } from 'react-native';  " + "\r\n" + "\r\n" +

		"  const NodeEvents = require('events'); " + "\r\n" +
		"  const " + svc_json_filename + "_JSON = require('./profile/svc/" + svc_json_filename + "'); " + "\r\n" +
		"  const svc_uuid = " + svc_json_filename + "_JSON.Service.uuid; " + "\r\n";

		for( var y = 0; y < service.Characteristics.length; y++)
        {
			var characteristic = service.Characteristics[y];
			var characteristicNameStr =  prepareSrcString(characteristic.Name);
			var characteristicNameSnakeStr =  prepareSrcStringAsSnakeCase(characteristic.Name);
			var chr_json_filename = "chr_" + characteristicNameSnakeStr;

			outputSrc = outputSrc +
			"  const " + chr_json_filename + "_JSON = require('./profile/chr/" + chr_json_filename + "'); " + "\r\n" +
			"  const " + chr_json_filename + "_uuid = " + chr_json_filename + "_JSON.Characteristic.uuid; " + "\r\n";

		}

	outputSrc = outputSrc + "\r\n" + "\r\n" + "\r\n";

	outputSrc =   outputSrc +
		"  const MAX_LISTENERS_PER_EVENT = 1; " + "\r\n" +

		"  var n" + serviceNameStr + "SvcObjCnt = 0; " + "\r\n" +

		"  /** " + "\r\n" +
		"  * @private " + "\r\n" +
		"  * This class provides interface to all the characteristics of " + serviceNameStr+ " service " + "\r\n" +
		"  * that is defined in the BLE profile of a device through Bluetooth developer studio. " + "\r\n" +
		"  * @extends NodeEvents. " + "\r\n" +
		"  */ " + "\r\n" +
		"  class "+ serviceNameStr +"Svc extends NodeEvents { " + "\r\n" +

		"     /** " + "\r\n" +
		"      * @private " + "\r\n" +
		"      */ " + "\r\n" +
		"     constructor(){ " + "\r\n" +
		"         super(); " + "\r\n" +

		"         // Member variables  " + "\r\n" +
		"         this.bInitSvcDone = false; " + "\r\n" +
		"         this.connectedPeripheralID = null; " + "\r\n" +
		"         this.objBLECommMgr =  null; " + "\r\n" +
		"         this.objUtils = getUtilsInstance(); " + "\r\n" +
		"         this.objDataPayloadParser  = null; " + "\r\n" +
		" 		this.maxListenersPerEvent = MAX_LISTENERS_PER_EVENT; " + "\r\n" +
		" 		this.setMaxListeners(this.maxListenersPerEvent); " + "\r\n" +

		"         n"+ serviceNameStr +"SvcObjCnt++; " + "\r\n" +
		"         DLog.printDebugMsg('"+ serviceNameStr +"Svc Object Count is ' + n"+ serviceNameStr +"SvcObjCnt); " + "\r\n" +

		"  		this.setNativeListeners();  " + "\r\n" +
		"     } " + "\r\n" +  "\r\n" +

		"     /** " + "\r\n" +
		"      * @private " + "\r\n" +
		"      */ " + "\r\n" +
		"  	setNativeListeners() " + "\r\n" +
		"  	{ " + "\r\n" +
		"  		DLog.printDebug(this,'RN Eric setNativeListeners'); " + "\r\n" +

		"  		NativeAppEventEmitter.addListener('BleAdminDidUpdateValueForCharacteristic', " + "\r\n" +
		"  											(args) => { " + "\r\n" +
		"  												this.handleReadEvent(args); " + "\r\n" +
		"  											} " + "\r\n" +
		"  											); " + "\r\n" +
		"  	} " + "\r\n" +  "\r\n" +

		"     /** " + "\r\n" +
		"      * @private " + "\r\n" +
		"      */	 " + "\r\n" +
		"     initSvc(objBLECommMgr,connectedPeripheralID) " + "\r\n" +
		"     { " + "\r\n" +
		"         this.bInitSvcDone = true; " + "\r\n" +
		"         this.connectedPeripheralID = connectedPeripheralID; " + "\r\n" +
		"         this.objBLECommMgr =  objBLECommMgr; " + "\r\n" +
		"         this.objDataPayloadParser  = getSDKServiceMgrInstance().getProtocolParser(); " + "\r\n" +

		"  		let lstnCnt = 0; " + "\r\n";

		for( var y = 0; y < service.Characteristics.length; y++)
        {
			var characteristic = service.Characteristics[y];
			var characteristicNameStr =  prepareSrcString(characteristic.Name);
			var characteristicNameSnakeStr =  prepareSrcStringAsSnakeCase(characteristic.Name);
			var chr_json_filename = "chr_" + characteristicNameSnakeStr;

			outputSrc = outputSrc +
			"  		lstnCnt = this.listenerCount(DeviceEvents.k"+characteristicNameStr+");  " + "\r\n" +
			"  		DLog.printDebug(this,'"+ serviceNameStr +" svc initialized: k"+ characteristicNameStr+" listener count =  ' + lstnCnt); " + "\r\n";

		}

		outputSrc =   outputSrc +
		"     } " + "\r\n" + "\r\n" +

		"     /** " + "\r\n" +
		"      * @private " + "\r\n" +
		"      */		 " + "\r\n" +
		"     handleReadEvent(args) " + "\r\n" +
		" 	{ " + "\r\n" +
		"         if(!this.bInitSvcDone ) " + "\r\n" +
		"         { " + "\r\n" +
		"             DLog.printDebug(this,' Service NOT initialized. ' ); " + "\r\n" +
		"             return; " + "\r\n" +
		"         } " + "\r\n" +
		"         DLog.printDebug(this,' handleReadEvent ' + args.characteristic + ' ' + args.value ); " + "\r\n" +

		"         switch(args.characteristic) " + "\r\n" +
		"         {  " + "\r\n";

		for( var y = 0; y < service.Characteristics.length; y++)
        {
			var characteristic = service.Characteristics[y];
			var characteristicNameStr =  prepareSrcString(characteristic.Name);
			var characteristicNameSnakeStr =  prepareSrcStringAsSnakeCase(characteristic.Name);
			var chr_json_filename = "chr_" + characteristicNameSnakeStr;

			outputSrc = outputSrc +
			"             case "+ chr_json_filename +"_uuid.toUpperCase() : " + "\r\n" +
			" 					if(this.objDataPayloadParser != null )  " + "\r\n" +
			" 					{ " + "\r\n" +
			" 						 let parsedData = this.objDataPayloadParser.parse(this.constructor.name, args); " + "\r\n" +
			" 						 if( parsedData != undefined && parsedData != null) " + "\r\n" +
			" 						 { " + "\r\n" +
			" 								let arrParsedDataValues = Object.values(parsedData); " + "\r\n" +
			" 								arrParsedDataValues.splice(0, 0, DeviceEvents.k"+ characteristicNameStr +"); " + "\r\n" +
			" 								DLog.printDebug(this,'Array of Parsed Data Values' + arrParsedDataValues + ' - with length = ' + arrParsedDataValues.length); " + "\r\n" +
			" 								this.emit.apply(this, arrParsedDataValues); " + "\r\n" +
			" 						 } " + "\r\n" +
			" 						 else " + "\r\n" +
			" 							this.emit(DeviceEvents.k"+ characteristicNameStr +", args.value); " + "\r\n" +

			" 					} " + "\r\n" +
			" 					else " + "\r\n" +
			" 					{ " + "\r\n" +
			" 						this.emit(DeviceEvents.k"+ characteristicNameStr +", args.value); " + "\r\n" +
			" 					} " + "\r\n" +
			" 					break; " + "\r\n";

		}

		outputSrc =   outputSrc +
		"         } " + "\r\n" +
		"     } " + "\r\n";

		outputSrc = outputSrc + "\r\n" + "\r\n" + "\r\n";

		for( var y = 0; y < service.Characteristics.length; y++)
        {
			var characteristic = service.Characteristics[y];
			var characteristicNameStr =  prepareSrcString(characteristic.Name);
			var characteristicNameSnakeStr =  prepareSrcStringAsSnakeCase(characteristic.Name);
			var chr_json_filename = "chr_" + characteristicNameSnakeStr;

			outputSrc = outputSrc +
			"     /** " + "\r\n" +
			"      * @public " + "\r\n" +
			"      * This function initiates request to read " + characteristicNameSnakeStr + " from device. " + "\r\n" +
			"      * @param {callback} autoReleasableListener - This callback will be called one time after device " + "\r\n" +
			"      * returns the " + characteristicNameSnakeStr + " value asynchronously. API consumer apps should set this callback to receive " + characteristicNameSnakeStr + ". " + "\r\n" +
			"      * If this callback is null (not set by app) then the app can't receive " + characteristicNameSnakeStr + ". Apps need not to release " + "\r\n" +
			"      * the callback as the SDK automatically releases the reference to callback after callback is called. " + "\r\n" +
			"      * @returns {Promise} - Resolves the promise if API call is success, rejects the promise if API call is failed " + "\r\n" +
			"      */		 " + "\r\n" +
			"     get_" + characteristicNameSnakeStr + "(autoReleasableListener) {         " + "\r\n" +
			"         DLog.printDebug(this,'RN Eric get_" + characteristicNameSnakeStr + "'); " + "\r\n" +
			"         if(autoReleasableListener != undefined) " + "\r\n" +
			"         { " + "\r\n" +
			"             this.once(DeviceEvents.k" + characteristicNameStr + ",autoReleasableListener); " + "\r\n" +

			"             let lstnCnt = this.listenerCount(DeviceEvents.k" + characteristicNameStr + "); " + "\r\n" +
			"             DLog.printDebug(this,'get_" + characteristicNameSnakeStr + " : k" + characteristicNameStr + " listener added, listener count =  ' + lstnCnt); " + "\r\n" +
			"         } " + "\r\n" +

			"  				return AExecutor.AsyncReadAsByteArray(this.objBLECommMgr, this.connectedPeripheralID,svc_uuid, chr_" + characteristicNameSnakeStr + "_uuid); " + "\r\n" +

			"     }  " + "\r\n";

		}

		outputSrc = outputSrc + "\r\n" + "\r\n" + "\r\n";

		for( var y = 0; y < service.Characteristics.length; y++)
        {
			var characteristic = service.Characteristics[y];
			var characteristicNameStr =  prepareSrcString(characteristic.Name);
			var characteristicNameSnakeStr =  prepareSrcStringAsSnakeCase(characteristic.Name);
			var chr_json_filename = "chr_" + characteristicNameSnakeStr;

			outputSrc = outputSrc +
			"     /** " + "\r\n" +
			"      * @public " + "\r\n" +
			"      * This function request to device to start sending indication " + characteristicNameSnakeStr + ". " + "\r\n" +
			"      * @param {boolean} bEnable - Set true/gfalse to start / stop indication. " + "\r\n" +
			"      * @param {callback} listener - This callback will be called one time after device " + "\r\n" +
			"      * returns the " + characteristicNameSnakeStr + " value asynchronously. API consumer apps should set this callback to receive " + characteristicNameSnakeStr + ". " + "\r\n" +
			"      * If this callback is null (not set by app) then the app can't receive " + characteristicNameSnakeStr + ". Apps must release " + "\r\n" +
			"      * the callback when it is not needed anymore.  " + "\r\n" +
			"      * @returns {Promise} - Resolves the promise if API call is success, rejects the promise if API call is failed " + "\r\n" +
			"      */		 " + "\r\n" +
			"     indicate_" + characteristicNameSnakeStr + "(bEnable, listener) {         " + "\r\n" +
			"  		if(bEnable) " + "\r\n" +
			"  		{ " + "\r\n" +
			"     		DLog.printDebug(this,' indicate_" + characteristicNameSnakeStr + "'); " + "\r\n" +

			"     		if(listener != undefined) " + "\r\n" +
			"     		{ " + "\r\n" +
			"         		let lstnCnt = this.listenerCount(DeviceEvents.k" + characteristicNameStr + "); " + "\r\n" +
			"         		DLog.printDebug(this,'indicate_" + characteristicNameSnakeStr + " : k" + characteristicNameStr + " listener added, listener count =  ' + lstnCnt); " + "\r\n" +
			"         		if(lstnCnt <  this._maxListeners) " + "\r\n" +
			"         		{ " + "\r\n" +
			"         		    this.on(DeviceEvents.k" + characteristicNameStr + ",listener); " + "\r\n" +
			"         		} " + "\r\n" +
			"         		else " + "\r\n" +
			"         		{ " + "\r\n" +
			"         		    DLog.printDebug(this,'Max listeners limit reached'); " + "\r\n" +
			"             		return AExecutor.throwAsyncException('Exception: Max listeners limit reached'); " + "\r\n" +
			"         		}					 " + "\r\n" +

			"     		} " + "\r\n" +

			"  			return AExecutor.AsyncIndicateAsByteArray(this.objBLECommMgr, this.connectedPeripheralID,svc_uuid, chr_" + characteristicNameSnakeStr + "_uuid); " + "\r\n" +
			"  		} " + "\r\n" +
			"  		else " + "\r\n" +
			"  		{ " + "\r\n" +

			"     		DLog.printDebug(this,' STOP indicate_" + characteristicNameSnakeStr + "'); " + "\r\n" +
			"     		return AExecutor.AsyncIndicateStop(this.objBLECommMgr, this.connectedPeripheralID,svc_uuid, chr_" + characteristicNameSnakeStr + "_uuid) " + "\r\n" +
			"  		} " + "\r\n" +

			"     }  " + "\r\n";

		}

		outputSrc = outputSrc + "\r\n" + "\r\n" + "\r\n";

		for( var y = 0; y < service.Characteristics.length; y++)
        {
			var characteristic = service.Characteristics[y];
			var characteristicNameStr =  prepareSrcString(characteristic.Name);
			var characteristicNameSnakeStr =  prepareSrcStringAsSnakeCase(characteristic.Name);
			var chr_json_filename = "chr_" + characteristicNameSnakeStr;

			outputSrc = outputSrc +
			"     /** " + "\r\n" +
			"      * @public " + "\r\n" +
			"      * This function initiates request to read " + characteristicNameSnakeStr + " from device. " + "\r\n" +
			"      * @param  {uint_8} " + characteristicNameSnakeStr + " - Value to set. " + "\r\n" +
			"      * @returns {Promise} - Resolves the promise if API call is success, rejects the promise if API call is failed " + "\r\n" +
			"      */		 " + "\r\n" +
			"     set_" + characteristicNameSnakeStr + "(" + characteristicNameSnakeStr + ") {         " + "\r\n" +
			"  		DLog.printDebug(this,' set_" + characteristicNameSnakeStr + "'); " + "\r\n" +

			"  		let " + characteristicNameSnakeStr + "ByteArray = this.objUtils.convert8BitInt2ByteArray(" + characteristicNameSnakeStr + "); " + "\r\n" +

			"  		return AExecutor.AsyncWrite(this.objBLECommMgr, this.connectedPeripheralID,svc_uuid, chr_" + characteristicNameSnakeStr + "_uuid, " + characteristicNameSnakeStr + "ByteArray); " + "\r\n" +

			"     }  " + "\r\n";

		}

		outputSrc = outputSrc + "\r\n" + "\r\n" + "\r\n";

		outputSrc =   outputSrc +
		"  	registerListener(event, listener) { " + "\r\n" +

		"  		if(listener != undefined) " + "\r\n" +
		"  		{ " + "\r\n" +

		"  			let lstnCnt = this.listenerCount(event); " + "\r\n" +
		"  			DLog.printDebug(this,'before register new listener for event = ' + event + 'listener count = ' + lstnCnt); " + "\r\n" +
		"  			if(lstnCnt < this._maxListeners) " + "\r\n" +
		"  			{ " + "\r\n" +
		"  				this.on(event,listener); " + "\r\n" +
		"  				lstnCnt = this.listenerCount(event); " + "\r\n" +
		"  				DLog.printDebug(this,'register new listener for event = ' + event + 'listener count = ' + lstnCnt); " + "\r\n" +
		"  				return true; " + "\r\n" +
		"  			} " + "\r\n" +
		"  			else " + "\r\n" +
		"  			{ " + "\r\n" +
		"  				DLog.printDebug(this,'Max listeners limit reached'); " + "\r\n" +
		"  				return AExecutor.throwAsyncException('Exception: Max listeners limit reached'); " + "\r\n" +
		"  			} " + "\r\n" +
		"  		} " + "\r\n" +
		"  		return false; " + "\r\n" +

		"  	} " + "\r\n";

		outputSrc = outputSrc + "\r\n" + "\r\n" + "\r\n";

		for( var y = 0; y < service.Characteristics.length; y++)
        {
			var characteristic = service.Characteristics[y];
			var characteristicNameStr =  prepareSrcString(characteristic.Name);
			var characteristicNameSnakeStr =  prepareSrcStringAsSnakeCase(characteristic.Name);
			var chr_json_filename = "chr_" + characteristicNameSnakeStr;

			outputSrc = outputSrc +
			"     /** " + "\r\n" +
			"      * @public " + "\r\n" +
			"      * This function releases the reference of the callback set by notification category of APIs. " + "\r\n" +
			"      * @param {callback} listener reference to callback to be released. " + "\r\n" +
			"      * @returns Nothing " + "\r\n" +
			"      */	 " + "\r\n" +
			"  release" + characteristicNameStr + "Listener(listener) " + "\r\n" +
			"  { " + "\r\n" +
			" 	let lstnCnt = this.listenerCount(DeviceEvents.k" + characteristicNameStr + " ); " + "\r\n" +
			" 	if(lstnCnt >= 1) " + "\r\n" +
			" 	{ " + "\r\n" +
			" 		this.removeAllListeners(DeviceEvents.k" + characteristicNameStr + " ); " + "\r\n" +
			" 	} " + "\r\n" +

			" 	lstnCnt = this.listenerCount(DeviceEvents.k" + characteristicNameStr + " ); " + "\r\n" +
			" 	DLog.printDebug(this,'After release:k" + characteristicNameStr + "  listener count = ' + lstnCnt); " + "\r\n" +
			"  }  " + "\r\n";

		}

		outputSrc = outputSrc + "\r\n" + "\r\n" + "\r\n";
		outputSrc =   outputSrc +
		"     /** " + "\r\n" +
		"      * @public " + "\r\n" +
		"      * This function releases the reference of the callback set by notification category of APIs. " + "\r\n" +
		"      * @param {callback} listener reference to callback to be released. " + "\r\n" +
		"      * @returns Nothing " + "\r\n" +
		"      */		 " + "\r\n" +
		"     releaseListener(listener) " + "\r\n" +
		"     { " + "\r\n" +

		"     } " + "\r\n" +

		"  } " + "\r\n" +

		"  module.exports = new " + serviceNameStr +"Svc();  " + "\r\n";

		outputSrc = outputSrc + "\r\n" + "\r\n" + "\r\n";

	var svcFilename = ".\\device_services\\" + serviceObjNameStr + ".js";
	FileManager.CreateFile(svcFilename, outputSrc);

	log("" + serviceObjNameStr + ".js generated sucessfully" );
	log(" ---------------------------------------------------------------------");
}

	log(" ---------------------------------------------------------------------");
	log("Generated JavaScript source file for all BLE services	" );
	log(" ---------------------------------------------------------------------");
}

function generateHelloBLESample(profileData) {
	var outputSrc = "";

	log(" ***************************************************************************");
	log("    Generating JavaScript source file for BLE Hello World example  " );
	log(" ***************************************************************************");

    outputSrc =   outputSrc +  addLicenseText();

	outputSrc =   outputSrc +
	"//" + "\r\n" +
	"//  BLEHelloWorldView.js" + "\r\n" +
	"//" + "\r\n" +
	"//  Autogenerated source" + "\r\n" +
	"//" + "\r\n" +
	"// ---------------------------------" + "\r\n" +
	"// BLEHelloWorldView " + "\r\n" +
	"// ---------------------------------" + "\r\n" + "\r\n" +

	" 'use strict' " + "\r\n" +

	" import React, {Component} from 'react'; " + "\r\n" +
	" import { " + "\r\n" +
	"     Text, " + "\r\n" +
	"     View, " + "\r\n" +
	"     Alert, " + "\r\n" +
	"     StyleSheet, " + "\r\n" +
	"     ScrollView " + "\r\n" +
	" } from 'react-native'; " + "\r\n" +

	" import Button from 'apsl-react-native-button' " + "\r\n" +
	" import Toast from 'react-native-simple-toast'; " + "\r\n" +
	" import globalVars from '../common/globals'; " + "\r\n" +
	" import {k} from '../screens/AppConstants'; " + "\r\n" +

	" export default class BLEHelloWorldView extends Component { " + "\r\n" + "\r\n" +

	"     constructor(props) { " + "\r\n" +
	"         super(props); " + "\r\n" +

	"     } " + "\r\n" + "\r\n" +

	"     async scanNearbyBleDevices() { " + "\r\n" +

	"			let listenerScan = (deviceID, deviceName, rssi) => {" + "\r\n" +
	"				console.log(' Device Found iOS CBUUID = ' + deviceID + '/' + deviceName + '[' + rssi + ']');" + "\r\n" +
	" 			};" + "\r\n" +

	"         try { " + "\r\n" +
	"             await globalVars.objSDKSvcMgr.getDevAdmin().getDeviceScanner().scanAllServices(k.BLE_SCAN_TIMEOUT, listenerScan); " + "\r\n" +
	"             Toast.show('Initiated scanning task successfully'); " + "\r\n" +
	"         } " + "\r\n" +
	"         catch (err) { " + "\r\n" +
	"             Toast.show('Failed to start scanning task'); " + "\r\n" +
	"         } " + "\r\n" +

	"     } " + "\r\n" + "\r\n" +

	"     async connect2BleDevice() { " + "\r\n" +

	"         try { " + "\r\n" +
	"             await globalVars.objSDKSvcMgr.getDevAdmin().getDeviceAccessor().connectToDevice(k.deviceId); " + "\r\n" +
	"             Toast.show('device connected'); " + "\r\n" +
	"         } " + "\r\n" +
	"         catch (err) { " + "\r\n" +
	"             Toast.show('device connection failed'); " + "\r\n" +
	"         } " + "\r\n" +

	"     } " + "\r\n" + "\r\n";

	for (var x = 0; x < profileData.Services.length; x++)
	{
		var service = profileData.Services[x];
		var serviceNameStr =  prepareSrcString(service.Name);
		for( var y = 0; y < service.Characteristics.length; y++)
        {
            var characteristic = service.Characteristics[y];
			var characteristicNameStr =  prepareSrcString(characteristic.Name);
			var characteristicNameSnakeStr =  prepareSrcStringAsSnakeCase(characteristic.Name);

			outputSrc = outputSrc +
			"\r\n" +
	"     async read"+ characteristicNameStr +"() { " + "\r\n" + "\r\n" +

	"         let listener" +  characteristicNameStr + " = ("+ characteristicNameStr +") => { " + "\r\n" +
	"             Alert.alert(' "+ characteristicNameStr + " is ' + "+ characteristicNameStr +"); " + "\r\n" +
	"         }; " + "\r\n" + "\r\n" +

	"         try { " + "\r\n" +
	"             await globalVars.objDevSvcMgr.getService('"+ serviceNameStr +"').get_"+ characteristicNameSnakeStr +"(listener"+ characteristicNameStr +"); " + "\r\n" +
	"             Toast.show('read"+ characteristicNameStr +" api call success'); " + "\r\n" +
	"         } " + "\r\n" +
	"         catch (err) { " + "\r\n" +
	"             Toast.show('read"+ characteristicNameStr +" api call failed'); " + "\r\n" +
	"         } " + "\r\n" + "\r\n" +

	"     } "  + "\r\n"  + "\r\n";

		}
	}

	outputSrc = outputSrc +
	"     render() { " + "\r\n" +
	"         return ( " + "\r\n" +
	"             <ScrollView style={styles.page}> " + "\r\n" +
	"                 <Text style={styles.tabText}>BLE Hello World </Text> " + "\r\n" +

	"                 <Button " + "\r\n" +
	"                     style={styles.Button} " + "\r\n" +
	"                     onPress={ () => { " + "\r\n" +

	"                         this.scanNearbyBleDevices(); " + "\r\n" +

	"                     } " + "\r\n" +
	"                     }> " + "\r\n" +
	"                     1-Scan " + "\r\n" +
	"                 </Button> " + "\r\n" +

	"                 <Button " + "\r\n" +
	"                     style={styles.Button} " + "\r\n" +

	"                     onPress={ () => { " + "\r\n" +

	"                         this.connect2BleDevice(); " + "\r\n" +

	"                     } " + "\r\n" +
	"                     }> " + "\r\n" +
	"                     2-Connect " + "\r\n" +
	"                 </Button> " + "\r\n";

	for (var x = 0; x < profileData.Services.length; x++)
	{
		var service = profileData.Services[x]
		for( var y = 0; y < service.Characteristics.length; y++)
        {
            var characteristic = service.Characteristics[y];
			var characteristicNameStr =  prepareSrcString(characteristic.Name);

			outputSrc = outputSrc +
	"                 <Button " + "\r\n" +
	"                     style={styles.Button} " + "\r\n" +
	"                     onPress={ () => { " + "\r\n" +
	"                         this.read" + characteristicNameStr + "(); " + "\r\n" +

	"                     } " + "\r\n" +
	"                     }> " + "\r\n" +
	"                     3-Read " + characteristicNameStr + " " + "\r\n" +
	"                 </Button> " + "\r\n";

		}
	}

	outputSrc =   outputSrc +
	"             </ScrollView> " + "\r\n" +
	"         ); " + "\r\n" +
	"     } " + "\r\n" +
	" } " + "\r\n" +

	" var styles = StyleSheet.create({ " + "\r\n" +
	"	  page: {" + "\r\n" +
	"		  backgroundColor: '#edf0f5'," + "\r\n" +
	"		  flex: 1," + "\r\n" +
	"	  }," + "\r\n" +
	"     tabContent: { " + "\r\n" +
	"         flex: 1, " + "\r\n" +
	"         justifyContent: 'center', " + "\r\n" +
	"         alignItems: 'center', " + "\r\n" +
	"         backgroundColor: 'white', " + "\r\n" +
	"     }, " + "\r\n" +
	"     tabText: { " + "\r\n" +
	"         color: 'black', " + "\r\n" +
	"         margin: 5, " + "\r\n" +
	"    }, " + "\r\n" +
	"     Button: { " + "\r\n" +
	"         backgroundColor: 'green', " + "\r\n" +
	"         margin: 30 " + "\r\n" +
	"     }, " + "\r\n" +
	"     Button2: { " + "\r\n" +
	"         backgroundColor: 'red', " + "\r\n" +
	"         margin: 30 " + "\r\n" +
	"     } " + "\r\n" +
	" }); " + "\r\n" ;

	FileManager.CreateFile(".\\example_apps\\HelloBLE\\app\\screens\\BLEHelloWorldView.js", outputSrc);

	//////////////////////////////////////////////////// Next File app.json //////////////////////////////////////

	var appName = prepareAppNameString(profileData.CustomNameSpace  , profileData.ProfileName);
    outputSrc =   "";

	outputSrc =   outputSrc +
	"{" + "\r\n" +
	"  \"name\": \"" + appName + "\"," + "\r\n" +
	"  \"displayName\": \"" + appName + "\"" + "\r\n" +
	"}" + "\r\n" ;

	FileManager.CreateFile(".\\example_apps\\HelloBLE\\app.json", outputSrc);

	//////////////////////////////////////////////////// Next File index.ios.js  //////////////////////////////////////

	outputSrc =   "";

	outputSrc =   outputSrc +
	"//" + "\r\n" +
	"//  index.ios.js" + "\r\n" +
	"//" + "\r\n" +
	"//  Autogenerated source" + "\r\n" +
	"//" + "\r\n" + "\r\n" +
	" import React, {Component} from 'react';" + "\r\n" +
	" import {" + "\r\n" +
	"     AppRegistry," + "\r\n" +
	"     Alert" + "\r\n" +
	" } from 'react-native';" + "\r\n" +

	" import { Navigator } from 'react-native-deprecated-custom-components';" + "\r\n" +
	" import {k} from './app/screens/AppConstants';" + "\r\n" +
	" import strings from './app/values/localization/strings';" + "\r\n" +
	" import globalVars from './app/common/globals';" + "\r\n" +
	" import BLEHelloWorldView from './app/screens/BLEHelloWorldView';" + "\r\n" + "\r\n" +

	" var globalNavigatorProps;" + "\r\n" + "\r\n" +

	" export default class HelloBLE extends Component {" + "\r\n" + "\r\n" +

	"     constructor() {" + "\r\n" +
	"         super();" + "\r\n" +

	"         this.initDeviceSDK();" + "\r\n" +

	"     }" + "\r\n" + "\r\n" +

	"     initDeviceSDK() {" + "\r\n" +

	"         globalVars.objDevSvcMgr = globalVars.objSDKSvcMgr.getDeviceSvcMgr(false);" + "\r\n" +
	"         if (globalVars.objDevSvcMgr == null || globalVars.objDevSvcMgr == undefined) {" + "\r\n" +
	"             return false;" + "\r\n" +
	"         }" + "\r\n" +
	"         else" + "\r\n" +
	"             return true;" + "\r\n" +

	"     }" + "\r\n" + "\r\n" +

	"     _renderScene(route, navigator) {" + "\r\n" +
	"         globalNavigatorProps = {navigator}" + "\r\n" +
	"         switch (route.ident) {" + "\r\n" +

	"             case 'BLEHelloWorldPage':" + "\r\n" +
	"                 return (" + "\r\n" +
	"                     <BLEHelloWorldView  {...globalNavigatorProps} />" + "\r\n" +
	"                 );" + "\r\n" +

	"         }" + "\r\n" +
	"     }" + "\r\n" + "\r\n" +

	"     render() {" + "\r\n" +
	"         return (" + "\r\n" +
	"             <Navigator" + "\r\n" +
	"                 initialRoute={{ident: 'BLEHelloWorldPage'}}" + "\r\n" +
	"                 ref='appNavigator'" + "\r\n" +
	"                 renderScene={this._renderScene}" + "\r\n" +

	"             />" + "\r\n" +
	"         );" + "\r\n" +
	"     }" + "\r\n" + "\r\n" +

	" }" + "\r\n" +
	" AppRegistry.registerComponent('" + appName + "', () => HelloBLE);" + "\r\n" ;

	FileManager.CreateFile(".\\example_apps\\HelloBLE\\index.ios.js", outputSrc);
	log("BLE Hello World example generated sucessfully	" );
	log(" ---------------------------------------------------------------------");
}

///////////////////// Utilities /////////////////////////////

function add2Src(output, srcLine)
{
    return output + srcLine + "\r\n" ;
}

function NewLine()
{
    return "\r\n";
}

function prepareAppNameString(s1, s2){
	var search1 =  "com.";
	var result1 = s1.replace(search1, "");
	result1 = spaceToUpperCamelCase(result1);

	var search2 =  "profile";
	var search3 =  "Profile";
	var result2 = s2.replace(search2, "");
	result2 = result2.replace(search3, "");
	result2 = removeHyphen(result2);
	result2 = replaceUnderscoreWithSpace(result2);
	result2 = spaceToUpperCamelCase(result2);

	var result = result1 + result2;
	result = result.trim();
	log('prepareAppNameString = ' + result);

	return  "Hello" + result;
}

function prepareSrcString(s){
	var srcStr = s.toLowerCase();
	srcStr = removeHyphen(srcStr);
	srcStr = replaceUnderscoreWithSpace(srcStr);
	srcStr = spaceToUpperCamelCase(srcStr);
    return srcStr;
}

function prepareSrcStringAsSnakeCase(s){
	var srcStr = s.toLowerCase();
	srcStr = removeHyphen(srcStr);
	srcStr = spaceToSnakeCase(srcStr);
    return srcStr;
}

function snakeToLowerCamel(s){
    return s.replace(/(\_\w)/g, function(m){return m[1].toUpperCase();});
}

function kebabToLowerCamel(s){
    return s.replace(/(\-\w)/g, function(m){return m[1].toUpperCase();});
}

function spaceToSnakeCase(s){
    return s.replace(/(\ \w)/g, function(m){return "_" + m[1];});
}

function spaceToLowerCamelCase(s){
    return s.replace(/(\ \w)/g, function(m){return m[1].toUpperCase();});
}

function spaceToUpperCamelCase(s){
	s = " " + s;
    return s.replace(/(\ \w)/g, function(m){return m[1].toUpperCase();});
}

function removeHyphen(s){
    return s.replace(/(\-\w)/g, function(m){
		return m[1].toUpperCase();
		});
}

function replaceUnderscoreWithSpace(s){
    return s.replace(/(\_\w)/g, function(m){
		return " " + m[1].toUpperCase();
		});
}

function addLicenseText()
{
    return " " + "\r\n" +
	"/* " + "\r\n" +
 " * React Native BLE Quick SDK with autogenerated source " + "\r\n" +
 " *" + "\r\n" +
 " * " + "\r\n" +
 " * Copyright (c) 2017 Ybrain Inc. " + "\r\n" +
 " * Author Eric S ( https://github.com/eric2036 ) " + "\r\n" +
 " * Licensed under the Apache License, Version 2.0 (the 'License'); " + "\r\n" +
 " * you may not use this file except in compliance with the License. " + "\r\n" +
 " * You may obtain a copy of the License at " + "\r\n" +
 " *    http://www.apache.org/licenses/LICENSE-2. " + "\r\n" +
 " * Unless required by applicable law or agreed to in writing, software " + "\r\n" +
 " * distributed under the License is distributed on an 'AS IS' BASIS, " + "\r\n" +
 " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. " + "\r\n" +
 " * See the License for the specific language governing permissions and " + "\r\n" +
 " * limitations under the License. " + "\r\n" +
 " * " + "\r\n" +
 " */ " + "\r\n" + "\r\n" ;

}


const path = require('path')
const StateLint = require('../statelint/statelint')
const LineReader = require('n-readlines')

class Cli {
  constructor (
    stateLint = new StateLint(),
    description = 'Validates Amazon State Language files'
  ) {
    this.stateLint = stateLint
    this.description = description
  } // constructor

  process (args = process.argv) {
    if (showIfHelp(args, this.description)) return true

    const files = args.slice(2)
    return validateFiles(files, this.stateLint)
  } // process
} // class Cli

/// ////////////////////////
function validateFiles (files, stateLint) {
  let ok = true

  for (const file of files) {
    try {
      ok = validate(file, stateLint) && ok
    } catch (err) {
      console.log(`ERROR: ${file} os not valid\n\t${err.message}`)
      ok = false
    }
  } // for ...

  return ok
} // validateFiles

function validate (file, stateLint) {
  const json = readAndParse(file)
  const problems = stateLint.validate(json)
  if (problems.length) {
    console.log(`ERROR: ${file} is not valid`)
    problems.forEach(p => console.log(`\t${p}`))
  }

  return (problems.length === 0)
} // validate

function readAndParse (jsonSource) {
  const lines = [...readlines(jsonSource)].join('\n')
  return JSON.parse(lines)
} // readAndParse

function * readlines (source) {
  const reader = new LineReader(source)
  let line
  while ((line = reader.next())) {
    yield line.toString('utf-8')
  }
} // readlines

function showIfHelp (args, description) {
  const name = path.basename(args[1])
  const opts = args.slice(2)

  if (!(opts.length === 1 && ['--help', '-h', '-?'].includes(opts[0]))) {
    return false
  }

  console.log(`Usage: ${name} ...[state-machine-spec]`)
  console.log('')
  console.log(description)
  return true
} // showIfHelp

module.exports = (stateLint, description) => new Cli(stateLint, description)