/** given a dialstring, determines and executes correct action 
Copyright 2009 Palm, Inc.  All rights reserved.

**/
/*
 * DialStringParser: Looks for USSD, MMI and pound-pound combinations within the dial string
 * Written by Peter Easton (if you have questions or bugs)
 */

var DialAction = {
	UNKNOWN: 0,
	DIAL: 1,
	MMI: 2,
	TTY: 3,
	VPON: 4,
	VPOFF: 5,
	AKEY: 6,
	EVDO: 7,
	PREV: 8,
	GPS: 9,
	MSL: 10,
	INCALLMMI: 11,
	RTN: 12,
	DEBUG: 13,
	DATA: 14,
	CROTEST: 15,
	CDTEST: 16,
	PPTEST: 17,
	VOICEMAIL: 18,
	STICKYDIALPAD: 19,
	DEBOUNCED: 20,
	OMADM: 21,
	LAUNCHEXTERNAL: 22,
	ODD: 24,
	RADIO: 26,
	OFF: 27,
	MASK: 28,
	AUDEQON: 29,
	AUDEQOFF: 30
};

var ExternalApp = {
	'8011183': 'collectlogs', // based on QWERTY: c011ect 
	'5647': 'collectlogs',
	'8727277': 'usbpassthrough',
	'387': 'ftp',
	'66338': 'ondevlog',
	'43574357': 'deviceinfo',
	'66623': 'monad'
};

var DialStringParser = {
	/****** CONSTANTS ******/
   	launchcodesCommon: {
		'8378': DialAction.CROTEST,
		'6346843277':	DialAction.CDTEST,
		'2355273667': DialAction.PPTEST,
		'8327633696': DialAction.STICKYDIALPAD, 
		'8011183': DialAction.LAUNCHEXTERNAL, // based on QWERTY: c011ect 
		'8727277': DialAction.LAUNCHEXTERNAL, // USBPASS
		'889': DialAction.TTY,
		'5647': DialAction.LAUNCHEXTERNAL, // LOGS
		'3366': DialAction.ODD, // OnDeviceDemo: DEMO
		'43574357': DialAction.LAUNCHEXTERNAL, // HELPHELP
		'72346': DialAction.RADIO,
		'633': DialAction.OFF,
		'7436581': DialAction.MASK, // SID6581: i adore my 64
		'477': DialAction.GPS,
		'387': DialAction.LAUNCHEXTERNAL, // FTP
		'66338': DialAction.LAUNCHEXTERNAL, // ONDEV,
		'66623': DialAction.LAUNCHEXTERNAL, // MONAD
		'2833766': DialAction.AUDEQON,
		'28337633': DialAction.AUDEQOFF
	},
	
	launchcodesCDMA: {
	    '8766': DialAction.VPON,
	    '87633': DialAction.VPOFF,
	    '2539' : DialAction.AKEY,
	    '3836': DialAction.EVDO,
	    '7738': DialAction.PREV,
		'786': DialAction.RTN,
		'33284': DialAction.DEBUG,
		'3282': DialAction.DATA,
		'66236': DialAction.OMADM
	},
	
	/* largely a series of tables mapping MMI codes onto service methods */
    /* Peter E. has the universe's eternal thanks for typing this in. */
    
    // This is the service code object.  There are 5 possible actions (activate, deactivate, register, unregister and 
    // interrogate) based on the prefix of the number entered by the user. The Supplementary Information is parsed
    // and stored as si1, si2, si3 and si4.  The bearer services (infoClass) corresponding to these siX values are also
    // stored.  These values are melded into the commands before they are sent. 
    serviceCodes: {
        "04": {
            register: {
                cmd: "pin1Change",
                oldPin: "#{si[1]}",
                newPin: "#{si[2]}",
                newPinConfirm: "#{si[3]}"
            }
        },
        "042": {
            register: {
                cmd: "pin2Change",
                oldPin: "#{si[1]}",
                newPin: "#{si[2]}",
                newPinConfirm: "#{si[3]}"
            }
        },
        "05": {
            register: {
                cmd: "pin1Reset",
                puk: "#{si[1]}",
                newPin: "#{si[2]}",
                newPinConfirm: "#{si[3]}"
            }
        },
        "052": {
            register: {
                cmd: "pin2Reset",
                puk2: "#{si[1]}",
                newPin2: "#{si[2]}",
                newPinConfirm: "#{si[3]}"
            }
        },
        "002": {
            activate: {
                cmd: "forwardActivate",
                condition: "allforwarding",
                bearer: "#{ic[2]}",
                activate: true
            },
            deactivate: {
                cmd: "forwardActivate",
                condition: "allforwarding",
                bearer: "#{ic[2]}",
                activate: false
            },
            register: {
                cmd: "forwardRegister",
                number: "#{si[1]}",
                condition: "allforwarding",
                bearer: "#{ic[2]}",
                time: "#{si[3]}"
            },
            unregister: {
                cmd: "forwardRegister",
                number: "",
                condition: "allforwarding",
                bearer: "#{ic[2]}",
                time: "#{si[3]}"
            },
            interrogate: {
                cmd: "forwardQuery",
                condition: "allforwarding",
                bearer: "#{ic[2]}"
            }
        },
        "004": {
            activate: {
                cmd: "forwardActivate",
                condition: "allconditional",
                bearer: "#{ic[2]}",
                activate: true
            },
            deactivate: {
                cmd: "forwardActivate",
                condition: "allconditional",
                bearer: "#{ic[2]}",
                activate: false
            },
            register: {
                cmd: "forwardRegister",
                number: "#{si[1]}",
                condition: "allconditional",
                bearer: "#{ic[2]}",
                time: "#{si[3]}"
            },
            unregister: {
                cmd: "forwardRegister",
                number: "",
                condition: "allconditional",
                bearer: "#{ic[2]}",
                time: "#{si[3]}"
            },
            interrogate: {
                cmd: "forwardQuery",
                condition: "allconditional",
                bearer: "#{ic[2]}"
            }
        },
        "21": {
            activate: {
                cmd: "forwardActivate",
                condition: "unconditional",
                bearer: "#{ic[2]}",
                activate: true
            },
            deactivate: {
                cmd: "forwardActivate",
                condition: "unconditional",
                bearer: "#{ic[2]}",
                activate: false
            },
            register: {
                cmd: "forwardRegister",
                number: "#{si[1]}",
                condition: "unconditional",
                bearer: "#{ic[2]}",
                time: "#{si[3]}"
            },
            unregister: {
                cmd: "forwardRegister",
                number: "",
                condition: "unconditional",
                bearer: "#{ic[2]}",
                time: "#{si[3]}"
            },
            interrogate: {
                cmd: "forwardQuery",
                condition: "unconditional",
                bearer: "#{ic[2]}"
            }
        },
        "61": {
            activate: {
                cmd: "forwardActivate",
                condition: "noreply",
                bearer: "#{ic[2]}",
                activate: true
            },
            deactivate: {
                cmd: "forwardActivate",
                condition: "noreply",
                bearer: "#{ic[2]}",
                activate: false
            },
            register: {
                cmd: "forwardRegister",
                number: "#{si[1]}",
                condition: "noreply",
                bearer: "#{ic[2]}",
                time: "#{si[3]}"
            },
            unregister: {
                cmd: "forwardRegister",
                number: "",
                condition: "noreply",
                bearer: "#{ic[2]}",
                time: "#{si[3]}"
            },
            interrogate: {
                cmd: "forwardQuery",
                condition: "noreply",
                bearer: "#{ic[2]}"
            }
        },
        "62": {
            activate: {
                cmd: "forwardActivate",
                condition: "unreachable",
                bearer: "#{ic[2]}",
                activate: true
            },
            deactivate: {
                cmd: "forwardActivate",
                condition: "unreachable",
                bearer: "#{ic[2]}",
                activate: false
            },
            register: {
                cmd: "forwardRegister",
                number: "#{si[1]}",
                condition: "unreachable",
                bearer: "#{ic[2]}",
                time: "#{si[3]}"
            },
            unregister: {
                cmd: "forwardRegister",
                number: "",
                condition: "unreachable",
                bearer: "#{ic[2]}",
                time: "#{si[3]}"
            },
            interrogate: {
                cmd: "forwardQuery",
                condition: "unreachable",
                bearer: "#{ic[2]}"
            }
        },
        "67": {
            activate: {
                cmd: "forwardActivate",
                condition: "mobilebusy",
                bearer: "#{ic[2]}",
                activate: true
            },
            deactivate: {
                cmd: "forwardActivate",
                condition: "mobilebusy",
                bearer: "#{ic[2]}",
                activate: false
            },
            register: {
                cmd: "forwardRegister",
                number: "#{si[1]}",
                condition: "mobilebusy",
                bearer: "#{ic[2]}",
                time: "#{si[3]}"
            },
            unregister: {
                cmd: "forwardRegister",
                number: "",
                condition: "mobilebusy",
                bearer: "#{ic[2]}",
                time: "#{si[3]}"
            },
            interrogate: {
                cmd: "forwardQuery",
                condition: "mobilebusy",
                bearer: "#{ic[2]}"
            }
        },
        "43": {
            activate: {
                cmd: "callWaitingSet",
                bearer: "#{ic[1]}",
                enable: true
            },
            deactivate: {
                cmd: "callWaitingSet",
                bearer: "#{ic[1]}",
                enable: false
            },
            register: {
                cmd: "callWaitingSet",
                bearer: "#{ic[1]}",
                enable: true
            },
            unregister: {
                cmd: "callWaitingSet",
                bearer: "#{ic[1]}",
                enable: false
            },
            interrogate: {
                cmd: "callWaitingQuery",
                bearer: "#{ic[1]}"
            }
        },
        "03": {
            activate: {
                cmd: "barringPasswordChange",
                condition: "#{bs[1]}",
                oldpassword: "#{si[2]}",
                newpassword: "#{si[3]}",
                newpasswordconfirm: "#{si[4]}"
            },
            register: {
                cmd: "barringPasswordChange",
                condition: "#{bs[1]}",
                oldpassword: "#{si[2]}",
                newpassword: "#{si[3]}",
                newpasswordconfirm: "#{si[4]}"
            }
        },
        "33": {
            activate: {
                cmd: "barringSet",
                condition: "baralloutgoing",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: true
            },
            deactivate: {
                cmd: "barringSet",
                condition: "baralloutgoing",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: false
            },
            register: {
                cmd: "barringSet",
                condition: "baralloutgoing",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: true
            },
            unregister: {
                cmd: "barringSet",
                condition: "baralloutgoing",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: false
            },
            interrogate: {
                cmd: "barringQuery",
                condition: "baralloutgoing",
                bearer: "#{ic[2]}"
            }
        },
        "331": {
            activate: {
                cmd: "barringSet",
                condition: "baroutgoingint",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: true
            },
            deactivate: {
                cmd: "barringSet",
                condition: "baroutgoingint",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: false
            },
            register: {
                cmd: "barringSet",
                condition: "baroutgoingint",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: true
            },
            unregister: {
                cmd: "barringSet",
                condition: "baroutgoingint",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: false
            },
            interrogate: {
                cmd: "barringQuery",
                condition: "baroutgoingint",
                bearer: "#{ic[2]}"
            }
        },
        "332": {
            activate: {
                cmd: "barringSet",
                condition: "baroutgoingintextohome",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: true
            },
            deactivate: {
                cmd: "barringSet",
                condition: "baroutgoingintextohome",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: false
            },
            register: {
                cmd: "barringSet",
                condition: "baroutgoingintextohome",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: true
            },
            unregister: {
                cmd: "barringSet",
                condition: "baroutgoingintextohome",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: false
            },
            interrogate: {
                cmd: "barringQuery",
                condition: "baroutgoingintextohome",
                bearer: "#{ic[2]}"
            }
        },
        "35": {
            activate: {
                cmd: "barringSet",
                condition: "barallincoming",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: true
            },
            deactivate: {
                cmd: "barringSet",
                condition: "barallincoming",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: false
            },
            register: {
                cmd: "barringSet",
                condition: "barallincoming",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: true
            },
            unregister: {
                cmd: "barringSet",
                condition: "barallincoming",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: false
            },
            interrogate: {
                cmd: "barringQuery",
                condition: "barallincoming",
                bearer: "#{ic[2]}"
            }
        },
        "351": {
            activate: {
                cmd: "barringSet",
                condition: "barincomingroaming",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: true
            },
            deactivate: {
                cmd: "barringSet",
                condition: "barincomingroaming",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: false
            },
            register: {
                cmd: "barringSet",
                condition: "barincomingroaming",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: true
            },
            unregister: {
                cmd: "barringSet",
                condition: "barincomingroaming",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: false
            },
            interrogate: {
                cmd: "barringQuery",
                condition: "barincomingroaming",
                bearer: "#{ic[2]}"
            }
        },
        "330": {
            activate: {
                cmd: "barringSet",
                condition: "barallbarring",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: true
            },
            deactivate: {
                cmd: "barringSet",
                condition: "barallbarring",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: false
            },
            register: {
                cmd: "barringSet",
                condition: "barallbarring",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: true
            },
            unregister: {
                cmd: "barringSet",
                condition: "barallbarring",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: false
            },
            interrogate: {
                cmd: "barringQuery",
                condition: "barallbarring",
                bearer: "#{ic[2]}"
            }
        },
        "333": {
            activate: {
                cmd: "barringSet",
                condition: "baroutgoing",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: true
            },
            deactivate: {
                cmd: "barringSet",
                condition: "baroutgoing",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: false
            },
            register: {
                cmd: "barringSet",
                condition: "baroutgoing",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: true
            },
            unregister: {
                cmd: "barringSet",
                condition: "baroutgoing",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: false
            },
            interrogate: {
                cmd: "barringQuery",
                condition: "baroutgoing",
                bearer: "#{ic[2]}"
            }
        },
        "353": {
            activate: {
                cmd: "barringSet",
                condition: "barincoming",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: true
            },
            deactivate: {
                cmd: "barringSet",
                condition: "barincoming",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: false
            },
            register: {
                cmd: "barringSet",
                condition: "barincoming",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: true
            },
            unregister: {
                cmd: "barringSet",
                condition: "barincoming",
                bearer: "#{ic[2]}",
                password: "#{si[1]}",
                enable: false
            },
            interrogate: {
                cmd: "barringQuery",
                condition: "barincoming",
                bearer: "#{ic[2]}"
            }
        },
        "30": {
            activate: {
                cmd: "clipSet",
                restrict: false
            },
            deactivate: {
                cmd: "clipSet",
                restrict: true
            },
            interrogate: {
                cmd: "clipQuery"
            }
        },
        "31": {
            activate: {
                cmd: "clirSet",
                restrict: true
            },
            deactivate: {
                cmd: "clirSet",
                restrict: false
            },
            register: {
                cmd: "clirSet",
                restrict: true
            },
            unregister: {
                cmd: "clirSet",
                restrict: false
            },
            interrogate: {
                cmd: "clirQuery"
            }
        },
        "300": {
            interrogate: {
                cmd: "cnapQuery"
            }
        },
        "06": {
            interrogate: {
                cmd: "imeiQuery"
            }
        },
    },
    
    // The responses to the MMI codes are interpreted.  The messages are listed in the order that the parameters should be
    // looked at.  Once a match is found, processing is stopped and the message is displayed.  You can use regular
    // expressions to match values (look at "04" below").  Values from both the user entered data (siX and icX) and
    // the returned JSON object are available for comparision and interpolation. 
    mmiResponses: {
        "04": {
            register: {
                returnValue: {
                    "true": $L("PIN change successful.")
                },
                errorCode: {
                    5: $L("PIN must be enabled before change is allowed.")
                },
                attemptsRemaining: {
                    "[0-9]*": $L("Unable to change PIN. #{attemptsRemaining} attempts remaining.")
                },
                serviceCode: {
                    "04": $L("Unable to change PIN.")
                }
            }
        },
        "042": {
            register: {
                returnValue: {
                    "true": $L("PIN2 change successful.")
                },
                errorCode: {
                    5: $L("PIN2 must be enabled before change is allowed.")
                },
                attemptsRemaining: {
                    "[0-9]*": $L("Unable to change PIN2. #{attemptsRemaining} attempts remaining.")
                },
                serviceCode: {
                    "042": $L("Unable to change PIN2.")
                }
            }
        },
        "05": {
            register: {
                returnValue: {
                    "true": $L("PIN reset successful.")
                },
                attemptsRemaining: {
                    "[0-9]*": $L("PIN reset failed. #{attemptsRemaining} attempts remaining.")
                },
                errorCode: {
                    ".*": $L("Unable to reset PIN.")
                }
            }
        },
        "052": {
            register: {
                returnValue: {
                    "true": $L("PIN2 reset successful.")
                },
                attemptsRemaining: {
                    "[0-9]*": $L("Unable to reset PIN2. #{attemptsRemaining} attempts remaining.")
                },
                errorCode: {
                    ".*": $L("Unable to reset PIN2.")
                }
            }
        },
        "002": {
            activate: {
                returnValue: {
                    "true": $L("Call forwarding activated for all-forwarding: #{ic[2]}."),
                    "false": $L("Call forwarding activation failed. #{errorText}")
                }
            },
            deactivate: {
                returnValue: {
                    "true": $L("Call forwarding deactivated for all-forwarding: #{ic[2]}."),
                    "false": $L("Call forwarding deactivation failed. #{errorText}")
                }
            },
            register: {
                returnValue: {
                    "true": $L("Call forwarding registered for #{si[1]} for all-forwarding: #{ic[2]}."),
                    "false": $L("Call forwarding registration failed. #{errorText}")
                }
            },
            unregister: {
                returnValue: {
                    "true": $L("Call forwarding unregistered for all-forwarding: #{ic[2]}."),
                    "false": $L("Call forwarding unregistration failed. #{errorText}")
                }
            },
            interrogate: {
                returnValue: {
                    "true": $L("Call forwarding (all-forwarding) status: #{status}"),
					"false": $L("Operation failed: Call forwarding (all-forwarding) query. #{errorText}")
                },
            }
        },
        "004": {
            activate: {
                returnValue: {
                    "true": $L("Call forwarding activated for all-conditional: #{ic[2]}."),
                    "false": $L("Call forwarding activation failed. #{errorText}")
                }
            },
            deactivate: {
                returnValue: {
                    "true": $L("Call forwarding deactivated for all-conditional: #{ic[2]}."),
                    "false": $L("Call forwarding deactivation failed. #{errorText}")
                }
            },
            register: {
                returnValue: {
                    "true": $L("Call forwarding registered for #{si[1]} for all-conditional: #{ic[2]}."),
                    "false": $L("Call forwarding registration failed. #{errorText}")
                }
            },
            unregister: {
                returnValue: {
                    "true": $L("Call forwarding unregistered for all-conditional: #{ic[2]}."),
                    "false": $L("Call forwarding unregistration failed. #{errorText}")
                }
            },
            interrogate: {
                returnValue: {
                   	"true": $L("Call forwarding (all-conditional) status: #{status}"),
				    "false": $L("Operation failed: Call forwarding (all-conditional). #{errorText}")
                },
                
            }
        },
        "21": {
            activate: {
                returnValue: {
                    "true": $L("Call forwarding activated for unconditional: #{ic[2]}."),
                    "false": $L("Call forwarding activation failed. #{errorText}")
                }
            },
            deactivate: {
                returnValue: {
                    "true": $L("Call forwarding deactivated for unconditional: #{ic[2]}."),
                    "false": $L("Call forwarding deactivation failed. #{errorText}")
                }
            },
            register: {
                returnValue: {
                    "true": $L("Call forwarding registered for #{si[1]} for unconditional: #{ic[2]}."),
                    "false": $L("Call forwarding registration failed. #{errorText}")
                }
            },
            unregister: {
                returnValue: {
                    "true": $L("Call forwarding unregistered for unconditional: #{ic[2]}."),
                    "false": $L("Call forwarding unregistration failed. #{errorText}")
                }
            },
            interrogate: {
                returnValue: {
                    "true": $L("Call forwarding (unconditional) status: #{status}"),
					"false": $L("Operation failed: Call forwarding (unconditional) #{errorText}")
                },
            }
        },
        "61": {
            activate: {
                returnValue: {
                    "true": $L("Call forwarding activated for no reply: #{ic[2]}."),
                    "false": $L("Call forwarding activation failed. #{errorText}")
                }
            },
            deactivate: {
                returnValue: {
                    "true": $L("Call forwarding deactivated for no reply: #{ic[2]}."),
                    "false": $L("Call forwarding deactivation failed. #{errorText}")
                }
            },
            register: {
                returnValue: {
                    "true": $L("Call forwarding registered for #{si[1]} for no reply: #{ic[2]}."),
                    "false": $L("Call forwarding registration failed. #{errorText}")
                }
            },
            unregister: {
                returnValue: {
                    "true": $L("Call forwarding unregistered for no reply: #{ic[2]}."),
                    "false": $L("Call forwarding unregistration failed. #{errorText}")
                }
            },
            interrogate: {
                returnValue: {
                    "true": $L("Call forwarding (no reply) status: #{status}"),
					"false": $L("Operation failed: Call forwarding (no reply). #{errorText}")
                },
            }
        },
        "62": {
            activate: {
                returnValue: {
                    "true": $L("Call forwarding activated for unreachable: #{ic[2]}."),
                    "false": $L("Call forwarding activation failed. #{errorText}")
                }
            },
            deactivate: {
                returnValue: {
                    "true": $L("Call forwarding deactivated for unreachable: #{ic[2]}."),
                    "false": $L("Call forwarding deactivation failed. #{errorText}")
                }
            },
            register: {
                returnValue: {
                    "true": $L("Call forwarding registered for #{si[1]} for unreachable: #{ic[2]}."),
                    "false": $L("Call forwarding registration failed. #{errorText}")
                }
            },
            unregister: {
                returnValue: {
                    "true": $L("Call forwarding unregistered for unreachable: #{ic[2]}."),
                    "false": $L("Call forwarding unregistration failed. #{errorText}")
                }
            },
            interrogate: {
                returnValue: {
                    "true": $L("Call forwarding (unreachable) status: #{status}"),
					"false": $L("Operation failed: Call forwarding (unreachable) query. #{errorText}")
                },
            }
        },
        "67": {
            activate: {
                returnValue: {
                    "true": $L("Call forwarding activated for mobile busy: #{ic[2]}."),
                    "false": $L("Call forwarding activation failed. #{errorText}")
                }
            },
            deactivate: {
                returnValue: {
                    "true": $L("Call forwarding deactivated for mobile busy: #{ic[2]}."),
                    "false": $L("Call forwarding deactivation failed. #{errorText}")
                }
            },
            register: {
                returnValue: {
                    "true": $L("Call forwarding registered for #{si[1]} for mobilebusy: #{ic[2]}."),
                    "false": $L("Call forwarding registration failed. #{errorText}")
                }
            },
            unregister: {
                returnValue: {
                    "true": $L("Call forwarding unregistered for mobile busy: #{ic[2]}."),
                    "false": $L("Call forwarding unregistration failed. #{errorText}")
                }
            },
            interrogate: {
                returnValue: {
					"true": $L("Call forwarding (mobile busy) status: #{status}"),
                    "false": $L("Operation failed: Call forwarding (mobile busy) query. #{errorText}")
                },
            }
        },
        "43": {
            activate: {
                returnValue: {
                    "true": $L("Call waiting is enabled for #{ic[1]}."),
                    "false": $L("Call waiting activation failed. #{errorText}")
                }
            },
            deactivate: {
                returnValue: {
                    "true": $L("Call waiting is disabled for #{ic[1]}."),
                    "false": $L("Call waiting deactivation failed. #{errorText}")
                }
            },
            register: {
                returnValue: {
                    "true": $L("Call waiting is enabled for #{ic[1]}."),
                    "false": $L("Call waiting registration failed. #{errorText}")
                }
            },
            unregister: {
                returnValue: {
                    "true": $L("Call waiting is disabled for #{ic[1]}."),
                    "false": $L("Call waiting unregistration failed. #{errorText}")
                }
            },
            interrogate: {
                returnValue: {
                    "false": $L("Call waiting query failed. #{errorText}")
                },
                enabled: {
                    "true": $L("Call waiting for #{ic[1]} is active."),
                    "false": $L("Call waiting is not active.")
                }
            }
        },
        "03": {
            activate: {
                returnValue: {
                    "true": $L("Call barring password change was successful."),
                    "false": $L("Call barring password change failed. #{errorText}")
                }
            },
            register: {
                returnValue: {
                    "true": $L("Call barring password change was successful."),
                    "false": $L("Call barring password change failed. #{errorText}")
                }
            }
        },
        "33": {
            activate: {
                returnValue: {
                    "true": $L("Call barring (bar all outgoing) enabled for #{ic[2]}."),
                    "false": $L("Call barring activation failed for #{ic[2]}. #{errorText}")
                }
            },
            deactivate: {
                returnValue: {
                    "true": $L("Call barring (bar all outgoing) disabled for #{ic[2]}."),
                    "false": $L("Call barring deactivation failed for #{ic[2]}. #{errorText}")
                }
            },
            register: {
                returnValue: {
                    "true": $L("Call barring (bar all outgoing) enabled for #{ic[2]}."),
                    "false": $L("Call barring registration failed for #{ic[2]}. #{errorText}")
                }
            },
            unregister: {
                returnValue: {
                    "true": $L("Call barring (bar all outgoing) disabled for #{ic[2]}."),
                    "false": $L("Call barring unregistration failed for #{ic[2]}. #{errorText}")
                }
            },
            interrogate: {
                returnValue: {
                    "false": $L("Operation failed: Call barring (bar all outgoing) query for #{ic[2]}. #{errorText}")
                },
                enabled: {
                    "true": $L("Call barring (bar all outgoing) is enabled for #{ic[2]}."),
                    "false": $L("Call barring (bar all outgoing) is disabled for #{ic[2]}.")
                }
            }
        },
        "331": {
            activate: {
                returnValue: {
                    "true": $L("Call barring (bar outgoing international) enabled for #{ic[2]}."),
                    "false": $L("Call barring activation failed for #{ic[2]}. #{errorText}")
                }
            },
            deactivate: {
                returnValue: {
                    "true": $L("Call barring (bar outgoing international) disabled for #{ic[2]}."),
                    "false": $L("Call barring deactivation failed for #{ic[2]}. #{errorText}")
                }
            },
            register: {
                returnValue: {
                    "true": $L("Call barring (bar outgoing international) enabled for #{ic[2]}."),
                    "false": $L("Call barring registration failed for #{ic[2]}. #{errorText}")
                }
            },
            unregister: {
                returnValue: {
                    "true": $L("Call barring (bar outgoing international) disabled for #{ic[2]}."),
                    "false": $L("Call barring unregistration failed for #{ic[2]}. #{errorText}")
                }
            },
            interrogate: {
                returnValue: {
                    "false": $L("Operation failed: Call barring (bar outgoing international) query for #{ic[2]}. #{errorText}")
                },
                enabled: {
                    "true": $L("Call barring (bar outgoing international) is enabled for #{ic[2]}."),
                    "false": $L("Call barring (bar outgoing international) is disabled for #{ic[2]}.")
                }
            }
        },
        "332": {
            activate: {
                returnValue: {
                    "true": $L("Call barring (bar outgoing international except to home country) enabled for #{ic[2]}."),
                    "false": $L("Call barring activation failed for #{ic[2]}. #{errorText}")
                }
            },
            deactivate: {
                returnValue: {
                    "true": $L("Call barring (bar outgoing international except to home country) disabled for #{ic[2]}."),
                    "false": $L("Call barring deactivation failed for #{ic[2]}. #{errorText}")
                }
            },
            register: {
                returnValue: {
                    "true": $L("Call barring (bar outgoing international except to home country) enabled for #{ic[2]}."),
                    "false": $L("Call barring registration failed for #{ic[2]}. #{errorText}")
                }
            },
            unregister: {
                returnValue: {
                    "true": $L("Call barring (bar outgoing international except to home country) disabled for #{ic[2]}."),
                    "false": $L("Call barring unregistration failed for #{ic[2]}. #{errorText}")
                }
            },
            interrogate: {
                returnValue: {
                    "false": $L("Operation failed: Call barring (bar outgoing international except to home country) query for #{ic[2]}. #{errorText}")
                },
                enabled: {
                    "true": $L("Call barring (bar outgoing international except to home) is enabled for #{ic[2]}."),
                    "false": $L("Call barring (bar outgoing international except to home) is disabled for #{ic[2]}.")
                }
            }
        },
        "35": {
            activate: {
                returnValue: {
                    "true": $L("Call barring (bar all incoming) enabled for #{ic[2]}."),
                    "false": $L("Call barring activation failed for #{ic[2]}. #{errorText}")
                }
            },
            deactivate: {
                returnValue: {
                    "true": $L("Call barring (bar all incoming) disabled for #{ic[2]}."),
                    "false": $L("Call barring deactivation failed for #{ic[2]}. #{errorText}")
                }
            },
            register: {
                returnValue: {
                    "true": $L("Call barring (bar all incoming) enabled for #{ic[2]}."),
                    "false": $L("Call barring registration failed for #{ic[2]}. #{errorText}")
                }
            },
            unregister: {
                returnValue: {
                    "true": $L("Call barring (bar all incoming) disabled for #{ic[2]}."),
                    "false": $L("Call barring unregistration failed for #{ic[2]}. #{errorText}")
                }
            },
            interrogate: {
                returnValue: {
                    "false": $L("Operation failed: Call barring (bar all incoming) query for #{ic[2]}. #{errorText}")
                },
                enabled: {
                    "true": $L("Call barring (bar all incoming) is enabled for #{ic[2]}."),
                    "false": $L("Call barring (bar all incoming) is disabled for #{ic[2]}.")
                }
            }
        },
        "351": {
            activate: {
                returnValue: {
                    "true": $L("Call barring (bar incoming roaming) enabled for #{ic[2]}."),
                    "false": $L("Call barring activation failed for #{ic[2]}. #{errorText}")
                }
            },
            deactivate: {
                returnValue: {
                    "true": $L("Call barring (bar incoming roaming) disabled for #{ic[2]}."),
                    "false": $L("Call barring deactivation failed for #{ic[2]}. #{errorText}")
                }
            },
            register: {
                returnValue: {
                    "true": $L("Call barring (bar incoming roaming) enabled for #{ic[2]}."),
                    "false": $L("Call barring registration failed for #{ic[2]}. #{errorText}")
                }
            },
            unregister: {
                returnValue: {
                    "true": $L("Call barring (bar incoming roaming) disabled for #{ic[2]}."),
                    "false": $L("Call barring unregistration failed for #{ic[2]}. #{errorText}")
                }
            },
            interrogate: {
                returnValue: {
                    "false": $L("Operation failed: Call barring (bar incoming roaming) query for #{ic[2]}. #{errorText}")
                },
                enabled: {
                    "true": $L("Call barring (bar incoming roaming) is enabled for #{ic[2]}."),
                    "false": $L("Call barring (bar incoming roaming) is disabled for #{ic[2]}.")
                }
            }
        },
        "330": {
            activate: {
                returnValue: {
                    "true": $L("Call barring (bar all barring) enabled for #{ic[2]}."),
                    "false": $L("Call barring activation failed for #{ic[2]}. #{errorText}")
                }
            },
            deactivate: {
                returnValue: {
                    "true": $L("Call barring (bar all barring) disabled for #{ic[2]}."),
                    "false": $L("Call barring deactivation failed for #{ic[2]}. #{errorText}")
                }
            },
            register: {
                returnValue: {
                    "true": $L("Call barring (bar all barring) enabled for #{ic[2]}."),
                    "false": $L("Call barring registration failed for #{ic[2]}. #{errorText}")
                }
            },
            unregister: {
                returnValue: {
                    "true": $L("Call barring (bar all barring) disabled for #{ic[2]}."),
                    "false": $L("Call barring unregistration failed for #{ic[2]}. #{errorText}")
                }
            },
            interrogate: {
                returnValue: {
                    "false": $L("Operation failed: Call barring (bar all barring) query for #{ic[2]}. #{errorText}")
                },
                enabled: {
                    "true": $L("Call barring (bar all barring) is enabled for #{ic[2]}."),
                    "false": $L("Call barring (bar all barring) is disabled for #{ic[2]}.")
                }
            }
        },
        "333": {
            activate: {
                returnValue: {
                    "true": $L("Call barring (bar outgoing) enabled for #{ic[2]}."),
                    "false": $L("Call barring activation failed for #{ic[2]}. #{errorText}")
                }
            },
            deactivate: {
                returnValue: {
                    "true": $L("Call barring (bar outgoing) disabled for #{ic[2]}."),
                    "false": $L("Call barring deactivation failed for #{ic[2]}. #{errorText}")
                }
            },
            register: {
                returnValue: {
                    "true": $L("Call barring (bar outgoing) enabled for #{ic[2]}."),
                    "false": $L("Call barring registration failed for #{ic[2]}. #{errorText}")
                }
            },
            unregister: {
                returnValue: {
                    "true": $L("Call barring (bar outgoing) disabled for #{ic[2]}."),
                    "false": $L("Call barring unregistration failed for #{ic[2]}. #{errorText}")
                }
            },
            interrogate: {
                returnValue: {
                    "false": $L("Operation failed: Call barring (bar outgoing) query for #{ic[2]}. #{errorText}")
                },
                enabled: {
                    "true": $L("Call barring (bar outgoing) is enabled for #{ic[2]}."),
                    "false": $L("Call barring (bar outgoing) is disabled for #{ic[2]}.")
                }
            }
        },
        "353": {
            activate: {
                returnValue: {
                    "true": $L("Call barring (bar incoming) enabled for #{ic[2]}."),
                    "false": $L("Call barring activation failed for #{ic[2]}. #{errorText}")
                }
            },
            deactivate: {
                returnValue: {
                    "true": $L("Call barring (bar incoming) disabled for #{ic[2]}."),
                    "false": $L("Call barring deactivation failed for #{ic[2]}. #{errorText}")
                }
            },
            register: {
                returnValue: {
                    "true": $L("Call barring (bar incoming) enabled for #{ic[2]}."),
                    "false": $L("Call barring registration failed for #{ic[2]}. #{errorText}")
                }
            },
            unregister: {
                returnValue: {
                    "true": $L("Call barring (bar incoming) disabled for #{ic[2]}."),
                    "false": $L("Call barring unregistration failed for #{ic[2]}. #{errorText}")
                }
            },
            interrogate: {
                returnValue: {
                    "false": $L("Operation failed: Call barring query (bar incoming) for #{ic[2]}. #{errorText}")
                },
                enabled: {
                    "true": $L("Call barring (bar incoming) is enabled for #{ic[2]}."),
                    "false": $L("Call barring (bar incoming) is disabled for #{ic[2]}.")
                }
            }
        },
        "30": {
            activate: {
                returnValue: {
                    "true": $L("Incoming caller ID presentation enabled."),
                    "false": $L("Incoming caller ID presentation not provisioned.")
                }
            },
            deactivate: {
                returnValue: {
                    "true": $L("Incoming caller ID presentation disabled."),
                    "false": $L("Incoming caller ID presentation not provisioned.")
                }
            },
            interrogate: {
                returnValue: {
                    "false": $L("Incoming caller ID presentation query failed. #{errorText}")
                },
                provisioned: {
                    "false": $L("Incoming caller ID presentation not provisioned.")
                },
                restricted: {
                    "true": $L("Incoming caller ID presentation disabled."),
                    "false": $L("Incoming caller ID presentation enabled.")
                }
            }
        },
        "31": {
            activate: {
                returnValue: {
                    "true": $L("Outgoing caller ID restricted."),
                    "false": $L("Outgoing caller ID restricted failed.")
                }
            },
            deactivate: {
                returnValue: {
                    "true": $L("Outgoing caller ID enabled."),
                    "false": $L("Outgoing caller ID enable failed.")
                }
            },
            register: {
                returnValue: {
                    "true": $L("Outgoing caller ID restricted."),
                    "false": $L("Outgoing caller ID restricted failed.")
                }
            },
            unregister: {
                returnValue: {
                    "true": $L("Outgoing caller ID enabled."),
                    "false": $L("Outgoing caller ID enable failed.")
                }
            },
            interrogate: {
                returnValue: {
                    "false": $L("Outgoing Caller ID restriction query failed.")
                },
                provisioned: {
                    "false": $L("Outgoing Caller ID restriction not provisioned.")
                },
                restricted: {
                    "true": $L("Outgoing caller ID restricted."),
                    "false": $L("Outgoing caller ID enabled.")
                }
            }
        },
        "300": {
            interrogate: {
                returnValue: {
                    "false": $L("CNAP query failed.")
                },
                provisioned: {
                    "false": $L("CNAP not provisioned.")
                },
                enabled: {
                    "true": $L("CNAP enabled."),
                    "false": $L("CNAP disabled.")
                }
            }
        },
        "06": {
            interrogate: {
                value: {
                    ".*": $L("IMEI: #{value}")
                },
                returnValue: {
                    "false": $L("IMEI query failed.")
                }
            }
        },
    },
    
    // Basic service group (info class)
    infoClass: {
        "11": "voice",
        "12": "data",
        "13": "fax",
        "16": "sms",
        "21": "allasyncservices",
        "22": "allsyncservices",
        "24": "datacircuitsync",
        "25": "datacircuitasync",
        "26": "packetaccess",
        "27": "padaccess",
        "89": "auxiliarytelephony"
    },
    
    infoClassDefault: "defaultbearer",
    
    // Call barring type
    callBarringType: {
        "33": "baralloutgoing",
        "331": "baroutgoingint",
        "332": "baroutgoingintextohome",
        "35": "barallincoming",
        "351": "barincomingroaming",
        "330": "barallbarring",
        "333": "baroutgoing",
        "353": "barincoming"
    },
    
    callBarringTypeDefault: "barallservices",
	
	// used externally to allow dial to proceed
	IMEIQuery: "*#06#",
	
	PINChange: "**04",
	
	PINUnblock: "**05",
    
	// returns true if passed string is allowed in limited mode:
	// IMEI query, PIN change and unblock 
	isMMIAllowedInLimited: function(code) {
		if (!code || code.length < 5)
			return false;
			
		if (code === this.IMEIQuery)
			return true;
		
		var prefix = code.substr(0,4);
		
		if (prefix === this.PINChange || prefix === this.PINUnblock)
			return true;
		
		return false;
	},
	
	// returns launchcode from number given dialstring containing one
	getLaunchcode: function(dialString) {
		if (PlatformType.isGSM()) {
			var result = this.launchcodePatternGsm.exec(dialString);
			return result && result.length >= 2 && result[1];
		} else {
			var result = this.launchcodePatternCdma.exec(dialString);
			return result && result.length >= 3 && result[2];
		}
	},
	
	// tests if dialstring contains a launchcode. used externally to make dialing proceed
	isLaunchcode: function(dialString) {
		return (PlatformType.isGSM() ? 
			this.launchcodePatternGsm.test(dialString)
			: this.launchcodePatternCdma.test(dialString));
	},
	
	// dials the call and returns action
	// sets debounce timer
	dialAndReturnAction: function(dialString, blockId, failureCb, successCb, pauseWaitDigits){
		this.debounced = true;
		this.debounceTimeout = setTimeout(this.clearDebounceTimeout, 5000);
		TelephonyCommands.dial(dialString, blockId, failureCb, successCb);
		return {
			action: DialAction.DIAL,
			"pauseWaitDigits": pauseWaitDigits
		};
	},
	
	clearDebounceTimeout: function() {
		DialStringParser.debounced = false;
		if (this.debounceTimeout) {
			clearTimeout(DialStringParser.debounceTimeout);
		}
		DialStringParser.debounceTimeout = null;
	},
	
    interpretDialString: (function() {
		
		// Numeric phone number
		const RE_REGULAR = /^[0-9\+]*$/;
		
		// 6-digit number (MSL)
		const RE_MSL = /^[0-9]{6}$/;
		
		// Split pause from phone number
		const RE_PAUSE_WAIT = /^([0-9\*\#\+]+)([pt][pt0-9\*\#]*)$/;
		
		// *31#<number><SEND> to supress CLIR or #31#<number><SEND> to invoke CLIR
		const RE_CLIR = /^([\*#]31#)([\+0-9]+)$/;
		
		// (star or hash) then a number, or '0', or '00'
		const RE_NA_GSM = /^([\*#][0-9]|0|00)$/;
		
		// Matches *|#|**|##|*# and puts that in
		// parsedDialString[1] and puts the rest of the dial string (excluding the final
		// '#') into parsedDialString[2].
		// '^' = match to start of string
		// (\*|#|\*\*|##|\*#) = match *|#|**|##|*# and put it into parsedDialString[1]
		// ([0-9]+[0-9\*]*) = match a single digit/+, then a series of digits and '*'
		// #$ = match a '#' at the end of the string
		const RE_LAUNCH_CODE = /^(\*|#|\*\*|##|\*#)([\+0-9]+[\+0-9\*]*)#$/;
		
		// Split pause/wait from phone number
		const RE_REGULAR_PW = /^([0-9\*\#\+]+)([pw][pw\*\#0-9]*)$/;
		
		// Service Code (the first number, terminated by either '*' or '#')
		const RE_SERVICE_CODE = /^([0-9]*)[\*#]?([\+0-9\*]*)$/;
		
		// Look for SI1, SI2, SI3 and SI4 separated by '*', allow + in first slot so call forwarding can use it
		const RE_MULTI_MMI = /^([0-9\+]*)[\*]?([0-9]*)[\*]?([0-9]*)[\*]?([0-9]*)[\*]?/;
		
		
		return function(dialString, isGSM, dialFailCb, messageCb, parserActionCallback){
	      	QDLogger.log( "DialStringParser::interpretDialString", dialString);
		    var url, action, serviceCode, supplementaryInfo;
	        var mmi = new Object();
	        var handle, scTemplate;
        
	        if (!dialString) {
	            return {
	                action: DialAction.UNKNOWN
	            };
	        }
        
			if(this.debounced) {
				QDLogger.log( "DialStringParser::interpretDialString", "DEBOUNCED");
				return {
					action: DialAction.DEBOUNCED
				};
			}
			
        
	        // If this is an emergency call then make it immediately
	        if (PlatformType.isEmergencyNumber(dialString)) {
	            QDLogger.log( "DialStringParser::interpretDialString", "dialing emergency call");
	            return this.dialAndReturnAction(dialString, undefined, dialFailCb);
			
		
	        }
        
	        // Make sure the phone type is known
	        if (isGSM === undefined) {
	            // Allow emergency calls if the SIM is not ready
	            QDLogger.log( "DialStringParser::interpretDialString", "isGSM === undefined!!")
	            return this.dialAndReturnAction(dialString, undefined, dialFailCb);
	        }
        
	        // Optimization: If this looks like a regular phone number (no '#' or '*') then dial it!
	        // This bypasses all the parsing logic and makes dialing happen a bit quicker
	        if (dialString.length >= 3 && dialString.match(RE_REGULAR)) {
	            return this.dialAndReturnAction(dialString, undefined, dialFailCb);
	        }
        
			// Handle CDMA parsing first
	        if (!isGSM) {
	            // TODO: factor out to parseCDMA()
	            QDLogger.log( "DialStringParser::interpretDialString", "CDMA");
			
				// handle 1 to dial voicemail
				if (dialString === "1") {
					return {
						action: DialAction.VOICEMAIL
					};
				}
			
				// deal with ##APPS#            
				var url = this.getLaunchcode(dialString);
	            if (url) {
	                QDLogger.log( "DialStringParser::interpretDialString", "URL is ::" , url);
	                // look up URL in internal codes then CDMA codes
					var action = this.launchcodesCommon[url] || this.launchcodesCDMA[url];
					QDLogger.log( "DialStringParser::interpretDialString", "action is " , action);
					if (action) {
						var result = {
							'action': action
						}
						if (action == DialAction.LAUNCHEXTERNAL) {
							result.app = ExternalApp[url];
						}
						return result;
					} else {
	                	// Show activation dialog for any 6-digit URL
	                     if (url.match(RE_MSL)) { // MSL or OTKSL
	                    		QDLogger.log( "DialStringParser::interpretDialString", "msl: " , url);
	                          return {
								action: DialAction.MSL,
								"msl": url
							}
                                             
	                    } else {
	                        QDLogger.log( "DialStringParser::interpretDialString", "URL doesn't match - not doing anything")
	                    }
	                    return {
	                    	action: DialAction.UNKNOWN
	                    };
	                }
	            }
            
	            // Dial phone number
	            // find pause/wait
				var dialParams = dialString.match(RE_PAUSE_WAIT);
	            if (dialParams == null) {
	                if (dialString.length > 0 
						&& (dialString.charAt(0) === 't' || dialString.charAt(0) === 'p')) {
						return {
							action: DialAction.UNKNOWN
						};
					} else {
						// No 't' (pause) or 'p' (wait) in the string - so no extra digits
						return this.dialAndReturnAction(dialString, undefined, dialFailCb);
					}
	            }
	            else {
					QDLogger.log( "DialStringParser::interpretDialString", "PARSE DIAL STRING " , dialParams[1] , "Extra digits " , dialParams[2]);
	                return this.dialAndReturnAction(dialParams[1], undefined, dialFailCb, undefined, dialParams[2]);
	            }
            
	        }
        
	        // Phone number is GSM
	        // TODO: factor out to parseGSM
        
			// check for internal codes
			var url = this.getLaunchcode(dialString);        
		
			if (url) {
				var action = this.launchcodesCommon[url];
				if (action) {
					var result = {
						'action': action
					}
					if (action == DialAction.LAUNCHEXTERNAL) {
						result.app = ExternalApp[url];
					}
					return result;
				}
			}
		
			// Is the dial string 2 characters or less?
	        // todo: factor out to parse short (?)
			// bails out with ACTION_MMI if it's a USSD, INCALLMMI otherwise
	        if (dialString.length <= 2) {
	            // Are we on an active call?
	            if (this.isCallConnected()) {
					if (dialString.length == 1) {
						switch (dialString) {
							// FIXME: order is important here. it needs to show the active call scene 
							// first so that it can receive the event via delegateToSceneAssistant
							case "0":
								Mojo.Controller.getAppController().assistant.showActiveCall();
								TelephonyCommands.disconnectAllHeld();
								if (this.incomingCallExists()) {
									this.announceIncomingRejected();
								}
								break; //release all held, accept waiting
							case "1":
								Mojo.Controller.getAppController().assistant.showActiveCall();
								TelephonyCommands.disconnectAllActive();
								break; //release all active, accept held
							case "2":
								Mojo.Controller.getAppController().assistant.showActiveCall();
								TelephonyCommands.swap();
								break; //swap
							case "3":
								Mojo.Controller.getAppController().assistant.showActiveCall();
								TelephonyCommands.merge();
								break; //merge
							default:
								TelephonyCommands.sendUSSD(dialString, messageCb);
								return {
									action: DialAction.MMI
								};
								//send ussd
							
						}
					} else {
						switch (dialString.charAt(0)) {
							// TODO FIXME: order is important here. it needs to show the active call scene 
							// first so that it can receive the event via delegateToSceneAssistant

							case '1':
								Mojo.Controller.getAppController().assistant.showActiveCall();
								TelephonyCommands.disconnectCallMmi(dialString.charAt(1));
								break; //release active call X
							case '2':
								Mojo.Controller.getAppController().assistant.showActiveCall();
								TelephonyCommands.extractMmi(dialString.charAt(1))
								break; //extract call X
							default:
								TelephonyCommands.sendUSSD(dialString, messageCb);
								return {
									action: DialAction.MMI
								};
								// send ussd							
						}
					}
				// or we're not on an active call
				} else {
					// TODO FIXME: won't dial 1 as USSD
					// It is a USSD code if it doesn't start with 1, p, or w
					if (dialString.length > 0 && '1pw'.indexOf(dialString.charAt(0)) === -1) {				
						if (PlatformType.isGSMNorthAmerican() && dialString.match(RE_NA_GSM)) {
							return this.dialAndReturnAction(dialString, undefined, dialFailCb);
						}
						else {
							TelephonyCommands.sendUSSD(dialString, messageCb);
							return {
								action: DialAction.MMI
							};
						}
					// it's not, so we should dial it if it's a valid number
					} else if ('pw'.indexOf(dialString.charAt(0)) === -1){
						return this.dialAndReturnAction(dialString, undefined, dialFailCb);
					} else {
						return {
							action: DialAction.UNKNOWN
						};
					}
				}
            
	            return {
	                action: DialAction.INCALLMMI
	            };
	        }
        
	        // Handle the special case for temporary supression or invokation of CLIR
	        var tempCLIR = dialString.match(RE_CLIR);
	        if (tempCLIR != undefined) {
	            QDLogger.log( "DialStringParser::interpretDialString", "Temp CLIR override:" , tempCLIR[1] , " dialing " , tempCLIR[2]);
	            return this.dialAndReturnAction(tempCLIR[2], tempCLIR[1] == "#31#" ? true : false, dialFailCb);
	        }
        
	        // TODO FIXME: this won't find a p or w if we matched CLIR
	        // Check if this is an MMI code
	        var parsedDialString = dialString.match(RE_LAUNCH_CODE);
	        if (!parsedDialString) {
	            // Since we didn't match, it's unlikely to be an MMI code.
				// if it has p/w, it's a phone number; extract digits and dial.
				// if it doesn't, dial it, unless it ends in a #, then treat as USSD. 
	            var dialParams = dialString.match(RE_REGULAR_PW);
	            if (dialParams == null) {
					// don't dial anything that's just pause/wait
					if (dialString.length > 0 
						&& (dialString.charAt(0) === 'w' || dialString.charAt(0) === 'p')) {
						return {
							action: DialAction.UNKNOWN
						};
					} else {
						// if it ends in a #, it's a USSD (3GPP 22.030, 6.5.3.2 item 3 / figure 3.5.3.2)
						if (dialString.length > 0 && dialString.charAt(dialString.length - 1) === '#') {
							TelephonyCommands.sendUSSD(dialString, messageCb);
	            			return {
	                			action: DialAction.MMI
	            			};
						} else {
							return this.dialAndReturnAction(dialString, undefined, dialFailCb);
						}
					}
	            }
	            else {
	                QDLogger.log( "DialStringParser::interpretDialString", "PARSE DIAL STRING " , dialParams[1] , "Extra digits " , dialParams[2]);
	                return this.dialAndReturnAction(dialParams[1], undefined, dialFailCb, undefined, dialParams[2]);
	            }
            
	        }
        
	        // The action is the first regex match
	        // TODO: factor as parseActiom
	        switch (parsedDialString[1]) {
	            case "*":
	                mmi.action = 'activate';
	                break;
	            case "#":
	                mmi.action = 'deactivate';
	                break;
	            case "**":
	                mmi.action = 'register';
	                break;
	            case "##":
	                mmi.action = 'unregister';
	                break;
	            case "*#":
	                mmi.action = 'interrogate';
	                break;
	        }
        
			// TODO: FIXME: added + support here.  this AT LEAST allows the first
			// parameter of the PIN MMI codes to contain a +.  it might cause other problems too.
	        parsedDialString[2].gsub(RE_SERVICE_CODE, function(match){
	            mmi.serviceCode = match[1];
	            mmi.si = match[2];
	        });
        	
	        // See if the service code is valid.  If not then this is a USSD
	        if (!mmi.serviceCode || !this.serviceCodes[mmi.serviceCode]) {
	            // The service code is not valid.  This must be USSD. Send the entire dial string
	            TelephonyCommands.sendUSSD(dialString, messageCb);
	            return {
	                action: DialAction.MMI
	            };
	        }
        
	        // Parse the supplementary info
	        mmi.si = mmi.si.match(RE_MULTI_MMI);
        
	        // Point to the service code template
	        scTemplate = this.serviceCodes[mmi.serviceCode][mmi.action];
        
	        // Make sure the action can be handled
	        if (!scTemplate) {
	            QDLogger.log( "DialStringParser::interpretDialString", mmi.action, " not supported for service code ", mmi.serviceCode);
	            return {
	                action: DialAction.UNKNOWN
	            };
	        }
        
	        // This is a valid MMI code. 
        
	        // Special case for call forwarding.  If the action is "activate" but a phone
	        // number is specified then the command must be changed to "register".
	        if (mmi.action == 'activate' && mmi.si[1].length > 0 && scTemplate.cmd.include("forward")) {
	            QDLogger.log( "DialStringParser::interpretDialString", "forward activate with phone number");
	            // If there is a template for "registration" then use that instead
	            if (this.serviceCodes[mmi.serviceCode]["register"]) {
	                scTemplate = this.serviceCodes[mmi.serviceCode]["register"];
	                mmi.action = "register";
	            }
	        }
        
	        // Create the basic service groups that correspond to the SI values entered by the user
	        mmi.ic = new Array();
			// TODO: will this let invalid codes through?
	        for (var i = 1; i <= 4; i++) {
	            if (this.infoClass[mmi.si[i]]) 
	                mmi.ic[i] = this.infoClass[mmi.si[i]];
	            else 
	                mmi.ic[i] = this.infoClassDefault;
	        }
        
	        // Create the call barring service types that correspond to the SI[1] value entered by the user
	        // This is used to change call barring password (*03 or **03)
	        mmi.bs = new Array();
				// TODO: will this let invalid call barring types through?
	        if (this.callBarringType[mmi.si[1]]) 
	            mmi.bs[1] = this.callBarringType[mmi.si[1]];
	        else 
	            mmi.bs[1] = this.callBarringTypeDefault;
        
			var cmd = scTemplate.cmd;
			// TODO FIXME WORKAROUND TO GET ARRAY OF BEARERS OUT
			mmi.cmd = scTemplate.cmd;
	        // Create the JSON arguments and substitute in the values that the user entered
	   		var interpolatedArgs = Object.toJSON(scTemplate).interpolate(mmi); // Mojo.parseJSON
	        var arg = interpolatedArgs.evalJSON();
			delete arg.cmd;
		
			QDLogger.log( "DialStringParser::interpretDialString", "Sending command: cmd=" ,  cmd , " arg=" , arg);
	        var parseResponse = this.parseResponse.bind(this, mmi, messageCb);
	
	        this.lastReq = new Mojo.Service.Request(TelephonyCommands.svcUri, {
				method: cmd,
				parameters: arg,
				onSuccess: function(response) {
					parseResponse(true, response);
				},
				onFailure: function(response) {
					parseResponse(false, response);
				}	 
			});
	        return {
	            action: DialAction.MMI
	        };
	    };
	})(),
    
    addObject: function(src, add){
        Object.keys(add).each(function(key){
            if (src[key] == undefined) {
                if (typeof(add[key]) == 'object' && !(add[key] instanceof Array)) 
                    this.addObject(src, add[key]);
                else 
                    src[key] = add[key];
            }
        }
.bind(this));
    },
    
	parseResponse: function(mmi, messageCb, success, payload) {
		var msg = this.mmiResponses[mmi.serviceCode][mmi.action];
            var addObject = this.addObject.bind(this);
        	
			// mod the error message if we don't have service
			if (!this.isServiced() && !success ) {
				QDLogger.log( "DialStringParser::interpretDialString", "no service on MMI error");
				payload.errorText = Messages.noServiceError.toString();
			} else {
				QDLogger.log( "DialStringParser::interpretDialString", "getting error string because serviced: ", this.isServiced(), " and success:", success);
				if (Messages.serviceErrors[payload.errorCode] !== undefined) {
					payload.errorText = Messages.serviceErrors[payload.errorCode].toString();
				} else {
					payload.errorText = Messages.serviceErrors[Messages.defaultErrorIndex].toString();
				}
				
			}
			
			// TODO FIXME: WORKAROUND TO HANDLE ARRAY IN FORWARDQUERY
			if (mmi.cmd == "forwardQuery" && success && payload.extended && payload.extended.status) {
				var status = payload.extended.status;
				var catStatus = ""; 
				for (var i = 0; i < status.length; i++) {
					var bearer = Messages.bearers[status[i].bearer] || $L(status[i].bearer);
					catStatus += bearer + ": "  
						+ (status[i].activated ? Messages.fwdActivated : Messages.fwdNotActivated) 
						+ " " + status[i].number + ";";
				}
				if (catStatus.length > 0)
					payload.extended.status = catStatus.substr(0, catStatus.length-1);
			} 
			
			if (mmi.ic && mmi.ic[2]){
				mmi.ic[2] =  Messages.bearers[mmi.ic[2]] || $L(mmi.ic[2]);
			} 
			
			if (mmi.ic && mmi.ic[1]) {
				mmi.ic[1] =  Messages.bearers[mmi.ic[1]] || $L(mmi.ic[1]);
			}
			
            // Put all the response and mmi codes into a single object (and flatten them) for interpolation to happen correctly
            var data = new Object();
            addObject(data, payload);
            addObject(data, mmi);
            
			Mojo.Log.info( "PhoneApp: DialStringParser::interpretDialString Mojo payload= %j" , payload);
            Mojo.Log.info( "PhoneApp: DialStringParser::interpretDialString mmiResponses= %j" , msg);
            Mojo.Log.info( "PhoneApp: DialStringParser::interpretDialString data = %j" , data);
            Object.keys(msg).each(function(key){
                // Does the object exist in the payload?
                if (data[key] != undefined) {
                    Object.keys(msg[key]).each(function(value){
                        QDLogger.log( "DialStringParser::interpretDialString", "comparing " , data[key].toString() , " to " , value);
                        // Perform regular expression matching (which matches plain vanilla strings too)
                        if (data[key].toString().match(RegExp(value))) {
                            QDLogger.log( "DialStringParser::interpretDialString", "match!!! msg=" , Object.toJSON(msg[key][value]));
                            // Perform parameter substitution
                            msg = new Template(msg[key][value]).evaluate(data);
                            messageCb(msg);
                            throw $break;
                        }
                        
                    });
                }
            });
	},
	
    isCallConnected: function(){
    	QDLogger.log( "DialStringParser::isCallConnected");
        return Mojo.Controller.getAppController().assistant.telephonyEventListener.isConnected();
    },
	
	isServiced: function() {
		return Mojo.Controller.getAppController().assistant.telephonyEventListener.serviced;
	},
	
	incomingCallExists: function() {
		return Mojo.Controller.getAppController().assistant.telephonyEventListener.incomingCallExists();
	},
	
	announceIncomingRejected: function() {
		Mojo.Controller.getAppController().assistant.announcer.announceIncomingClose(AnnouncerAssistant.kIncomingRejected);
	}
	
    
};

// CDMA, GSM codes that will cause non-dialing actions to happen.
// on CDMA: ##[number]# or #*#[number]#
// on GSM: #*[number]#
DialStringParser.launchcodePatternGsm = /^#\*(.*)#$/;
DialStringParser.launchcodePatternCdma = /^#(#|\*#)(.*?)(#)?$/;
// true when we think we're already busy
DialStringParser.debounced = false;
DialStringParser.debounceTimeout = null;